sscanf从输入文件读取多个字符串的问题

问题描述 投票:1回答:2

所以我正在慢慢地继续学习C。现在,我有一个任务,要从文件中读取数据并对其进行排序。

文件数据:

House naming 1 30 300
House naming 2 45 450
.......
House naming 10 5 120

因此,第一个值:House naming,可以是任何名称,例如Empire state building

第二个值是:住所地址(我仅选择了integer个值)

第三个值是:建筑物的年龄

第四个值是:千瓦时/年

程序必须从文件中获取数据->打印出来->排序(如何?见下文)->再次打印出来,进行排序。

排序:

  • kwh <200-可持续建筑,
  • 千瓦时<300 &&年龄<40-需要翻新,
  • kwh> 300 && age> 40-待拆除。

这里是代码:

#include <stdio.h>
#include <stdlib.h>
#include "input.h"

int main(void) {
    int kwh;
    int age;
    char building[SIZE];
    int addr;
    char buff[SIZE];
    FILE *fi;

    // opening the files and checking if it succeeded
    fi = fopen(F_INPUT, "r");
    if (fi == NULL) {
        printf("Error opening input file \"%s\"", F_INPUT);
        exit(EXIT_INPUT_FAIL);
    }
    while (fgets(buff, sizeof(buff), fi) != NULL) {
        sscanf(buff, "%s %d %d %d", building, &addr,&age,&kwh);
        if (kwh < 200) {
            puts(buff);
            printf("Sustainable\n");
        } else
        if (kwh < 300 && age < 40) {
            puts(buff);
            printf("Needs renovation\n");
        } else
        if (kwh > 300 && age > 40) {
            puts(buff);
            printf("IN DEMOLITION LIST\n");
        }
    }
    /* close the files when they're not needed anymore */
    fclose(fi);
    return 0;
}

我已经结合了一些步骤,使其变得更容易一些,读取数据->输出已经标记为1)可持续,2)需要翻新,3)准备拆除。

问题出在while循环中的某处,我认为它在sscanf函数中。按照我的逻辑,如果我没有记错的话,它必须使用逻辑(请看sscanf和输入文件)从文件中读取字符串:char valueintegerintegerinteger。程序读取文件,输出数据,但将所有建筑物标记为sustainable

您建议更仔细阅读的内容或为读取多个字符串而更好地选择的逻辑。

输出:

House naming 1 30 300
Sustainable
House naming 2 45 450
Sustainable
........
House naming 10 5 120
Sustainable
c file input scanf puts
2个回答
2
投票

[OP]完成后,通过fgets()从文件中将line读取到string是很好的第一步。

“房屋名称”可以包含数字吗?像“ Stdio 54”?是的,它可以包含数字,不能包含数字。如果我是对的,那么任务对命名没有任何帮助。

下一部分很棘手,因为房屋名称与后面的3个整数之间没有唯一的分隔符。

一种方法是找到3个尾随的整数,然后将其余的开始文本作为房屋名称

  while (fgets(buff, sizeof(buff), fi) != NULL) {
    int address, age, power;
    char *p = buff + strlen(buff);  // start as buff end
    p = reverse_scan_int(buff, p, &power);
    p = reverse_scan_int(buff, p, &age);
    p = reverse_scan_int(buff, p, &address);
    if (p) {
      *p = '\0';
      trim(buff);  // remove leading trailing white-space
      printf("house_name:'%s', address:%d age:%d power:%d\n", buff, address,
          age, power);
    } else {
      printf("Failed to parse '%s'\n", buff);
    }
  }

现在我们只需要reverse_scan_int()。示例未经测试的代码提示:

#include <ctype.h>
#include <stdbool.h>
char *reverse_scan_int(const char *begin, char *one_past, int *i) {
  if (one_past == NULL) {
    return NULL;
  }
  // is...() functions work best with unsigned char
  unsigned char *p = (unsigned char *) one_past;
  // Skip trailing whitespace;
  while (p > begin && isspace(p[-1])) {
    p--;
  }
  // Skip digits;
  bool digit_found = false;
  while (p > begin && isdigit(p[-1])) {
    p--;
    digit_found = true;
  }
  if (!digit_found)
    return NULL;
  if (p > begin && (p[-1] == '-' || p[-1] == '+')) {
    p--;
  }
  *i = atoi(p); // More roubust code would use `strtol()`, leave for OP.
  return (char *) p;
}

[trim a string包括this one的方法很多。


2
投票

您的问题很难用sscanf()解决,因为房屋名称和3个数字字段之间没有明确的分隔符。 %s是不合适的:它解析一个单词。在您的程序中,sscanf()实际上无法转换数字并为所有行返回1,从而在您比较实际未初始化的数值时导致未定义的行为。

这里是使用%[转换规范的修改版本:

#include <stdio.h>
#include <stdlib.h>

#define F_INPUT  "input.txt"
#define EXIT_INPUT_FAIL  1

int main(void) {
    char buff[256];
    char building[100];
    int addr, age, kwh;
    FILE *fi;

    // opening the files and checking if it succeeded
    fi = fopen(F_INPUT, "r");
    if (fi == NULL) {
        printf("Error opening input file \"%s\"", F_INPUT);
        exit(EXIT_INPUT_FAIL);
    }
    while (fgets(buff, sizeof(buff), fi) != NULL) {
        /* parse the building name upto and excluding any digit,
           then accept 3 integral numbers for the address, age and power */
        if (sscanf(buff, "%99[^0-9]%d%d%d", building, &addr, &age, &kwh) != 4) {
            printf("parsing error: %s", buff);
            continue;
        }
        if (kwh < 200) {
            puts(buff);
            printf("Sustainable\n");
        } else
        if (kwh < 300 && age < 40) {
            puts(buff);
            printf("Needs renovation\n");
        } else
        if (kwh > 300 && age > 40) {
            puts(buff);
            printf("IN DEMOLITION LIST\n");
        }
        // allocate structure with building details and append it to the list or array of buildings
    }
    /* close the files when they're not needed anymore */
    fclose(fi);
    // sort the list or array and print it
    // free the list or array
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.