我在 C 中使用二进制文件的 I/O 哪里出错了?

Posted

技术标签:

【中文标题】我在 C 中使用二进制文件的 I/O 哪里出错了?【英文标题】:Where did i go wrong with I/O with binary files in C? 【发布时间】:2020-06-12 21:47:44 【问题描述】:

我需要使用凯撒密码加密一个句子(它还没有完成,还需要检查xyzXYZ),打开一个二进制文件并在那里写入加密代码。第一个参数应该是二进制文件的名称,然后选择是否要添加句子或显示它,如果我输入“#”作为第一个参数,它会提示我输入要读取的二进制文件的名称编码。它应该是这样的:

./a.out CODE.DAT >> A >> Kekli kek >> S >> Nhnol nhn >> E.

现在当我这样做时:

./a.out '#' >> CODE.DAT 

它给了我正确的字符串,直到最后一个字符。像

Nhnol nhM�b�U"

另外,如果我的代码中有任何不好的习惯,或者有任何关于编写更简洁、更好的代码的建议,如果你能指出来,我会非常高兴。

主要功能:

int main(int argument_count, char **argument_vector) 
    if ((**(argument_vector + 1)) == '#') 
        FILE *stream;
        printf("Name of file: \n");
        char stream_name[15];
        scanf("%s", stream_name);
        if ((stream = fopen(stream_name, "rb")) != NULL) 
            char string[50];
            fread(string, sizeof(char *), 1, stream);
            printf("%s\n", string);
            fclose(stream);
        
     else
    if (argument_count == 2) 
        char character;
        do 
            printf("ADD [A], SHOW [S], END [E]\n");
            scanf("\n%c", &character);
            if (character == 'A') 
                FILE *stream;
                char string[50];
                read(string);
                code(string);
                if ((stream = fopen(*(argument_vector + 1), "wb")) != NULL) 
                    fwrite(string, sizeof(char *), 1, stream);
                    fclose(stream);
                
             else
            if (character == 'S') 
                FILE *stream;
                if ((stream = fopen(*(argument_vector + 1), "rb")) != NULL) 
                    char string[50];
                    fread(string, sizeof(char *), 1, stream);
                    printf("%s\n", string);
                    fclose(stream);
                
            
         while (character != 'E');
     else
        return printf("Error"), 1;

代码功能:

void code(char *string) 
    int i = 0;
    for(; string[i] != '\0'; i++) 
        if ((int)string[i] == 32)
            string[i] = ' ';
        else
            string[i] += 3;
    
    string[i + 1] = '\0';

读取函数:

void read(char *string) 
    printf("Input string: \n");
    scanf("\n%[^\n]%*c", string);

【问题讨论】:

每个人都调用主参数argcargv。不要编自己的名字,这会让任何阅读你代码的人感到困惑。 并使用普通数组访问语法:(** (argument_vector + 1))应该是argv[1][0] 文件名可以超过 14 个字符。避免使用任意长度的缓冲区。您在使用fread 读取的缓冲区上使用printf。不保证会被'\0' 终止。 fread 函数一次读取 8 个字节,这完全没有意义。但是,如果您只是将fwrite 缓冲到stdout 并且不要忘记使用fread 的返回值,它应该可以工作。 return printf("Error"), 1; 不要这样做! 【参考方案1】:

问题出在这段代码中:

        char string[50];
        fread(string, sizeof(char *), 1, stream);
        printf("%s\n", string);

fread() 读取二进制数据,因此不会添加空终止符。

另外,sizeof(char *) 是错误的。您正在读取一个字符串,而不是一个指针。但fread() 用于读取固定大小的数据。如果要读取可变大小的字符串,应指定1 作为对象的大小,最多指定49 个对象(需要为空终止符留出空间)。 fread 返回它成功读取的对象的数量,使用它来确定在哪里添加空字节。

        char string[50];
        size_t n = fread(string, 1, 49, stream);
        if (n > 0) 
            string[n] = '\0';
            printf("%s\n", string);
        

【讨论】:

您可能还提到sizeof(char *) 与从文件中读取字符串无关。 糟糕,我读的是sizeof(char)fread() 似乎完全错了。 @Barmar 我的字符串的最后一个字符没有被这段代码打印出来,我玩了一下,你应该把它编辑为 string[n+1] = '\0'。这行得通。谢谢! @MarkoMajstorovic 否,因为数组索引是从零开始的。如果您读取 10 个字符,它们会进入索引 09,而 null 进入索引 string[10] @Barmar 当我使用字符串 [n] 时,字符串的最后一个字符不会被打印,字符串 [n + 1] 效果很好,我不知道为什么,但如果你有一些空闲时间你可以自己看看。没有任何额外的代码。

以上是关于我在 C 中使用二进制文件的 I/O 哪里出错了?的主要内容,如果未能解决你的问题,请参考以下文章

C和指针 第十五章 文件I/O和二进制I/O

APUE---标准I/O库

c语言辗转相除法 将十进制转换成任意进制的数 运行结果出错,与正确不符,求指导哪里出错

C# 等效于 C 的 fread 文件 i/o

文件I/O

Java基础——I/O