为什么 fscanf 读取垃圾?

Why is fscanf read garbage?

提问人:Anil 提问时间:11/23/2021 更新时间:11/24/2021 访问量:131

问:

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define PATH "F:\\c\\projects\\Banking Management System\\data\\"
#define F_ACCT "accounts.txt"
#define FILENAME(file)  PATH file

#define F_ACCT_FPRINTF "%05d%-8s%-30s%d%d%d%-20s%-20s%-20s%c%-15.2lf\n"
#define F_ACCT_FSCANF  "%05d%8s%30[^\n]%d%d%d%20[^\n]%20[^\n]%20[^\n]%c%lf\n"

typedef struct Date
{
    int dd;
    int mm;
    int ccyy;
} Date;

typedef struct Account
{
    int id;
    char acct_no[8];
    char name[30];
    Date birthday;
    char telephone_no[20];
    char mobile_no[20];
    char tfn[20];
    char acct_type;   // 'S' - Saving | 'C' - Current | Fixed - 'F' | Recurring - 'R'
    double acct_bal;
} Account;

int main(int argc, char *argv[])
{

    Account *ac_t=malloc(sizeof(Account));

    if (ac_t==NULL)
    {
        free(ac_t);
        perror("Fatal error: ");
        exit(EXIT_FAILURE);
    }

    FILE *fp=fopen(FILENAME(F_ACCT),"a+"); // Save option selected by the user

    if (!fp)              // NULL=0=true
    {
        free(ac_t);
        perror("ERROR:");
        exit(EXIT_FAILURE);
    }


    (fscanf(fp,F_ACCT_FSCANF,\
            &ac_t->id,\
            ac_t->acct_no,\
            ac_t->name,\
            &ac_t->birthday.dd,\
            &ac_t->birthday.mm,\
            &ac_t->birthday.ccyy,\
            ac_t->telephone_no,\
            ac_t->mobile_no,\
            ac_t->tfn,\
            &ac_t->acct_type,\
            &ac_t->acct_bal));

    printf("\ntmp=%d", tmp);
    printf("\n[%d]",ac_t->id);
    printf("\n[%s]",ac_t->acct_no);
    printf("\n[%s]",ac_t->name);
    printf("\n[%d]",ac_t->birthday.dd);
    printf("\n[%d]",ac_t->birthday.mm);
    printf("\n[%d]",ac_t->birthday.ccyy);
    printf("\n[%s]",ac_t->telephone_no);
    printf("\n[%s]",ac_t->mobile_no);
    printf("\n[%s]",ac_t->tfn);
    printf("\n[%c]",ac_t->acct_type);
    printf("\n[%lf]",ac_t->acct_bal);
    system("pause");

    free(pw_t);

    return 0;

}

=========================================================================================

输入文件 (accounts: .txt) =========================

000011000    Anil Dhar                     27111960(02) 8883 2827      0408 942 407        111222333           S         100.21   

Note: The record was created successfully using frpintf() as per F_ACCT_FPRINTF.

**Problem**
=======

fscanf is reading garbage values like this:

ac_t->id 1
t_acct_no
name Anil Dhar
birthday.dd 27
birthday.mm 11
birthday.ccyy 1960
telephone_no (02) 8883 2827      0408 942 407        111222333           Sogram Files\Intel\ip¬tαK4
mobile_no 0408 942 407        111222333           Sogram Files\Intel\ip¬tαK4
tfn 111222333           Sogram Files\Intel\ip¬tαK4
t_acct_type
acct_bal 74895632819821970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000

我所有的字符串变量,如 name、telephone_no、mobile_no、tfn 都可以包含空格。 该记录没有用任何内容分隔。无论我在哪里读取字符串变量,我的 fscanf() 都没有正确填充字段。

可能出了什么问题????

GCC SCANF 代码块

评论

0赞 Anil 11/23/2021
更正:#define F_ACCT_FSCANF“%05d%8[^\n]%30[^\n]%02d%02d%04d%20[^\n]%20[^\n]%20[^\n]%c%15.2lf\n”
1赞 n. m. could be an AI 11/23/2021
用于读取此类数据是一个可怕的想法,但您直接的问题是字符缓冲区溢出。 只能包含长度为 29 max 的字符串,因为每个字符串都有一个 null 分隔符,但您正在尝试将长度为 30 的字符串塞进去。所有其他字段都相同。fscanfchar name[30];char [...]
0赞 KamilCuk 11/23/2021
%20[^\n]它一直读取到换行符,所以它读取所有内容直到行尾。在您的例子中,字段似乎被空格分隔。输入格式是什么?
0赞 Anil 11/23/2021
输入格式为F_ACCT_FPRINTF
0赞 Anil 11/23/2021
@KamilCuk 输入格式在F_ACCT_PRINTF中定义为预处理器指令。谢谢

答:

0赞 KamilCuk 11/24/2021 #1

$20[^\n]读取所有内容,直到行尾。例如,一直读到一个空格。请注意,除 和 之外的所有说明符都会自动占用并忽略前导空格(制表符、换行符和 spc)。请注意,所有空格都会自动占用并忽略零个或多个空格。请参阅 scanf 文档。无论如何,我也会撒空格以使格式更具可读性。不要添加尾随 。检查 scanf 的返回值,不要将其保留为未选中状态 - 扫描可能会失败。您最多需要读取比缓冲区少一个字符,或者您必须将缓冲区增加一个位置才能终止零字节。%[]%cscanf\n

"%d %7[^ ] %29[^ ] %d %d %d %19[^ ] %19[^ ] %19[^ ] %c %lf"
0赞 Chris Dodd 11/24/2021 #2

%s读取空格分隔的字符串,同时读取下一个换行符。看来你想要一个特定数量的字符,所以你想要.所以你应该有更多类似的东西:%[^\n]%c

#define F_ACCT_FSCANF "%5d%8c%30c%2d%2d%4d%20c%20c%20c%c%lf"

我不确定这是否完全正确,因为您的打印格式有些模棱两可(使用 instead instead 意味着您不知道会得到多少位数字)。如果您的输入文件以任何方式修改了其间距,它也会严重偏离轨道,因此您可能希望使用 fgets+sscanf 而不是 fscanf,因为这至少允许您在任何损坏的行后重新同步。%d%02d

需要注意的一件事 - 将恰好将 8 个字符读入作为参数提供的缓冲区中,其中 NO 终止 NUL - 如果您想要以 NUL 结尾的字符串,则需要手动安排。%8c