【问题标题】:Reading lines of text file in C++ fails due to hidden/control characters由于隐藏/控制字符,在 C++ 中读取文本文件行失败
【发布时间】:2013-08-26 00:40:02
【问题描述】:

我有两个文本文件,它们在文本编辑器中看起来是相同的,但是我用于读取文件的 C++ 代码会为每个文件生成不同的行数。我不知道文件在哪里不同,或者如何在我的 C++ 代码中适应这种差异。

让我解释一下……

我有两个文本文件,d1.txt 和 d2.txt。每个包含 100 个数字,每行 1 个。当我在 vim 中打开任一文件并输入 :set list! 时,只有 100 行,每行包含一个数字和每行最后一个数字后面的行尾字符 ($)。换句话说,在 vim 中查看它们时,它们看起来相同,只是数字的精度不同。精度不同,因为一个文件来自 MATLAB,另一个来自 Gnumeric。

文件的快速差异呈现以下输出(我使用大括号省略号“[...]”来省略部分以节省空间):

1,28c1,28
< 0.01218465532007
       [...]
< 0.01327976337895
---
> 0.0121846553200678
       [...]
> 0.0132797633789485
30,100c30,100
< 0.01329705254301
       [...]
< 0.00017832496354
---
> 0.0132970525430057
       [...]
> 0.000178324963543758
\ No newline at end of file

尽管有关于第二个文件 (d2.txt) 末尾没有换行符的消息,但在 vim 中检查文件的最后一行时,我看不到任何区别,正如我上面提到的。

我创建了一个 C++ 函数readVectorFromFile(std::vector&lt;double&gt;&amp;,const string),它返回从相应文本文件中读取的行数。当我使用代码阅读文本文件时:

std::cout << "d1.txt has " << readVectorFromFile(v1,"./d1.txt") << " lines.\n";
std::cout << "d2.txt has " << readVectorFromFile(v1,"./d1.txt") << " lines.\n";

我得到了输出:

d1.txt has 99 lines.
d2.txt has 100 lines.

函数定义如下:

int readVectorFromFile(vector<double>& vec, const string& fullFilePathName) {

    int value, numLines;
    char line[10000];
    ifstream inFile;

    /* attempt to open file */
    inFile.open(fullFilePathName.c_str());
    if (inFile.fail()) {
        LOG(FATAL) << "Unable to open file \"" << fullFilePathName.c_str() << "\" for reading.";
    } else {
        cout << "Importing vector from file " << fullFilePathName.c_str() << "\n";
    }

    /* records the number of lines in the input file */
    numLines = static_cast<int>( count(istreambuf_iterator<char>(inFile),
                                       istreambuf_iterator<char>(), '\n') );

    /* start file over from beginning */
    inFile.clear();
    inFile.seekg(0, ios::beg);

    vec.clear(); // clear current vec contents
    vec.reserve(numLines);

    /* read value from each line of file into vector */
    for(int i=0; i<numLines; ++i) {
        inFile.getline(line, 10000);
        vec.push_back( strtod(line,NULL) );
    }

    inFile.close(); // close filestream

    return numLines; // return the number of lines (values) read

}

为什么我在 vim 中查看这些文件时看不到它们之间的区别?导致此问题的上述功能是否存在根本性错误?

【问题讨论】:

  • 在 d1.txt 中添加一个换行符。即使文件不是以一个结尾,Vim 也会呈现一个换行符。而且您的行数只计算换行符。因此,行数减少 1 也就不足为奇了。
  • 如果您的文件有任何双换行符或空行,上述函数将友好。我认为我理解你想要做什么,我认为这可以大大简化。
  • @WhozCraig 是的,该功能仅供我个人使用,它取决于没有双换行符或空行的假设。
  • @FDinoff 有没有办法强制 vim 显示所有 ASCII 字符,包括换行符?似乎应该有,因为 vim 非常强大且可自定义。
  • 实际上,vim 似乎在文件末尾添加了一个新行,它之前缺少一个 (stackoverflow.com/questions/1050640/…) (如果你重新保存文件,换行符就会出现)。 :set list 可能是你能得到的最接近的。

标签: c++ vim text ifstream


【解决方案1】:

根据您的描述,这两个文件之一的末尾没有换行符。您可以使用例如od -c file | less 查看文件以查看文件的确切内容,包括它们的字符代码。

也就是说,您阅读行的方法可能会有所改进:只需读取一行,检查它是否可以读取,然后处理它。这样,就无需预先计算行尾数:

for (std::string line; std::getline(inFile, line); ) {
    vec.push_back(strtod(line.c_str()));
}

就个人而言,我可能会首先阅读数字,例如:

for (double value; inFile >> value; ) {
    vec.push_back(value);
}

嗯,这并不是将doubles 序列读入向量的真正方法,但这是:

std::vector<double> vec((std::istream_iterator<double>(inFile)),
                        std::istream_iterator<double>());

(您可以在 C++11 中使用统一初始化符号来代替额外的括号)。

【讨论】:

  • +1 如果每行一个确实是一个要求,并且每行有更多,那么它会涉及更多(显然)但不会太多。很好的答案。
  • 在使用 istream_iterator 的第三个解决方案中,如何修改它以读入通过引用传递给函数的 std::vector ? (而不是在函数内声明一个新的此类对象)我只是好奇。
  • @synaptik:看看std::vector&lt;T&gt; 界面!例如,您可以使用v.swap(std::vector&lt;std::string&gt;(begin, end));v.assign(begin, end)(其中beginend 只是对应的std::istream_iterator&lt;std::string&gt; 对象)。
  • 这很酷。是的,我需要更多的 RTFM。从来没有真正尝试过深入了解 STL 更强大的方面,但在这篇 SO 帖子之后,我想我会的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 2013-05-08
  • 1970-01-01
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多