【问题标题】:C - Loading from file with fgets and sscanfC - 使用 fgets 和 sscanf 从文件加载
【发布时间】:2012-12-06 02:12:15
【问题描述】:

在我的程序的一个函数中,我试图将文件中的数据加载到这个结构数组中:

/* database struct */
typedef struct node {
    char       name[MAX];
    char       address[MAX];
    long int   number;
}record_type;

record_type record[100];

函数如下:

/* load database from disk */
void load_database() {
    char line[128];

    /* Set up database */
    database = fopen("database.txt", "r+w+a+");
    if(database == NULL) {
        printf("\n\tWARNING: No database found.");
        exit(1);
    }

    /* Get database file from disk */
    while(fgets(line, sizeof(line), database) != NULL) {
        sscanf(line, "%s %s %lu", record[rec_num].name,
            record[rec_num].address, &record[rec_num].number);

        /* keeps track of array size */
        rec_num++;
    }
}

我遇到的问题是与 sscanf 不一致。如果我包含名字和姓氏,我不能在它们之间放置空格,否则它将名字放在 name[] 中,将姓氏放在 address[] 中。

这是我尝试输入的数据示例:

1.  Name: james manes       Address: 220 test addr      Number: 5558889999

我需要将“james manes”放入 name[] 字段,将 220 test addr 放入 address[] 字段,将 5558889999 放入结构的 number 字段。这可能吗?

有没有更有效的方法来管理这种类型的输入?

【问题讨论】:

  • 请提供您尝试解析的数据样本。
  • 如果您需要处理输入中的可选空格,并且可能也在地址中,除非您在输入中的字段之间有可靠的分隔符,否则这很棘手。无论哪种方式,使用正则表达式解析器可能会更好(尝试man 3 regex)。
  • @JimStewart 我从未研究过正则表达式。我会调查的。
  • 如果您总是有名字和姓氏,这应该不是问题。如果您有时提供一个名称,有时提供两个名称,这只会有问题。
  • 如果您要对电话号码进行算术运算以外的任何操作,我建议您将它们存储为字符串。这将简化查找正则表达式的过程。

标签: c input struct spaces scanf


【解决方案1】:

scanf("%s"... 会在您的输入中解析一个以空格分隔的字符串,因此如果您要解析的字符串中有空格,它将不起作用。

虽然您可以使用正则表达式来获得所需的内容,但由于您使用固定字符串作为标记,您可以改为使用 strstr 来提取字符串:

while(fgets(line, sizeof(line), database) != NULL) {
    char *Name = strstr(line, "Name:");
    char *Address = strstr(line, "Address:");
    char *Number = strstr(line, "Number:");
    if (Name && Address && Number) {
        Name += strlen("Name:");
        *Address = '\0';
        Address += strlen("Address");
        *Number = '\0';
        Number += strlen("Number:");
        strcpy(record[rec_num].name, Name);
        strcpy(record[rec_num].address, Address);
        sscanf(Number, "%lu", &record[rec_num].number);
        rec_num++; } }

请注意,这也会拉入名称和地址周围的所有空白 - 如果您希望它更清晰,您可以修剪前导和尾随空白。

【讨论】:

  • 我预见到这会给我的好朋友“Jonathon de l'Address: von Number: III”带来问题... :-P
  • 我明天会和我的教授谈谈这个问题并征求他的意见。
【解决方案2】:

首先,您可能想再看看http://www.cplusplus.com/reference/cstdio/fgets/。在这里您将看到 str 参数是指向缓冲区的指针(您正确提供),然后 num 是您要读取的字节数 ma​​x ,您没有正确提供

在您的代码中传递给 fgets 的字节数的问题是 sizeof 运算符的使用不正确。 sizeof 运算符“返回”,您可能知道,给定 type 的大小。您传递给 sizeof 的类型是指针类型(因为 C 中的数组 99% 与指针相同)。指针的大小取决于您运行的系统(Intel x86 上为 32 位,AMD64 上为 64 位,ATmega AVR 上为 16 位等)。因此,假设您有一台 64 位机器,您将允许 fgets “获取” 64 位(即 8 个字节)的数据,这不是您想要的。那么正确的说法是什么?

while(fgets(line, sizeof(*line)*128, database) != NULL) {
    ...

我在这里所做的是将 char 指针 取消引用到一个 char 并乘以这个数组的大小。

然后,其次,您关于这是否可能的问题:是的。我现在也想问自己一个问题。是否有必要在 C 中执行此操作(即平台不支持任何不同的学习目的等),或者您也可以在 C#、Java、Python 中实现它。如果是这样,我强烈建议您这样做。

最后但并非最不重要的是,您向我们询问了您的代码的实用性。这个答案很简单:不。不在当前或固定状态。使用“真实”数据库(如 MySQL)及其 API 解决了您遇到的更复杂的问题。

【讨论】:

  • 这不是为了好玩。这是我大学的 C 和 UNIX 课程。我正在学习 C,我需要帮助我的程序的一小部分。我很感激你的帮助,但我不需要粗体字,听起来你好像在跟我说话。再说一次,我不明白这一点。
  • -1:虽然数组可以在很多地方静默转换为指针,但sizeof 的操作数不是这些地方之一,James 对它的使用是完全正确的。
猜你喜欢
  • 1970-01-01
  • 2019-07-25
  • 2021-11-09
  • 1970-01-01
  • 1970-01-01
  • 2014-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多