C 读取文本文件后,结果显示不正确

C 读取文本文件后,结果显示不正确,c,string,text-files,stdio,C,String,Text Files,Stdio,我编写了一个程序,从文本文件中每行读取四个变量(三个字符串和一个字符)。但是,当我显示变量时,每行末尾会弹出一个意外字符。(我已经确保变量的长度足够大) 为什么会这样?(还是缓冲区溢出?) 文本文件内容: M0001 Cool Name F 123-456789 M0002 Name Cool M 987-654321 M0001冷名称F 123-456789 M0002名称酷M 987-654321 代码: #包括 #包括 int main(){ 文件*文本; 字符id[6],姓名[101],

我编写了一个程序,从文本文件中每行读取四个变量(三个字符串和一个字符)。但是,当我显示变量时,每行末尾会弹出一个意外字符。(我已经确保变量的长度足够大)

为什么会这样?(还是缓冲区溢出?)

文本文件内容:

M0001 Cool Name F 123-456789 M0002 Name Cool M 987-654321 M0001冷名称F 123-456789 M0002名称酷M 987-654321 代码:

#包括
#包括
int main(){
文件*文本;
字符id[6],姓名[101],性别,联系人[13];
text=fopen(“test.txt”、“r”);
while(fscanf(文本,“%s%[^\n]s%c%s\n”、id、姓名和性别、联系人)!=EOF)
printf(“%s%s%c%s\n”,id、姓名、性别、联系人);
fclose(文本);
返回0;
}
我期望的结果是:

M0001 Cool Name F 123-456789 M0002 Name Cool M 987-654321 M0001冷名称F 123-456789 M0002名称酷M 987-654321 我得到的是:

M0001 Cool Name F 123-456789 1⁄4 M0002 Name Cool M 987-654321 1⁄4 M0001冷名称F 123-456789 1⁄4 M0002名称冷却M 987-654321 1⁄4
%[^\n]s
从这一点开始,会吃掉所有东西,并将其放入
name
中。因此,仅填写
id
name
<代码>性别和
联系人
具有来自程序堆栈的“随机”内容(因为它们未初始化)

意外地,您的堆栈在
gender
+
contact
中有
1/4


在我的计算机上,程序崩溃。

在调用
fscanf()
时,格式字符串“%s%[^\n]s%c%s\n”不正确

  • “[^\n]”将读取到行尾(这将使输入缓冲区溢出:`name'。然后下一个字符不是's',因为下一个字符是换行符
  • 应将返回值与4(而不是EOF)进行比较
  • 输入/格式说明符“%[…]”和“%s”溢出输入缓冲区没有问题,因此应始终具有比输入缓冲区长度小一个的MAX_CHARACTERS修饰符(这些格式说明符始终向输入追加NUL字节)
  • 建议的守则如下:

    #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;
    }
    
  • 干净地编译
  • 记录包含每个头文件的原因
  • 执行所需的功能
  • 将“名称”拆分为“firstname”和“lastname”,以便于处理并匹配输入数据的格式
  • 正确检查来自
    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;
    }
    
    #包括//fopen()、fclose()、fscanf()、perror()、printf()
    #包括//退出(),退出失败
    枚举{
    最大ID长度=6,
    最大名称长度=20,
    最大触点长度=13
    };
    内部主(空)
    {
    字符id[MAX_id_LEN];
    char firstname[MAX_NAME_LEN];
    char lastname[MAX_NAME_LEN];
    性别;
    字符触点[最大触点长度];
    FILE*text=fopen(“test.txt”、“r”);
    如果(!text)
    {
    perror(“fopen读取'test.txt'失败”);
    退出(退出失败);
    }
    //否则,fopen成功了
    而(5==fscanf(文本,“%5s%19s%19s%c%12s”,
    id、名、姓和性别、联系人)
    {
    printf(“%s%s%s%c%s\n”,
    身份证、名、姓、性别、联系人);
    }
    fclose(文本);
    返回0;
    }
    
    由于您姓名中以空格分隔的单词的数量显然是可变的,因此您只能使用
    %[^\n]s
    获取“尽可能多的”–但这也会消耗掉以下所有相关数据。一个快速的解决方案是重新设计输入格式并将名称放在末尾;然后,您的
    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;
    }
    
    #包括
    #包括
    #包括
    #包括
    内部主(空)
    {
    文件*文本;
    字符id[6],姓名[101],性别,联系人[13];
    字符*回望;
    int结果;
    无符号整数行数=0;
    text=fopen(“test.txt”、“r”);
    if(text==NULL)
    {
    printf(“未找到文件!\n”);
    返回退出失败;
    }
    做
    {
    结果=fscanf(文本,“%s%[^\n]s\n”,id,名称);
    行数++;
    如果(结果==EOF)
    打破
    如果(结果!=2)
    {
    printf(“第%u行的数据文件中出现错误(至少需要2项)”,第\u行编号;
    打破
    }
    /*此时,“姓名”还包含“性别”和“联系人”*/
    lookback=strrchr(名称“”);
    如果(回望==NULL | | strlen(回望+1)>12)
    {
    printf(“第%u行数据文件中的错误(应为“联系人”)\n”,第\u行编号);
    打破
    }
    /*lookback+1,因为lookback本身指向空间*/
    strcpy(联系人,回望+1);
    /*向后看时被切断*/
    *回望=0;
    lookback=strrchr(名称“”);
    如果(回望==NULL | | strlen(回望+1)!=1)
    {
    printf(“第%u行数据文件错误(应为“性别”)\n”,第\u行编号);
    打破
    }
    /*现在回头看,指向性别之前的空间*/
    性别=toupper(回顾[1]);
    如果(性别!='F'和性别!='M')
    {
    printf(“第%u行数据文件错误(应为'M'或'F')\n”,第\u行编号);
    打破
    }
    /*回望时再次切断;现在名称已完成*/
    *回望=0;
    printf(“%s%s%c%s\n”,id、姓名、性别、联系人);
    }而(1),;
    fclose(文本);
    返回退出成功;
    }
    
    这种方法确实有几个优点