使用Fscanf在C语言中读取混合的浮点数和字符

3

我有一个 .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 |

我想把存储在三个变量中的信息,城市、描述和价格,但无法将它们保存到任何行。

   #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;

1
%s 只能读取一个单词。你不能用它来读取整个描述。 - Barmar
1
你还会遇到一个问题,那就是城市名称由多个单词组成,例如“纽约”。 - Barmar
我明白了,看来我的方法不对。 - Rowry
2
使用fgetsstrtok函数来将每一行以分隔符字符串"*|"进行分割。然后使用strdup函数复制这两个文本字符串,并使用sscanf函数从第三个标记中提取费用。 - Weather Vane
scanf系列函数中的函数,读取字符串直到遇到空格(空格、制表符、换行符)。 - Aleksandar Makragić
显示剩余2条评论
2个回答

3

我认为使用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|

我不理解的是分隔符“|*”,它是如何工作的? - Rowry
我建议您阅读strtok man page。它是一组字符,任何一个字符都可以以任何组合方式打破字符串。 - Weather Vane
是的,我明白了,但是它不会在前两个“**”上断开字符串吗?如果格式是:->巴黎->航班,5天,参观各种纪念碑。¥2999.99 ¥ 这里该使用什么限定符?(我猜是“->¥”) - Rowry
你需要“转义”双斜杠'\'。请注意,分隔符字符串不是一个序列,而是一组字符,因此文本中的任何单个'-'都会破坏它。 - Weather Vane

1

使用单个fscanf语句非常难以做到,因为正如@Barmar所指出的那样,城市名称可能超过一个词。更容易的方法是读取整行(使用fgets),然后自己解析该行。解析方法如下:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接