【问题标题】:Reading from ifstream won't read whitespace从 ifstream 读取不会读取空格
【发布时间】:2011-07-21 10:47:26
【问题描述】:

我在 C++ 中实现了一个自定义词法分析器,当尝试读取空白时,ifstream 不会读出它。我正在使用>> 逐个字符地阅读,所有的空格都消失了。有什么方法可以让 ifstream 保留所有的空白并把它读给我听?我知道在读取整个字符串时,读取将在空白处停止,但我希望通过逐个字符读取,我会避免这种行为。

尝试过:.get(),很多答案都推荐,但是效果和std::noskipws一样,就是我现在得到了所有的空格,但是不是换行符我需要 lex 一些结构。

这是有问题的代码(扩展 cmets 被截断)

while(input >> current) {
    always_next_struct val = always_next_struct(next);
    if (current == L' ' || current == L'\n' || current == L'\t' || current == L'\r') {
        continue;
    }
    if (current == L'/') {
        input >> current;
        if (current == L'/') {
            // explicitly empty while loop
            while(input.get(current) && current != L'\n');
            continue;
        }

我正在打破while 行并查看current 的每个值,而\r\n 绝对不在其中- 输入只是跳到下一行输入文件。

【问题讨论】:

  • 如果您在 while 行中断,那么您肯定不会在 current 中看到 \n,就像 get 遇到 \n 您会在 continue 行而不是while 行。还是我误会了?
  • L'\n' 是 16 位 wchar_t,而不是 8 位 char,但这应该没有区别。
  • @Charles:然后它将停止中断并且不会开始显示文件下一行的内容。 @Rene:这是一个wifstream
  • 对所有三个输入都使用input.get()
  • @Rene:前两个完全按预期工作,没有空格可以跳过。

标签: c++ c++11


【解决方案1】:

有一个操纵器可以禁用空格跳过行为:

stream >> std::noskipws;

【讨论】:

  • 我得到了所有的空格,但仍然没有换行符。
  • 你也可以使用stream.unsetf(ios_base::skipws);手动删除该格式标志。
  • @sth:这正是noskipws 所做的。
【解决方案2】:

运算符>> 吃掉空格(空格、制表符、换行符)。使用yourstream.get() 读取每个字符。

编辑:

注意:平台(Windows、Un*x、Mac)的换行编码不同。它可以是“\n”、“\r”或两者兼而有之。它还取决于您如何打开文件流(文本或二进制)。

编辑(分析代码):

之后

  while(input.get(current) && current != L'\n');
  continue;

如果未到达文件末尾,current 中将有一个 \n。之后,您继续最外面的 while 循环。在那里,下一行的第一个字符被读入current。这不是你想要的吗?

我试图重现您的问题(使用charcin 而不是wchar_twifstream):

//: get.cpp : compile, then run: get < get.cpp

#include <iostream>

int main()
{
  char c;

  while (std::cin.get(c))
  {
    if (c == '/') 
    { 
      char last = c; 
      if (std::cin.get(c) && c == '/')
      {
        // std::cout << "Read to EOL\n";
        while(std::cin.get(c) && c != '\n'); // this comment will be skipped
        // std::cout << "go to next line\n";
        std::cin.putback(c);
        continue;
      }
     else { std::cin.putback(c); c = last; }
    }
    std::cout << c;
  }
  return 0;
}

应用到自身的这个程序消除了其输出中的所有 C++ 行 cmets。内部 while 循环不会吃掉所有文本到文件末尾。请注意putback(c) 声明。没有它,换行符就不会出现。

如果它对wifstream 不起作用,那将是非常奇怪的,除了一个原因:当打开的文本文件没有保存为 16 位字符 并且 \n 字符以错误的字节结束...

【讨论】:

  • 也不要得到\r,我在Windows上以文本模式打开,即CRLF。
  • @DeadMG :当我使用 int c = std::cin.get(); 时,我在 Windows 机器上按 ENTER 时得到 ASCII 10,所以它应该以相同的方式为 ifstream 工作。
  • 事实并非如此。发生的情况是整个文件在 while 循环中被读取并且它没有退出。预期的内容与您所说的差不多 - 当找到行尾时,循环终止并在外循环中继续。
  • @DeadMG :这很奇怪......所以我又尝试了一次。
【解决方案3】:

您可以以二进制模式打开流:

std::wifstream stream(filename, std::ios::binary);

如果您这样做,您将丢失任何提供给我的流的格式化操作。

另一种选择是将整个流读入一个字符串,然后处理该字符串:

std::wostringstream ss;
ss << filestream.rdbuf();

当然,从 ostringstream 获取字符串需要额外的字符串副本,因此如果您喜欢冒险,您可以考虑在某个时候更改它以使用自定义流。 编辑:其他人提到 istreambuf_iterator,这可能是比将整个流读入字符串更好的方法。

【讨论】:

    【解决方案4】:

    将流(或其缓冲区,具体而言)包装在std::streambuf_iterator?这应该忽略所有格式,并且还为您提供了一个不错的迭代器接口。

    或者,一种更有效且更简单的方法可能只是使用 Win32 API(或 Boost)来对文件进行内存映射。然后你可以使用纯指针遍历它,并且保证运行时不会跳过或转换任何内容。

    【讨论】:

    • 有趣的想法。我之前没有真正使用过那个特定的迭代器类,我来看看
    • 我发现这个迭代器几乎是使用 IOStreams 的唯一明智的方法,如果你想对你正在做什么和正在发生的事情进行任何形式的控制。当然,它仍然很慢,正如您所期望的那样,将 IOStreams(慢)与按字符 I/O(也很慢)结合起来的任何东西都会如此。但它有效!
    【解决方案5】:

    您可以将流包装在 std::streambuf_iterator 中,以获取包含所有空格和换行符的数据。

               /*Open the stream in default mode.*/
                std::ifstream myfile("myfile.txt");
    
                if(myfile.good()) {
                    /*Read data using streambuffer iterators.*/
        vector<char> buf((std::istreambuf_iterator<char>(myfile)), (std::istreambuf_iterator<char>()));
    
                    /*str_buf holds all the data including whitespaces and newline .*/
                    string str_buf(buf.begin(),buf.end());
    
                    myfile.close();
                } 
    

    【讨论】:

    • +1 用于 myfile.good() - 我认为这是一个错字,但是当 "good()=1 eof()=0 fail()= 0 bad()=0" - 似乎远优于 eof() 检查
    • 顺便说一句,您也可以执行“std::vector buf(std::istreambuf_iterator(myfile), {});” - 不确定这是否更清楚,afaik 它调用默认构造函数,所以 {} 可能会提供更多线索
    【解决方案6】:

    流提取器的行为相同并跳过空格。

    如果你想读取每个字节,你可以使用未格式化的输入函数,比如stream.get(c)

    【讨论】:

    • 正如@CharlesBailey 的回答:我仍然没有得到换行符。
    【解决方案7】:

    为什么不简单地使用getline

    你会得到所有的空格,虽然你不会得到行尾字符,但你仍然会知道它们的位置:)

    【讨论】:

      【解决方案8】:

      默认情况下,这个 skipws 标志已经在 ifstream 对象上设置,所以我们必须禁用它。 ifstream 对象具有这些默认标志,因为 std::basic_ios::init 会在每个新的 ios_base 对象 (more details) 上调用。 以下任何一种都可以:

      in_stream.unsetf(std::ios_base::skipws);
      in_stream >> std::noskipws; // Using the extraction operator, same as below
      std::noskipws(in_stream); // Explicitly calling noskipws instead of using operator>>
      

      cpp reference 上列出了其他标志。

      【讨论】:

        【解决方案9】:

        只需使用 getline。

        while (getline(input,current))
        {
              cout<<current<<"\n";
        
        }
        

        【讨论】:

        • 不是一个好的答案......你可能有一行没有'\n'在最后......在这种情况下你会添加'\n'即使它不存在
        【解决方案10】:

        我最终只是打开了 Windows API 并使用它首先将整个文件读入缓冲区,然后逐个字符地读取该缓冲区。谢谢各位。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-07-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多