【问题标题】:Why is fscanf read garbage?为什么 fscanf 读取垃圾?
【发布时间】:2022-01-01 15:55:23
【问题描述】:
#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() 都没有正确填充字段。

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

【问题讨论】:

  • 更正:#define F_ACCT_FSCANF "%05d%8[^\n]%30[^\n]%02d%02d%04d%20[^\n]%20[^\n] %20[^\n]%c%15.2lf\n"
  • 使用fscanf 读取这种数据是一个可怕的想法,但你的直接问题是字符缓冲区溢出。 char name[30]; 只能包含一个最大长度为 29 的字符串,因为每个字符串都有一个空分隔符,但您试图在其中填充一个长度为 30 的字符串。与所有其他 char [...] 字段相同。
  • %20[^\n] 一直读到换行符,所以它读到 everything 直到行尾。在你的情况下,看起来字段是用空格分隔的。输入格式是什么?
  • 输入格式为 F_ACCT_FPRINTF
  • @KamilCuk 输入格式在 F_ACCT_PRINTF 中定义为预处理器指令。谢谢

标签: gcc scanf codeblocks


【解决方案1】:

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

"%d %7[^ ] %29[^ ] %d %d %d %19[^ ] %19[^ ] %19[^ ] %c %lf"

【讨论】:

    【解决方案2】:

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

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

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

    需要注意的一件事——%8c 会将 8 个字符准确读入作为参数提供的缓冲区中,并且没有终止 NUL——如果你想要以 NUL 终止的字符串,你需要手动安排。

    【讨论】:

      猜你喜欢
      • 2016-03-02
      • 2022-01-14
      • 2021-12-15
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-04
      相关资源
      最近更新 更多