【问题标题】:Reading a file stops at the first null character读取文件在第一个空字符处停止
【发布时间】:2021-03-28 18:31:22
【问题描述】:

我的 C++ 知识非常有限,所以在此先道歉,因为这可能是一个非常简单的问题,但我一直无法找到解决方案。

我有一个正在尝试读取的二进制文件。尝试读取二进制文件的代码如下所示:

string readFile2(const string &fileName)
{
    cout << "B1 \n";
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(bytes.data(), fileSize);
    cout << bytes.data();;
    cout << "\n";
    cout << fileSize;
    cout << "\n";
    return bytes.data();
    // return string(bytes.data(), fileSize);
}

根据cout &lt;&lt; fileSize; 的输出,它显示744402 字节,但是当我打印出bytes.data() 时,我只得到前8 个字节LIZM 2.9。我使用 hexdump 工具查看二进制文件,发现第 9 个字节是空字符。前 16 个字节的十六进制转储与相应的 ASCII 如下所示:

 4C 49 5A 4D 20 32 2E 39  00 00 21 C4 00 00 00 00  LIZM 2.9 __!____

如您所见,_ 对应于空字符 00。我的问题是如何读取每个字节而不是在空字符处停止?

【问题讨论】:

  • 在使用tellg() 获取文件大小之前,您必须先搜索到文件末尾。
  • 确定您没有阅读整个文件吗?我认为你的问题可能不是你想的那样。
  • @Eljay "ate: seek to the end of stream immediately after open" - 没有什么冲突/模棱两可的。
  • @Eljay 另见filebuf::open(): "如果打开操作成功并且openmode &amp; std::ios_base::ate != 0(设置了ate 位),则将文件位置重新定位到文件末尾,如if调用std::fseek(file, 0, SEEK_END),其中file是调用fopen返回的指针。如果重新定位失败,调用close()并返回一个空指针表示失败。"

标签: c++


【解决方案1】:

问题不在于您如何读取文件,而在于您如何输出其数据。您将数据视为以 null 结尾的 char* 字符串,它将在遇到的第一个 nul 字符处中断。非文本二进制文件中往往有很多 0x00 字节。

替换这个:

cout &lt;&lt; bytes.data();

有了这个:

cout.write(bytes.data(), fileSize);

然后替换这个:

return bytes.data();

有了这个:

return string(bytes.data(), fileSize);

或者,请参阅How do I read an entire file into a std::string in C++?

【讨论】:

  • 谢谢,这解决了问题,但是如果我试图将二进制数据保存到采用“BLOB”类型的数据库中怎么办?它必须是二进制格式,所以当我返回一个字符串而不是二进制数据类型时,postgres 会抱怨
  • @Mark 只需确保在使用二进制数据填充 blob 时考虑二进制的大小。不要仅仅依靠指向二进制数据的指针。如果您将二进制数据存储在字符串或向量或其他任何内容中,这并不重要。这只是内存管理的问题。
  • 您能提供更多细节吗?我对此还是很陌生。谢谢。
  • @Mark 没有看到你遇到问题的 postgres 代码。
  • 我将创建一个新问题并将其链接到此处。再次感谢您。
【解决方案2】:

我建议使用显式搜索到文件末尾而不是使用ios::ate,就像我在下面的代码中所做的那样。

还请注意,您的数据中有零字节,这意味着当输出到 cout 并从函数返回时,您的字符串将被第一个零截断。您必须在给定文件大小的情况下显式初始化返回的字符串,就像您在代码的最后一行注释掉一样。

Try it online!

#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary);

    ifs.seekg(0, ios::end);
    ifstream::pos_type fileSize = ifs.tellg();
    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(bytes.data(), fileSize);
    cout.write(bytes.data(), fileSize);
    cout << "\n";
    cout << fileSize;
    cout << "\n";
    return string(bytes.data(), fileSize);
}

int main() {
    readFile2("readme.txt");
}

输入:

Hello, World!
Hello, again!

输出:

Hello, World!
Hello, again!

28

【讨论】:

    猜你喜欢
    • 2017-01-19
    • 1970-01-01
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多