我正在做一个关于 C 的项目,该项目读取文本文件并将其转换为布尔数组。首先,我将文件读取为大小为 n
的字符串(是一个无符号的 char 数组),然后我使用一个函数将该字符串转换为大小为 n * 8
的布尔数组。该功能完美运行,毫无疑问。
我使用以下代码从文件中获取字符串:
unsigned char *Data_in; // define pointer to string
int i;
FILE* sp = fopen("file.txt", "r"); //open file
fseek(sp, 0, SEEK_END); // points sp to the end of file
int data_dim = ftell(sp); // Returns the position of the pointer (amount of bytes from beginning to end)
rewind(sp); // points sp to the beginning of file
Data_in = (unsigned char *) malloc ( data_dim * sizeof(unsigned char) ); //allocate memory for string
unsigned char carac; //define auxiliary variable
for(i=0; feof(sp) == 0; i++) // while end of file is not reached (0)
{
carac = fgetc(sp); //read character from file to char
Data_in[i] = carac; // put char in its corresponding position
}
//
fclose(sp); //close file
问题是有一个由Windows XP中的记事本制作的文本文件。里面有这个 4 个字符的字符串":nnC"
(冒号、回车键、回车键、大写 C)。
这是HxD(十六进制编辑器)的样子:3A 0D 0A 0D 0A 43
.
下表更清楚地说明了:
character hex decimal binary
: 3A 58 0011 1010
n (enter+newline) 0D 0A 13 10 0000 1101 0000 1010
n (enter+newline) 0D 0A 13 10 0000 1101 0000 1010
C 43 67 0100 0011
现在,我执行程序,该程序以二进制形式打印该部分,因此我得到:
character hex decimal binary
: 3A 58 0011 1010
(newline) 0A 10 0000 1010
(newline) 0A 10 0000 1010
C 43 67 0100 0011
好了,现在显示了这一点,我问以下问题:
- 读数正确吗?
- 如果是这样,为什么它会去掉 0D?
- 这是怎么回事?
将fopen
设置为二进制文件:
fopen("file.txt", "rb");
^
否则,您的标准库只会吃掉r
(0x0D
)。
作为旁注,以二进制模式打开文件还可以缓解另一个问题,即文件中间的某个序列看起来像DOS上的EOF。
您将文件视为 ASCII 文件。如果将其视为二进制文件,您将能够看到这两个字符。为此,请在打开文件时使用"rb"作为模式。也使用阅读文件内容。
除了"rb"问题之外,还有一个错误:您将在末尾读取一个额外的字符,因为读取最后一个字符后feof(sp)
保持 0。只有在您尝试读取过去的 EOF 后,它才设置为 1。这是一个常见的初学者错误。迭代输入字符的惯用 C 代码是
int c; /* int, not char due to EOF. */
while ((c = fgetc(sp)) != EOF) {
/* Work with c. */
}
其他答案讨论了二进制与文本模式输入。
您的代码实际上有一个单独的问题。 这个成语是给帕斯卡的,而不是C:
for (i = 0; feof(sp) == 0; i++)
{
carac = fgetc(sp);
Data_in[i] = carac;
}
麻烦的是,当fgetc()
获得EOF时,您将其视为一个字符(可能将其映射到ÿ,y-变音,U + 00FF,拉丁小写字母Y与diaeresis)。 feof()
测试放错了地方;它不会在尝试读取下一个字符之前检测到 EOF。 此外,函数fgetc()
及其getc()
和getchar()
都返回一个int
,而不是一个char
。 你必须学会使用标准的C习语:
int c;
for (i = 0; (c = fgetc(sp)) != EOF; i++)
Data_in[i] = c;
成语是作业和测试的结合。 围绕它的计数不太标准;事实上,这可能相当罕见。 但这并没有错;它适用于您的程序。
在大多数 C 代码中不需要使用 feof()
;几乎任何时候你使用它,它都是一个错误。 并非总是如此;它的存在是有目的的。 但这样做的目的是区分 EOF 和函数(如 fgetc()
返回 EOF)后的错误,而不是在读取函数说它已达到 EOF 之前测试您是否已经达到 EOF。 (在我所有的数百个程序中,我认为对feof()
的引用很少:2884个源文件,18个对feof()
的引用,以及大多数最初由其他人编写的代码。