【问题标题】:Reading a binary file line by line in C++在 C++ 中逐行读取二进制文件
【发布时间】:2016-05-07 20:18:22
【问题描述】:

我是 C++ 初学者,希望大家多多包涵。

尝试读取文本格式的文件,每个文件的行看起来像这样(前几行,称为标题行):

@HD VN:1.5  SO:queryname

或者像这样

read.1  4   *   0   0   *   *   0   0   CAACCNNTACCACAGCCCGANGCATTAACAACTTAANNNCNNNTNNANNNNNNNNNNNNTTGAAAAAAAAAAAAAAAAAA    A<.AA##F..<F)<)FF))<#A<7<F.)FA.FAA.)###.###F##)############)FF)A<..A..7A....<F.A    XC:Z:CAACCNNTACCA   RG:Z:A  XQ:i:2

两者都是制表符分隔的。

该文件非常大,因此是二进制格式。 我想知道是否可以从二进制格式文件中读取每一行,对该行进行一些处理,然后将其写入二进制格式输出文件。

我从这段代码开始:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
  string input_file = argv[1];
  string output_file = argv[2];
  string line;
  ifstream istream;
  istream.open(input_file.c_str(),ios::binary|ios::in);
  ofstream ostream;
  ostream.open(output_file.c_str(),ios::binary|ios::out);
  while(getline(istream,line,'\n')){
    if(line.empty()) continue;
    //process line assuming it is read as a string
    ostream<<line<<endl;
  }
  istream.close();
  ostream.close();
}

但是它崩溃了:Segmentation fault (core dumped),在我试图将line解析为stringvector的部分。

有没有办法读取二进制格式并按行拆分,对每一行进行字符串处理,然后将它们写入二进制输出?

顺便说一句,我在 Linux 上运行它。

【问题讨论】:

  • 原则上,每个文件都是二进制文件,因为这正是计算机的工作方式。现在,说“我正在尝试逐行阅读”显然意味着您将其视为文本文件。现在,如果它是一个文本文件,这可能不是问题,但如果它是一个没有任何换行符的大文件,你可能只是用完了所有的 RAM。
  • 顺便说一下,argv[0] 通常是你程序的可执行文件名,不是在运行时指定的第一个参数。
  • 吹毛求疵,但是:这不会编译。请确保 100% 确保您发布的代码与您可以自己尝试的代码相同
  • @PaulMcKenzie:抱歉,这不对。 LF 字符是\n。如果您在 Windows 上,变量 line 将有一个尾随 \r 字符,但仅此而已。 (有些旧的 Mac 中的行由 \r 而不是 \n 分隔,但 OP 不太可能在其中一个上处理 30GB 文件。还有一些文件系统确实没有行尾字符 - 但我敢打赌他也没有使用其中之一。)
  • @PaulMcKenzie 不。我为与您相矛盾而感到难过,但在这种情况下文件模式无关紧要。 getline 被明确告知分隔符是什么。

标签: c++ binary ifstream getline ofstream


【解决方案1】:

是否可以逐行读取二进制文件?

原则上,每个文件都是二进制文件,因为这正是计算机的工作方式。现在,说“我正在尝试逐行阅读”显然意味着您将其视为文本文件——“行”是一个文本概念。

文件非常大,因此是二进制格式。

这是一流的废话。大小不会改变文件的格式。

如何将每一行作为一个字符串? ostream&lt;&lt;line&lt;&lt;endl; 是否可以将字符串写入二进制文件?

是与否:如果您的文件不是文本文件,为什么这些'\n' 字符的位置很重要?对于非文本文件,这些只是普通字节,例如 'a'\0x000xFF。所以基本上,您正在查看 并尝试在其中发现字母。

但是,根据您对我们正在讨论的文件的说明,它们实际上是仅包含文本的文件。

所以您的问题似乎在于单行可能超过您在std::string 中可用的存储空间。这是一种罕见的情况——但它似乎可能发生在基因串上。好吧。

让自己熟悉 C++ 所具有的非面向文本的文件 I/O。基本上,有ifstream.read(),您应该使用它来获取(有限)数量的字节,进行处理,写入输出,重复。在您的输入中寻找换行符,如果您已经阅读过它,请“倒回”您的文件 (fseek)。

另外,我真的很想知道你的台词需要多长时间才能打破std::string。我猜你可能在一些非常有限的操作系统(32 位?)或计算机(非常少的 RAM + Swap?)上运行。

【讨论】:

  • 感谢您的插图。我希望我对二进制文件文本格式的编辑能让我的问题更清楚。
  • @dan 不,事实并非如此。
【解决方案2】:

如果您的文件结构为多行,并且每一行都以\n 结尾,那么它一个文本文件。每个文件底层都是二进制文件,文本文件只是一种特殊的二进制文件。

因此,鉴于此,您显示的代码可能适用于任何大小的文件。

您确实应该删除ios:binary,但我不认为它在这种情况下会产生任何影响。

但是,如果您在“处理”文件的一行时遇到了崩溃,那就是最有可能出现错误的地方 - 在您尚未披露的代码中!

【讨论】:

    【解决方案3】:

    您的文件似乎有一些超出您预期的行尾。它可能有\r,而您希望它有\n。如果是这种情况,那么std::getline 会尝试将整个 30GB 文件读入line std::string。

    我建议您检查文件中的行尾,以进行上述验证。如果是这种情况,那么您可以使用此 SO:Getting std :: ifstream to handle LF, CR, and CRLF? 中的行读取功能,即使它们的结尾与您的平台不兼容(或者更确切地说是您不期望的结尾),它也应该读取行。

    另外,使用非二进制文件模式应该没问题。您所显示的示例文件行对我来说看起来不是很二进制。

    【讨论】:

    • 好的,伙计们。非常感谢您的帮助(再次感谢可爱的插图 - Marcus Müller)。我要删除这篇文章,因为它无处可去。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 2013-12-06
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多