C 当文件末尾没有新行时,fgets无法正确读取
问题是,如果文件末尾没有新行,fgets显示错误。假设我有两个文本文件,如下所示 text1.txt的内容:C 当文件末尾没有新行时,fgets无法正确读取,c,file,C,File,问题是,如果文件末尾没有新行,fgets显示错误。假设我有两个文本文件,如下所示 text1.txt的内容: German Hoy 43 68 Jesse Boster 88 29 German Hoy 43 68 Jesse Boster 88 29 while( fgets (s, 60, file)!=NULL ) { s[strlen(s)-1] = '\0'; strcpy(tempName, s); fgets(s, 60, f
German Hoy
43
68
Jesse Boster
88
29
German Hoy
43
68
Jesse Boster
88
29
while( fgets (s, 60, file)!=NULL ) {
s[strlen(s)-1] = '\0';
strcpy(tempName, s);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempFinal);
setup(tempName, tempMid, tempFinal);
}
- 请注意,文件在29之后完全结束。29号以后没有电话
German Hoy
43
68
Jesse Boster
88
29
German Hoy
43
68
Jesse Boster
88
29
while( fgets (s, 60, file)!=NULL ) {
s[strlen(s)-1] = '\0';
strcpy(tempName, s);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempFinal);
setup(tempName, tempMid, tempFinal);
}
- 请注意,29后面还有一行
German Hoy
43
68
Jesse Boster
88
29
German Hoy
43
68
Jesse Boster
88
29
while( fgets (s, 60, file)!=NULL ) {
s[strlen(s)-1] = '\0';
strcpy(tempName, s);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempFinal);
setup(tempName, tempMid, tempFinal);
}
当文件结尾后还有一行时,程序将正确运行text2.txt。但是,如果我在文件末尾没有像text1.txt这样的一行代码,就不会出现这种情况。我怎样才能解决这个问题?我希望得到相同的结果,不管文件末尾是否还有一行。(在这两种情况下,它应输出相同的结果)
下面是与问题相关的部分源代码:
German Hoy
43
68
Jesse Boster
88
29
German Hoy
43
68
Jesse Boster
88
29
while( fgets (s, 60, file)!=NULL ) {
s[strlen(s)-1] = '\0';
strcpy(tempName, s);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempFinal);
setup(tempName, tempMid, tempFinal);
}
使用的系统是LINUX您可以在
s
缓冲区的末尾添加额外的新行
fgets(s, 60, file);
length = strlen(s);
s[length] = '\n';
s[length+1] = '\0';
sscanf(s, "%d", &tempFinal);
重要提示:
German Hoy
43
68
Jesse Boster
88
29
German Hoy
43
68
Jesse Boster
88
29
while( fgets (s, 60, file)!=NULL ) {
s[strlen(s)-1] = '\0';
strcpy(tempName, s);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempFinal);
setup(tempName, tempMid, tempFinal);
}
您必须确保您的缓冲区长度至少为61字节,以适合新行。您可以在
s
缓冲区的末尾添加额外的新行,而不必考虑
fgets(s, 60, file);
length = strlen(s);
s[length] = '\n';
s[length+1] = '\0';
sscanf(s, "%d", &tempFinal);
重要提示:
German Hoy
43
68
Jesse Boster
88
29
German Hoy
43
68
Jesse Boster
88
29
while( fgets (s, 60, file)!=NULL ) {
s[strlen(s)-1] = '\0';
strcpy(tempName, s);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
fgets(s, 60, file);
s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempFinal);
setup(tempName, tempMid, tempFinal);
}
您必须确保缓冲区长度至少为61字节,以适合新行。可选地剥离新行:
while( fgets (s, 60, file)!=NULL ) {
s[strcspn(s, "\n")] = '\0';
不要使用s[strlen(s)-1]='\0'代码>因为这可能是黑客利用的fgets()
读取空字符,就像读取任何其他非新行字符一样。行中的第一个字符可能是“\0”
,然后OP的代码调用未定义的行为
此外,在OP代码中,甚至不需要删除潜在的新行字符
fgets(s, 60, file);
// s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
另外,最好测试sccanf()
的返回值,或者使用strtol()
选择性地剥离新行:
while( fgets (s, 60, file)!=NULL ) {
s[strcspn(s, "\n")] = '\0';
不要使用s[strlen(s)-1]='\0'代码>因为这可能是黑客利用的fgets()
读取空字符,就像读取任何其他非新行字符一样。行中的第一个字符可能是“\0”
,然后OP的代码调用未定义的行为
此外,在OP代码中,甚至不需要删除潜在的新行字符
fgets(s, 60, file);
// s[strlen(s)-1] = '\0';
sscanf(s, "%d", &tempMid);
另外,最好测试sccanf()
的返回值,或者使用strtol()
此代码大约是一个MCVE:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[60];
char tempName[60];
int tempMid;
int tempFinal;
FILE *file = stdin;
while (fgets(s, 60, file) != NULL)
{
s[strlen(s) - 1] = '\0';
strcpy(tempName, s);
if (fgets(s, 60, file) == NULL)
break;
s[strlen(s) - 1] = '\0';
sscanf(s, "%d", &tempMid);
if (fgets(s, 60, file) == NULL)
break;
s[strlen(s) - 1] = '\0';
sscanf(s, "%d", &tempFinal);
printf("[%s] %d %d\n", tempName, tempMid, tempFinal);
}
return 0;
}
这正是我所期望的,因为您的代码会清除最后一行的最后一个字符,而不管它是否为换行符。如果希望输出包含29
的9
,则必须更加小心:
s[strcspn(s, "\n")] = '\0';
部署3次更改后,两个程序的输出相同
有什么区别strcspn()。如果没有换行符,它将报告空字节,赋值将用另一个空字节(no-op)覆盖空字节。但它是可靠和安全的。此代码大约是一个MCVE:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[60];
char tempName[60];
int tempMid;
int tempFinal;
FILE *file = stdin;
while (fgets(s, 60, file) != NULL)
{
s[strlen(s) - 1] = '\0';
strcpy(tempName, s);
if (fgets(s, 60, file) == NULL)
break;
s[strlen(s) - 1] = '\0';
sscanf(s, "%d", &tempMid);
if (fgets(s, 60, file) == NULL)
break;
s[strlen(s) - 1] = '\0';
sscanf(s, "%d", &tempFinal);
printf("[%s] %d %d\n", tempName, tempMid, tempFinal);
}
return 0;
}
这正是我所期望的,因为您的代码会清除最后一行的最后一个字符,而不管它是否为换行符。如果希望输出包含29
的9
,则必须更加小心:
s[strcspn(s, "\n")] = '\0';
部署3次更改后,两个程序的输出相同
有什么区别strcspn()。如果没有换行符,它会报告空字节,赋值会用另一个空字节覆盖空字节-一个no-op。但是它是可靠和安全的。您得到了什么输出?您应该检查每个fgets()
调用,以确保没有收到EOF报告。您应该向我们展示一个MCVE()-例如,您的setup()
调用可以被合适的printf()
调用替换。还有,你在哪个平台上工作?Windows与类Unix可能会有所不同。s[strlen(s)-1]='\0'代码>替换为s[strcspn(s,“\n”)]='\0'代码>。第二个和第三个s[strlen(s)-1]='\0'代码>是不必要的。感谢您的更新-除了更新不是MCVE,输入与前面描述的完全不同,并且您得到的输出丢失,并且您期望的输出从您显示的输入中不明显。事实上,更新一点帮助都没有。你得到了什么结果?您应该检查每个fgets()
调用,以确保没有收到EOF报告。您应该向我们展示一个MCVE()-例如,您的setup()
调用可以被合适的printf()
调用替换。还有,你在哪个平台上工作?Windows与类Unix可能会有所不同。s[strlen(s)-1]='\0'代码>替换为s[strcspn(s,“\n”)]='\0'代码>。第二个和第三个s[strlen(s)-1]='\0'代码>是不必要的。感谢您的更新-除了更新不是MCVE,输入与前面描述的完全不同,并且您得到的输出丢失,并且您期望的输出从您显示的输入中不明显。事实上,这个更新一点帮助都没有。末尾已经有一个空字节;你只添加了一个字符,所以缓冲区中只需要61个字节。你是对的,我认为fgets将执行类似于strcpy的操作:)@ameen是的,现在已修复,非常感谢!为什么不省略3行length=strlen(s);s[length]='\n';s[length+1]='\0'代码>?因为问题是当文件末尾没有换行符时,末尾已经有一个空字节;你只需要加一个c