我编写了一个程序,它从文本文件的每一行读取四个变量(三个字符串和一个字符)。但是当我显示变量时,每行末尾会弹出一个意外的字符。 (我确保变量的长度足够大)。
这是为什么? (再次溢出缓冲区?)我该如何解决这个问题呢?
文本文件内容:
M0001 Cool Name F 123-456789 M0002 Name Cool M 987-654321
代码:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *text;
char id[6], name[101], gender, contact[13];
text = fopen("test.txt", "r");
while (fscanf(text, "%s %[^\n]s %c %s\n", id, name, &gender, contact) != EOF)
printf("%s %s %c %s\n", id, name, gender, contact);
fclose(text);
return 0;
}
我期望的输出:
M0001 Cool Name F 123-456789 M0002 Name Cool M 987-654321
我得到了什么:
M0001 Cool Name F 123-456789 1⁄4 M0002 Name Cool M 987-654321 1⁄4
答案 0 :(得分:5)
在fscanf()
的调用中,格式字符串:&#34;%s%[^ \ n] s%c%s \ n&#34;是不正确的。
以下提议的代码:
fscanf()
fopen()
的任何错误,如果返回错误,请正确输出错误消息以及指示系统认为函数失败的原因的文本stderr
fscanf()
和printf()
enum
声明现在建议的代码:
#include <stdio.h> // fopen(), fclose(), fscanf(), perror(), printf()
#include <stdlib.h> // exit(), EXIT_FAILURE
enum{
MAX_ID_LEN = 6,
MAX_NAME_LEN = 20,
MAX_CONTACT_LEN = 13
};
int main( void )
{
char id[ MAX_ID_LEN ];
char firstname[ MAX_NAME_LEN ];
char lastname[ MAX_NAME_LEN ];
char gender;
char contact[ MAX_CONTACT_LEN ];
FILE *text = fopen("test.txt", "r");
if( !text )
{
perror( "fopen to read 'test.txt' failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
while (5 == fscanf(text, "%5s %19s %19s %c %12s",
id, firstname, lastname, &gender, contact) )
{
printf("%s %s %s %c %s\n",
id, firstname, lastname, gender, contact);
}
fclose(text);
return 0;
}
答案 1 :(得分:3)
%[^\n]s
从那时开始吃掉所有内容并将其放入name
。因此只填充id
和name
。 gender
和contact
具有来自程序堆栈的“随机”内容(因为它们未初始化)。
在1/4
+ gender
中,您的筹码不小心contact
。
在我的机器上,程序崩溃。
答案 2 :(得分:0)
由于您名称中以空格分隔的单词数量显然是可变的,您只能使用%[^\n]s
来“尽可能多地”抓取 - 但这也会消耗掉任何以下的 / em>相关数据。一个快速的解决方案是重新设计输入格式并将名称放在最后;那么,你的fscanf
参数将是:
"%s %c %s %s\n", id, &gender, contact, name
或者,重写代码以使用更少的fscanf
和更多“手动”解析:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main (void)
{
FILE *text;
char id[6], name[101], gender, contact[13];
char *lookback;
int result;
unsigned int line_number = 0;
text = fopen ("test.txt", "r");
if (text == NULL)
{
printf ("file not found!\n");
return EXIT_FAILURE;
}
do
{
result = fscanf(text, "%s %[^\n]s\n", id, name);
line_number++;
if (result == EOF)
break;
if (result != 2)
{
printf ("error in data file on line %u (expected at least 2 items)\n", line_number);
break;
}
/* at this point, 'name' also contains 'gender' and 'contact' */
lookback = strrchr (name, ' ');
if (lookback == NULL || strlen(lookback+1) > 12)
{
printf ("error in data file on line %u (expected 'contact')\n", line_number);
break;
}
/* lookback+1 because lookback itself points to the space */
strcpy (contact, lookback+1);
/* cut off at lookback */
*lookback = 0;
lookback = strrchr (name, ' ');
if (lookback == NULL || strlen(lookback+1) != 1)
{
printf ("error in data file on line %u (expected 'gender')\n", line_number);
break;
}
/* lookback now points to the space before the gender */
gender = toupper(lookback[1]);
if (gender != 'F' && gender != 'M')
{
printf ("error in data file on line %u (expected 'M' or 'F')\n", line_number);
break;
}
/* cut off again at lookback; now name is complete */
*lookback = 0;
printf ("%s %s %c %s\n", id, name, gender, contact);
} while (1);
fclose(text);
return EXIT_SUCCESS;
}
这种方法确实有一些相关的缺点。 scanf
的一个好处是它可以规范化空白;在扫描之前,多个空格和制表符(甚至返回)将静默地转换为单个空格。另一方面,此代码显式检查单个空格字符。如果数据文件中的空白存在差异,您也必须考虑到这一点。
最后两项“手动”处理后,您可以选择根本不使用fscanf
。您可以使用fgets
(内置行长检查)一次阅读整行文本,并使用strchr
和strrchr
查找空格。要解决可能的空白问题,请在行中搜索制表符和双精度空格,然后将其更改为单个空格。