【问题标题】:Why writing a text file in C does not gives me expected results?为什么用 C 编写文本文件没有给我预期的结果?
【发布时间】:2023-01-08 20:40:30
【问题描述】:

我正在尝试做几个练习来了解在 C 上写入文本文件和二进制文件之间的区别,并且在使用 hexdump 实用程序查看结果时,我发现了意想不到的结果。你能帮我理解原因吗?

特别是,我正在尝试使用以下代码编写文本文件:

#include <stdio.h>

int main() {
    FILE *ptr_myfile;
    char c = 'a';
    int numero = 12345;

    ptr_myfile = fopen("test.txt","w");

    if (!ptr_myfile){
        printf("Unable to open file!");
        return 1;
    }

    fwrite(&c, sizeof(char), 1, ptr_myfile);
    fwrite(&numero, sizeof(int), 1, ptr_myfile);

    fclose(ptr_myfile);

    return 0;
}

在执行“cat test.txt”时,我发现文件的内容是:

猫测试.txt

a90

无法理解 12345 是如何转换为 90 的。

此外,如果我做一个

hexdump test.txt

0000000 3961 0030 0000
0000005

在那种情况下,我发现第一个字节的值是 39。为什么?第二个值 (61) 已经与 'a'' 的 ascii 值匹配(61 hex = 97 dec = 'a' ascii code),但找不到其余位的逻辑解释。

如果我将写入模式更改为二进制文件,修改行

ptr_myfile=fopen("test.txt","w")  by ptr_myfile=fopen("test.txt","wb")

我没有看到文件书面内容的行为有任何变化。

【问题讨论】:

  • 使用fwrite,您可以写入值的原始二进制数据,而不是它们的文本表示形式。对于 int,通常是四个字节的数据。
  • 提示,十六进制的 12345 是 3039。
  • 使用hexdump -C,应该不会那么混乱
  • @Someprogrammerdude,所以,对于 fwrite,我总是写二进制文件,无论我以哪种模式打开文件?您是说我将文件视为文本的二进制文件吗?为什么忽略“fopen”模式?
  • 对,那是正确的。十进制值12345将被写为四个字节0x00003039。如果你想写文字,使用例如fprintf喜欢fprintf(ptr_myfile, "%c%d", c, numero)

标签: c


【解决方案1】:

test.txt 文件的内容是:

$ hexdump -C test.txt

00000000  61 39 30 00 00                                    |a90..|
00000005

第一个字节61'a',之后的字节是12345little-endian表示。

39 30 00 00 是 4 个字节,这是 int 的典型大小。

请注意,此号码不是0x39300000,而是0x00003039

写入数字的字节顺序取决于系统的字节顺序。

您可以通过使用 htonl 将主机端字节序转换为大端字节序(网络字节顺序)来自己观察这一点:

#include <stdio.h>

int main() {
    FILE *ptr_myfile;
    char c = 'a';
    int numero = 12345;
    ptr_myfile = fopen("test.txt","w");

    if (!ptr_myfile) {
        printf("Unable to open file!");
        return 1;
    }

    // convert from host endianness to network byte order
    int numero_big_endian = htonl(numero);

    fwrite(&c, sizeof(char), 1, ptr_myfile);
    fwrite(&numero_big_endian, sizeof(int), 1, ptr_myfile);
    fclose(ptr_myfile);

    return 0;
}

这将产生:

$ hexdump -C test.txt

00000000  61 00 00 30 39                                    |a..09|
00000005

如您所见,字节顺序现在颠倒了。

这是您可能不想将二进制数据直接写入磁盘的原因之一,因为字节序存在差异。

大端系统会将0x00003039识别为0x39300000,这将是959447040而不是1234

正如其他人所提到的,fwrite 不会以其字符串表示形式写入数据。

如果需要,您可以先使用snprintf 将您的号码转换为字符串,然后将其写入文件:

#include <stdio.h>
#include <string.h>

int main() {
    FILE *ptr_myfile;
    char c = 'a';
    int numero = 12345;
    ptr_myfile = fopen("test.txt","w");

    if (!ptr_myfile) {
        printf("Unable to open file!");
        return 1;
    }

    // convert numero to a string
    char numero_str[64];
    // check result of numero_str, omitted for readability
    snprintf(numero_str, sizeof(numero_str), "%d", numero);

    fwrite(&c, sizeof(char), 1, ptr_myfile);
    fwrite(numero_str, strlen(numero_str), 1, ptr_myfile);
    fclose(ptr_myfile);

    return 0;
}
$ cat test.txt

a12345

【讨论】:

    【解决方案2】:

    当您使用fwrite 时,write 函数处理数据时就好像它是一定长度的二进制文件一样。这与您之前选择的文件打开方式无关。

    让我们考虑以下示例:

    /** A character buffer. */
    char *ascii_buf = "ABCD";
    
    /** A buffer which contains binary representation of A, B, C, D letters in ASCII. */
    uint8_t binary_buf[4] = { 65, 66, 67, 68 };
    
    written = fwrite(ascii_buf, 1, strlen(ascii_buf), fout);
    written = fwrite(binary_buf, 1, sizeof(binary_buf), fout);
    

    以上对fwrite 的两次调用在目标输出文件中产生了相同的输出"ABCD"

    唯一的区别在于解释数据的方式。在第一种情况下,ascii_buf 数据被解释为字符。而在第二种情况下,binary_buf 数据被解释为无符号整数。内容相同,但它们的表示不同。

    你通常会想使用:

    • fprintf 将格式化字符串输出到文件。
    • fwrite 将原始数据输出到文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-06
      • 1970-01-01
      • 2015-10-07
      • 1970-01-01
      • 2013-11-21
      • 2013-12-20
      • 1970-01-01
      • 2019-10-04
      相关资源
      最近更新 更多