↰ Return to documentation for file (include/networkit/io/METISParser.hpp)
/*
* METISParser.hpp
*
* Created on: 27.11.2012
* Author: Christian Staudt
*/
#ifndef NETWORKIT_IO_METIS_PARSER_HPP_
#define NETWORKIT_IO_METIS_PARSER_HPP_
#include <algorithm>
#include <cctype>
#include <charconv>
#include <exception>
#include <fstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include <networkit/Globals.hpp>
#include <networkit/auxiliary/NumberParsing.hpp>
namespace NetworKit {
namespace METISParserDetails {
inline bool isSpace(char c) {
return std::isspace(static_cast<unsigned char>(c));
}
inline const char *skipSpaces(const char *first, const char *last) {
return std::find_if_not(first, last, isSpace);
}
// Returns the number of whitespace-separated tokens in [first, last).
inline size_t countTokens(const char *first, const char *last) {
size_t tokens = 0;
for (first = skipSpaces(first, last); first != last; first = skipSpaces(first, last)) {
++tokens;
first = std::find_if(first, last, isSpace);
}
return tokens;
}
template <class T>
const char *parseNumber(const char *first, const char *last, T &value) {
first = skipSpaces(first, last);
const char *const tokenEnd = std::find_if(first, last, isSpace);
// A view, so that the token is only copied into a string for error messages.
const std::string_view token(first, static_cast<size_t>(tokenEnd - first));
if (token.empty()) {
throw std::runtime_error("METIS line ended where a value was expected");
}
const char *numberEnd = first;
if constexpr (std::is_integral_v<T>) {
const auto [ptr, ec] = std::from_chars(first, tokenEnd, value);
if (ec == std::errc::result_out_of_range) {
throw std::runtime_error("METIS value " + std::string(token)
+ " is out of range for the requested type");
}
if (ec == std::errc{}) {
numberEnd = ptr;
}
} else {
// std::from_chars for floating-point types is too recent for all supported compilers.
try {
std::tie(value, numberEnd) =
Aux::Parsing::strTo<T, const char *, Aux::Checkers::Enforcer>(first, tokenEnd);
} catch (const std::exception &) {
numberEnd = first;
}
}
// E.g., the fraction of 1.5 or the sign of -1 for unsigned types are not part of the number.
if (numberEnd != tokenEnd) {
throw std::runtime_error("METIS value " + std::string(token)
+ " is not valid for the requested type");
}
return tokenEnd;
}
template <class NodeT>
std::vector<NodeT> parseLine(const std::string &line, count ignoreFirst = 0) {
static_assert(std::is_integral_v<NodeT>, "Integral required for NodeT.");
const char *it = line.data();
const char *const end = it + line.size();
for (count i = 0; i < ignoreFirst; ++i) {
double ignored;
it = parseNumber(it, end, ignored);
}
std::vector<NodeT> adjacencies;
adjacencies.reserve(countTokens(it, end));
while (skipSpaces(it, end) != end) {
NodeT v;
it = parseNumber(it, end, v);
adjacencies.push_back(v);
}
return adjacencies;
}
} // namespace METISParserDetails
class METISParser final {
std::ifstream graphFile;
// Returns the next line that is not a comment.
std::string nextLine();
public:
METISParser(std::string_view path);
std::tuple<count, count, index, count> getHeader();
bool hasNext();
template <class NodeT = node>
std::vector<NodeT> getNext(count ignoreFirst = 0) {
return METISParserDetails::parseLine<NodeT>(nextLine(), ignoreFirst);
}
template <class NodeT = node, class EdgeWeightT = edgeweight>
std::vector<std::pair<NodeT, EdgeWeightT>> getNextWithWeights(count ignoreFirst = 0);
};
template <class NodeT, class EdgeWeightT>
std::vector<std::pair<NodeT, EdgeWeightT>> METISParser::getNextWithWeights(count ignoreFirst) {
static_assert(std::is_integral_v<NodeT>, "Integral required for NodeT.");
using METISParserDetails::parseNumber;
const std::string line = nextLine();
const char *it = line.data();
const char *const end = it + line.size();
for (count i = 0; i < ignoreFirst; ++i) {
double ignored;
it = parseNumber(it, end, ignored);
}
std::vector<std::pair<NodeT, EdgeWeightT>> adjacencies;
// A line with an odd number of tokens is malformed and throws below.
adjacencies.reserve(METISParserDetails::countTokens(it, end) / 2);
while (METISParserDetails::skipSpaces(it, end) != end) {
NodeT v;
EdgeWeightT weight;
it = parseNumber(it, end, v);
it = parseNumber(it, end, weight);
adjacencies.emplace_back(v, weight);
}
return adjacencies;
}
} /* namespace NetworKit */
#endif // NETWORKIT_IO_METIS_PARSER_HPP_