【问题标题】:Reading/parsing text file input c++读取/解析文本文件输入c ++
【发布时间】:2012-03-04 00:03:17
【问题描述】:

一点背景知识:我正在为一个学校项目开发一个滑块拼图,这是我们第一次使用 C++ 而不是 Java。这是我第一次不得不实现从文件中读取数据的东西。

我有一个关于从文本文件中读取输入的简单问题。 我了解如何逐行读取文件并将每一行保存在一个字符串中,我想知道在读取文件时是否可以将字符串解析为不同的数据类型。

目前我正在读取每一行并将它们作为字符串存储在一个向量中以供以后解析,我知道必须有一种更简单的方法来实现这一点

第一行包含 2 个整数,表示网格的长度和宽度,接下来的行将包含 4 个整数和一个 char,用于创建块时用作参数。

我的问题是,如果我逐个字符地读取文件,是否可以使用一个函数来检测字符是整数还是字符(并忽略空格),以便我可以立即存储它们并在读取文件时创建块对象?在这种情况下,我将如何处理大于 10 的整数?

编辑:只是注意到我使用 fstream 来读取文件,我不熟悉其他输入法

示例输入:

4  4
3  1  2  1  b
1  1  1  1  a 

【问题讨论】:

    标签: c++ input


    【解决方案1】:

    要检测一段字符串是否可以解析为整数,你只需要解析它,看看你是否成功。最好的函数可能是std::strtoul(),因为它可以告诉你它消耗了多少个字符,这样你就可以在那之后继续解析。 (有关详细信息,请参阅手册页。)

    但是,如果您已经知道文件的格式,则可以使用 iostream 格式提取。这很简单:

    #include <fstream>
    
    
    std::ifstream infile("thefile.txt");
    
    int n1, n2, x1, x2, x3, x4;
    char c;
    
    if (!(infile >> n1 >> n2)) { /* error, could not read first line! Abort. */ }
    
    while (infile >> x1 >> x2 >> x3 >> x4 >> c)
    {
        // successfully extracted one line, data is in x1, ..., x4, c.
    }
    

    另一种方法是将每一行读入一个字符串(使用std::getline),然后从该行创建一个字符串流,并使用&gt;&gt; 解析该字符串流。这有一个额外的好处,您可以发现并跳过坏行并恢复,而在我上面介绍的直接格式化提取中,您无法从任何错误中恢复。

    【讨论】:

    • 非常感谢。 >> 是否忽略空格?看来我不需要担心使用这种方法的每一行的结尾或文件的结尾正确吗?
    • @Gadesxion:是的,格式化输入函数从跳过空格开始。
    • @KerrekSB:当然,即使不使用std::istringstream,您也可以从输入错误中恢复:这是eof() 的唯一用途。当输入失败但eof() 未设置时,您将clear() 流的状态和ignore() 字符,例如直到有换行符。
    • @Gadesxion >> 运算符不会完全忽略空格。它会尝试读取您告诉它读取的类型。例如,使用 >> 将123 456 读入int 会将123 放入变量中,因为它会尽可能长地读取数字,遇到空格,然后“放弃”。 Here is more detailed documentation of the istream << operator.
    • @rob05c:错了。在正常情况下 >> 忽略前导空格然后读取适当类型的值。
    【解决方案2】:

    如果您可以断言每种类型,我建议您使用流运算符,就像使用 cin 一样。

    #include <fstream>
    
    using namespace std;
    
    int main()
    {
        fstream fileStream;
        fileStream.open("inputfile.txt");
    
        int firstNumber;
        fileStream >> firstNumber;
    
        char firstChar;
        fileStream >> firstChar;
    }
    

    这样,您可以按值读取,而不是按行读取然后解析行。只需将您需要的每个值读入一个变量,当您发现您需要它时,就像这样。

    【讨论】:

      【解决方案3】:

      我会将每一行读入一个字符串(就像你一直在做的那样)。
      然后我会将该行中的标记读入适当的变量中。

      运算符>>在应用于流时会将流中的下一个值转换为正确的类型。如果这是不可能的,它会在流上设置标志,指​​示易于测试的失败。

       int  x;
       stream >> x; // ignores white space then: reads an integer from the stream into x
      
       char c;
       stream >> c; // ignores white space then: reads an char from the stream into c
      
       double d;
       stream >> d; // ignores white space then: reads an double from the stream into d
      

      假设您的输入:

      4  4
      3  1  2  1  b
      1  1  1  1  a 
      

      不知道这些值意味着什么,我会将我的假设放在 cmets 中。

      // Assume that stream is a std::fstream already opened to you file.
      
      std::string line1;
      std::getline(stream, line1);           // reads "4 4" into `line1`
      
      std::stringstream  line1stream(line1); // convert line1 into a stream for reading.
      int a;
      int b;
      line1stream >> a >> b;   // reads "4 4" from 'line1' into a (now 4) b (now 4)
      if (!stream || !line1stream)
      {
           // failed reading the first line.
           // either reading the line failed (!stream)
           // or     reading 2 integers from line1stream failed (!line1stream)
           throw SomeException("Failed");
      }
      
      
      std::string line2;
      std::getline(stream, line2);           // reads "3  1  2  1  b" into line2
      
      std::stringstream line2stream(line2);  // convers line2 into a stream for reading.
      int  data[4];
      char c;
      
      line2stream >> data[0] >> data[1] >> data[2] >> data[3] >> c;
      if (!stream || !line2stream)
      {
           // failed reading the second line.
           // either reading the line failed (!stream)
           // or     reading 4 integers and one char from line2stream failed (!line2stream)
           throw SomeException("Failed");
      }
      

      【讨论】:

        【解决方案4】:

        ifstreams 也是 istreams,因此您可以使用与 std::cin 相同的运算符 >>。

        int main()
        {
            std::ifstream s("test.txt");
            if (s.is_open())
            {
                int i, j, k;
                s >> i >> j >> k;
            }
        }
        

        请注意,这不是最快的解析方式,但这可能与您无关。

        【讨论】:

        • 你能解释一下什么是慢的,哪种方法更快?
        • 我不知道为什么这很慢(询问库实现者),但我知道它慢的一个事实。一种更快的方法是在缓冲区中加载文件块并使用您自己的解析器对其进行解析。 -- 如果你不想写,你可以只测试从文本文件中解析 1kk 双打,strtod() 比流快约 3 倍。
        • 我认为cooky451的观点可以概括为“C比C++更快”。
        • @cooky451:嗯,我并没有要求解释为什么(某些)iostream 库速度很慢,而是你在比较什么。事实上,已经(实现了 iostream 和 locales 库)(dietmar-kuehl.de/cxxrt)(注意:我已经 10 多年没有接触过这段代码了;它可能有很多问题)我知道哪里有问题,但即使是这个老实施避免了其中的大部分。具体来说,浮点格式是较弱的部分之一,因为它在解析值后委托给strtod(),但是:此特定代码中没有浮点。
        • @rob05c:对于 iostreams 的糟糕实现,感觉就像首先构建格式字符串然后委托给&lt;stdio.h&gt; 函数,这肯定会更慢。 Iostream 可以(并且已经)以不同的方式实现,即使我上次测量 &lt;stdio.h&gt; 和我的 iostream 实现之间的差异(参见上面的指针)我们可以忽略不计:对于某些用例,&lt;stdio.h&gt; 稍微快一些(特别是对于解析浮点,这主要是因为没有为那些实现解析器,只是委托给strtod()),用于我的其他库。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多