【问题标题】:Fscanf to read a mixture of float and characters in cFscanf 在 c 中读取浮点数和字符的混合
【发布时间】:2016-11-17 16:59:34
【问题描述】:

所以我有一个 .txt 文件,如下所示:

** Paris ** Flight,5 days,visiting various monuments. | 2999.99 |
** Amsterdam ** By bus,7 days, local art gallery. | 999.99 |
** London ** Flight,3 days,lots of free time. | 1499.99 |

我想将信息存储到 3 个变量中,城市、描述和价格,但我根本无法保存行。

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

int main()
{
    FILE *fp;
    char city[256],desciption[256];
    float temp;
    if(fp=fopen("Ponuda.txt","rt")==NULL){
        printf("ERROR\n");
        exit(1);
    }

    while(fscanf(fp,"** %s ** %s | %f |",city,description,&temp)==3){

        printf("** %s ** %s |%f|\n",city,description,temp);
    }
    return 0;

【问题讨论】:

  • %s 只读取 1 个单词。你不能用它来阅读整个描述。
  • 您还会遇到超过 1 个字的城市的问题,例如 New York
  • 我明白了,看来我当时的方法是错误的。
  • 使用fgets 后跟strtok 用分隔符字符串"*|" 分割每一行。然后用strdup复制两个文本字符串,sscanf从第三个token中提取票价。
  • c 如何看到单词的结尾?难道我不能以某种方式使整个描述是一个词,并且阅读停止在字符'*'处吗?

标签: c file scanf


【解决方案1】:

IMO 使用fgets 读取每个文件行要容易得多,然后使用strtok 用分隔符字符串"*|" 分割每一行。

然后我使用strdup 将文本字符串复制到结构中,并使用sscanf 从第三个标记中提取票价。我应该测试来自strdup 的返回值,因为它在内部调用malloc

我也使用double 而不是float(我只会在有限制的情况下使用)。

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

#define MAX  10

typedef struct {
    char *city;
    char *descrip;
    double fare;
} flight_t;

int main()
{
    FILE *fp;
    flight_t visit[MAX] = {0};
    char buffer [1024];
    char *tok;
    int records = 0, i;
    if((fp = fopen("Ponuda.txt", "rt")) == NULL) {
        printf("Error opening file\n");
        exit(1);
    }

    while(fgets(buffer, sizeof buffer, fp) != NULL) {
        if((tok = strtok(buffer, "|*")) == NULL) {
            break;
        }
        if(records >= MAX) {
            printf("Too many records\n");
            exit(1);
        }
        visit[records].city = strdup(tok);          // add NULL error checking

        if((tok = strtok(NULL, "|*")) == NULL) {    // pass NULL this time
            break;
        }
        visit[records].descrip = strdup(tok);       // add NULL error checking

        if((tok = strtok(NULL, "|*")) == NULL) {    // pass NULL this time
            break;
        }
        if(sscanf(tok, "%lf", &visit[records].fare) != 1) {     // read a double
            break;
        }
        records++;
    }

    fclose(fp);

    // print the records
    for(i = 0; i < records; i++) {
        printf("** %s ** %s |%.2f|\n", visit[i].city, visit[i].descrip, visit[i].fare);
    }

    // free the memory given by strdup
    for(i = 0; i < records; i++) {
        free(visit[i].city);
        free(visit[i].descrip);
    }
    return 0;
}

程序输出:

**  Paris  **  Flight,5 days,visiting various monuments.  |2999.990000|
**  Amsterdam  **  By bus,7 days, local art gallery.  |999.990000|
**  London  **  Flight,3 days,lots of free time.  |1499.990000|

【讨论】:

  • 我不明白的一件事是分隔符“|*”,它是如何工作的?
  • 我建议你阅读strtok man page。它是一个 set 字符,任何组合都会破坏字符串。
  • 是的,我理解,但它不会破坏前两个“**”上的字符串吗?如果格式是: -> 巴黎 -> 飞行,5 天,参观各种古迹,怎么样。 \$ 2999.99 \$ 我将在这里使用什么作为限制器? (我的猜测是“->\$”)
  • 您需要“转义”“\\”。 (他们两个人)。请注意,分隔符字符串不是序列,它是一组字符,因此文本中的任何单个- 都会破坏它。
【解决方案2】:

使用单个 fscanf 语句很难做到这一点,因为正如@Barmar 指出的那样,城市名称可能不止一个词。阅读整行(使用 fgets)然后自己解析行要容易得多。解析它:

  1. 查找“**”后的第一个非空白字符
  2. 查找之后的最后一个非空白字符,即“**”之前的字符
  3. 这些索引之间的文本将是城市名称(可能包含空格)
  4. 描述是在步骤 2 中找到的“**”和下一个“|”的索引之间的文本之后。
  5. 成本是紧跟在“|”之后的字符串

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 2013-09-18
    相关资源
    最近更新 更多