UNIX API调用:使用read()函数打开文件并将其打印到屏幕会添加额外的随机字符

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UNIX API调用:使用read()函数打开文件并将其打印到屏幕会添加额外的随机字符相关的知识,希望对你有一定的参考价值。

我正在尝试编写一个程序来使用UNIX API调用来比较两个文本文件。以下是我的两个文件的内容:

f1.txt

This is my sample.
It contains text
And for some reason
The last few chars
are duplicated?

f2.txt

This is another sample
Sometimes instead of
duplicating the last few chars,
it prints another new line
instead
4567865

我有一个打开并读取这些文件的cpp文件。我的OpenRead函数将文件名作为c字符串,并将文本文件的内容放入字符串并返回它。

    #include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <string>
#include <cstring>



using namespace std;

string OpenRead(const char*);

int main(int argc, char **argv)
{
    string text1 = "", text2 = "";

    string file1(argv[1]);
    string file2(argv[2]);


    text1 = OpenRead(file1.c_str());
    text2 = OpenRead(file2.c_str());
    cout << text1 << endl;
    cout << text2 << endl;

    exit(EXIT_SUCCESS);


 return 0;
}



string OpenRead(const char* filename)
{
    int inFD1;
    string text;

    char * buf = new char[fsize(filename)];

    inFD1 = open(filename, O_RDONLY, 0);
    if(inFD1 < 0) exit(EXIT_FAILURE);
    else
    {
         while (read(inFD1, buf, sizeof(int)) != 0) 
            text += buf; //cout << buf;

    }   

    close(inFD1);
    delete [] buf;
    return text;
}

size_t fsize(const char *filename) {
    struct stat st; 

    if (stat(filename, &st) == 0)
        return st.st_size;

    return -1; 
}

问题是,当我将其编译成可执行文件并运行我的命令:fileComp f1.txt f2.txt时,它会打开并读取它们几乎完全正常,但会产生奇怪的输出,其中额外的字符会附加到结尾。这是输出的样子:

This is my sample.
It contains text
And for some reason
The last few chars
are duplicated?
e
This is another sample
Sometimes instead of
duplicating the last few chars,
it prints another new line
instead
4567865
8

出于某种原因,它将e附加到第一个文件,将8附加到第二个文件。此行为因文本文件而异,但始终会将缓冲区中的随机字符附加到末尾。

答案

我看到的主要问题是你没有检查读取的结果是否读取了字符数。

    while (read(inFD1, buf, sizeof(int)) != 0) 

你要求每次读取时使用sizeof(int)字节。但它可能回报不到这个。所以你真的应该有这个价值。

此外,当向结果text添加缓冲区时,您假设缓冲区已经为零(因此您将获得默认的空终止符' 0')

        text += buf; //cout << buf;

请注意,operator+=假设buf是一个C-String,因此无效。您没有为您的代码提供该保证。

int len;
while ((len = read(inFD1, buf, sizeof(int))) > 0) {
    text.append(buf, len); 
}

一旦你得到它的工作。把它带到https://codereview.stackexchange.com查看最佳实践。

另一答案

通常的错误。问题出在您的代码中:

while (read(inFD1, buf, sizeof(int)) != 0) 
    text += buf; //cout << buf;

这应该是:

int count;
while ((count = read(inFD1, buf, sizeof(int))) > 0) 
    text.append(buf, count); 

注意:

  • 用文件名的长度声明buf,或者将任何小于buf的实际长度的东西传递给read()函数是没有意义的。
  • 您不需要动态分配buf。像其他人一样使用char buf[4096];
另一答案

你的阅读循环被破坏了:

     while (read(inFD1, buf, sizeof(int)) != 0) 
        text += buf; 

问题的原因

首先,您必须验证read()返回正数,因为它也可以返回负的错误消息,使buf处于未知状态。

然后你必须得到读取的字节数(read()的返回值),知道缓冲区内容的结束位置,否则你可以在缓冲区中保留剩余的单位化字符。如果最后没有空终结符,你很可能得到UB。

最后,我希望您的文件不是二进制文件,因为任何将被读取的null都将被解释为字符串终止符,从而导致输入被截断。

更好的方法

另请注意,您保留一个可以包含完整文件的缓冲区,但每次只读取几个字节(精确地为sizeof(int)字节),然后将它们复制到一个字符串中。为什么不使用C ++流并使用getline()读取内容以方便字符串或使用fstream::read()读取更大的数据块?

另一答案

声明text += buf期望buf被终止。但是,在从文件读取数据之后,循环不会放置空字符。至少,您需要将其添加到循环中:

ssize_t len;
while ((len = read(inFD1, buf, sizeof(int))) > 0) 
{
    buf[len] = 0; // <-- add this
    text += buf;
}

如果qazxsw poi的大小至少没有分配给qazxsw poi字节,或者文件数据中包含任何空字节,那么哪个BTW会中断。

您可以使用buf方法来避免null终止符问题:

sizeof(int)

话虽如此,因为你将std::string::append()分配给文件的完整大小,但是你的循环只读取ssize_t len; while ((len = read(inFD1, buf, sizeof(int))) > 0) text.append(buf, len); 每次迭代的字节数,你在buf浪费sizeof(int)字节数(并且你的代码在fsize(filename) - sizeof(int)时有未定义的行为) 。

您应该使用fsize(filename) > sizeof(int)的固定大小。在每次循环迭代中,不需要分配超过实际读取的内容:

fsize(filename) < sizeof(int)

或者,只需预先分配buf,然后直接将文件读入string OpenRead(const char* filename) { int inFD1 = open(filename, O_RDONLY, 0); if (inFD1 < 0) exit(EXIT_FAILURE); string text; char buf[1024]; ssize_t len; do { len = read(inFD1, buf, sizeof(buf)); if (len == -1) { close(inFD1); exit(EXIT_FAILURE); break; } if (len == 0) break; text.append(buf, len); } while (true); close(inFD1); return text; } 。这样,您根本不需要分配临时缓冲区:

text

话虽这么说,你正在做的是读取文件的C方式。您正在编写C ++代码,因此您应该使用C ++样式文件I / O.有很多方法可以将文件读入text。见string OpenRead(const char* filename) { size_t size = fsize(filename); if (size < 0) exit(EXIT_FAILURE); string text; if (size > 0) { int inFD1 = open(filename, O_RDONLY, 0); if (inFD1 < 0) exit(EXIT_FAILURE); text.resize(size); char *buf = &text[0]; ssize_t len; do { len = read(inFD1, buf, size); if (len <= 0) { close(inFD1); exit(EXIT_FAILURE); break; } buf += len; size -= len; } while (size > 0); close(inFD1); } return text; }

以上是关于UNIX API调用:使用read()函数打开文件并将其打印到屏幕会添加额外的随机字符的主要内容,如果未能解决你的问题,请参考以下文章

UNIX读写数据过程

UNIX环境高级编程:文件I/O

Unix环境高级编程文件I/O

Unix环境高级编程标准I/O库

Unix文件 I/O(不带缓冲区的)上

Unix系统编程