/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://lammps.sandia.gov/, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing authors: Richard Berger (Temple U) ------------------------------------------------------------------------- */ #include "text_file_reader.h" #include "fmt/format.h" #include "tokenizer.h" #include "utils.h" #include using namespace LAMMPS_NS; /** Class for reading and parsing text files * * The value of the class member variable *ignore_comments* controls * whether any text following the pound sign (#) should be ignored (true) * or not (false). Default: true, i.e. ignore. \verbatim embed:rst *See also* :cpp:class:`TextFileReader` \endverbatim * * \param filename Name of file to be read * \param filetype Description of file type for error messages */ TextFileReader::TextFileReader(const std::string &filename, const std::string &filetype) : filename(filename), filetype(filetype), ignore_comments(true) { fp = fopen(filename.c_str(), "r"); if (fp == nullptr) { throw FileReaderException(fmt::format("cannot open {} file {}", filetype, filename)); } } /** Closes the file */ TextFileReader::~TextFileReader() { fclose(fp); } /** Read the next line and ignore it */ void TextFileReader::skip_line() { char *ptr = fgets(line, MAXLINE, fp); if (ptr == nullptr) { // EOF throw EOFException(fmt::format("Missing line in {} file!", filetype)); } } /** Read the next line(s) until *nparams* words have been read. * * This reads a line and counts the words in it, if the number * is less than the requested number, it will read the next * line, as well. Output will be a string with all read lines * combined. The purpose is to somewhat replicate the reading * behavior of formatted files in Fortran. * * If the *ignore_comments* class member has the value *true*, * then any text read in is truncated at the first '#' character. * * \param nparams Number of words that must be read. Default: 0 * \return String with the concatenated text */ char *TextFileReader::next_line(int nparams) { // concatenate lines until have nparams words int n = 0; int nwords = 0; char *ptr = fgets(line, MAXLINE, fp); if (ptr == nullptr) { // EOF return nullptr; } // strip comment if (ignore_comments && (ptr = strchr(line, '#'))) *ptr = '\0'; nwords = utils::count_words(line); if (nwords > 0) n = strlen(line); while (nwords == 0 || nwords < nparams) { char *ptr = fgets(&line[n], MAXLINE - n, fp); if (ptr == nullptr) { // EOF if (nwords > 0 && nwords < nparams) { throw EOFException(fmt::format("Incorrect format in {} file! {}/{} parameters", filetype, nwords, nparams)); } return nullptr; } // strip comment if (ignore_comments && (ptr = strchr(line, '#'))) *ptr = '\0'; nwords += utils::count_words(&line[n]); // skip line if blank if (nwords > 0) { n = strlen(line); } } return line; } /** Read lines until *n* doubles have been read and stored in array *list* * * This reads lines from the file using the next_line() function, * and splits them into floating-point numbers using the * ValueTokenizer class and stores the number is the provided list. * * \param list Pointer to array with suitable storage for *n* doubles * \param n Number of doubles to be read */ void TextFileReader::next_dvector(double * list, int n) { int i = 0; while (i < n) { char *ptr = next_line(); if (ptr == nullptr) { // EOF if (i < n) { throw FileReaderException(fmt::format("Incorrect format in {} file! {}/{} values", filetype, i, n)); } } ValueTokenizer values(line); while (values.has_next()) { list[i++] = values.next_double(); } } } /** Read text until *nparams* words are read and passed to a tokenizer object for custom parsing. * * This reads lines from the file using the next_line() function, * and splits them into floating-point numbers using the * ValueTokenizer class and stores the number is the provided list. * * \param nparams Number of words to be read * \param separators String with list of separators. * \return ValueTokenizer object for read in text */ ValueTokenizer TextFileReader::next_values(int nparams, const std::string &separators) { return ValueTokenizer(next_line(nparams), separators); }