如何在不知道每一行中字符串长度的情况下使用fscanf

时间:2017-06-15 08:20:24

标签: c fgets scanf strstr

根据我的理解,有一种解析输入的方法:

  

一百万美元退出$$$ 16岁的Cheit及其惩罚$$$ 8战争和纪念$$$ 12
  战争之风$$$ 12如何玩足球$$$ 12
超短   脉冲$$$ 8
非线性光学$$$ 8
等..

“$$$”在数据字段之间分开 我正在寻求升级这句话:

sscanf(line, " %200[^$][^$][^$]$$$%ld", name, &copies);

所以它适合行号。示例中的1。

修改

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NAME_LENGTH 200
#define ERROR -1
typedef int BOOL;
#define TRUE 1
#define FALSE 0

typedef struct book{
    char name[NAME_LENGTH];
    long copies;
    struct book *next;
} Book;

Book* create_book(char name[], long copies){
    Book *new_book = (Book*) malloc(sizeof(Book));
    if (new_book != NULL) {
        strcpy(new_book->name, name);
        new_book->next = NULL;
        new_book->copies = copies;
    }
    return new_book;
}

Book* add_first(Book *head, char name[], long copies){
    Book *new_book = create_book(name, copies);
    if (new_book == NULL)
        return NULL;
    new_book->next = head;
    return new_book;
}

Book* add_last(Book *head, char name[], long copies){
    Book *tail;
    Book *new_book = create_book(name, copies);
    if (new_book == NULL)
        return NULL;
    if (head == NULL)
        return new_book;
    tail = head;
    while (tail->next != NULL)
        tail = tail->next;
    tail->next = new_book;
    return head;
}

Book* add_sorted(Book *head, char name[], long copies){
    Book* iter, *prev = NULL;
    Book* new_book = create_book(name, copies);
    if(new_book == NULL)
        return head;
    if (head == NULL)
        return new_book;
    if (!strcmp(new_book->name, head->name)){
        new_book->next = head;
        return new_book;
    }
    iter = head;
    while ((iter != NULL) && (strcmp(new_book->name, head->name))){
        prev = iter;
        iter = iter->next;
    }
    prev->next = new_book;
    new_book->next = iter;
    return head;
}

int length(const Book *head){
    if (head == NULL)
        return 0;
    return 1 + length(head->next);
}

void free_library(Book *head_book){
    if (head_book == NULL)
        return;
    free_library(head_book->next);
    free(head_book);
}

Book* find_book(Book *head, char name[]){
    if (head == NULL)
        return NULL;
    if (strcmp(head->name, name) == 0)
        return head;
    find_book(head->next, name);
    return NULL;
}

Book* delete_book(Book *head, char name[]){
    Book *iter = head, *prev = NULL;
    if (head == NULL)
        return head;
    if ((!strcmp(head->name, name)) == 1){
        iter = head->next;
        free(head);
        return iter;
    }
    while (iter->next != NULL){
        if ((!strcmp(head->name, name)) == 1){
            prev->next = iter->next;
            free(iter);
            break;
        }
        prev = iter;
        iter = iter->next;
    }
    return head;
}

Book* initBooksList(FILE *input){
    Book *head_book = NULL, *existing_book = NULL;
    long copies = 0;
    char line[256] = {0}, name[NAME_LENGTH];
    if (input == NULL){
        printf("File did not open. Exit..\n");
        return NULL;
    }
    while(!feof(input)){
        if((fgets(line, 256, input) != NULL) && (head_book == NULL)){
            sscanf(line, " %200[^$][^$][^$]$$$%ld", name, &copies);
            printf("%s\n%ld\n", name, copies);
            head_book = create_book(name, copies);
            strcpy(line, "");
            strcpy(name, "");
            copies = 0;
        }
        else{
            sscanf(line, " %200[^$][^$][^$]$$$%ld", name, &copies);
            existing_book = find_book(head_book, name);
            if(existing_book != NULL){
                existing_book->copies += copies;
                printf("%s\n%ld\n", name, existing_book->copies);
            }
            else{
                add_sorted(head_book, name, copies);
                printf("%s\n%ld\n", name, copies);
                strcpy(line, "");
                strcpy(name, "");
                copies = 0;
            }
        }
    }
    return head_book;
}

void storeBooks(Book *head_book){

}

void returnBook(Book *head_book){

}

void borrowBook(Book *head_book){

}

int main(int argc, char *argv[]){
    int i = 0;
    FILE *ptr;
    printf("%d\n", argc);
    for(i = 0; i < argc; i++)
        printf("argv[%d] = %s\n", i, argv[i]);
    ptr = fopen(argv[1], "r");
    initBooksList(ptr);
    return 0;
}

2 个答案:

答案 0 :(得分:0)

这应该让你知道你可以做什么:

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

int main()
{
  char line[] = "The Cheit and its Punishment $$$ 8";

  char *seppointer = strchr(line, '$');
  *seppointer = 0;    
  int price = atoi(seppointer + 4);

  printf("Title: %s\nPrice: %d\n", line, price);
}

免责声明:没有错误检查,并且假设该行具有所需的格式。

答案 1 :(得分:0)

如果你知道最长的标题是200个字符,如your comment所示,你可以为此分配一个数组(包括空终止符的空格)。

您可以使用fscanf()使用格式字符串" %200[^$]$$$%d"来解析文件的行。第一个空格告诉fscanf()跳过前导空格,这些空格可能会从之前的I / O操作中遗留下来。下一个转换说明符是%200[^$],它告诉fscanf()将任何字符读入字符串,直到遇到$为止。 $保留在输入流中。请注意,此处指定的最大宽度为200,以防止缓冲区溢出。格式字符串中的后三个字符$$$必须存在于输入中,并且在到达最终转换说明符%d之前匹配。

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

#define MAX_TITLE  201

int main(void)
{
    /* Open file, and check for success */
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    char title[MAX_TITLE];
    int price;

    while (fscanf(fp, " %200[^$]$$$%d", title, &price) == 2) {
        printf("Title: %s --- Price: $%d\n", title, price);
    }

    fclose(fp);

    return 0;
}

以下是针对输入文件运行时的程序输出:

Title: The Cheit and its Punishment  --- Price: $8
Title: War and Remembrance  --- Price: $12
Title: Winds of War  --- Price: $12
Title: How to Play Football  --- Price: $12
Title: Ultrashort Pulses  --- Price: $8
Title: Nonlinear Optics  --- Price: $8

上述代码中对fscanf()的调用会在输入流中每行的最后一个数字后面留下空白字符;这就是为什么需要格式字符串中的前导空格的原因。更好的解决方案是使用fgets()来获取输入行,使用sscanf()来解析行。应该分配buffer来保存每行的内容;这里的慷慨分配是好的,因为它减少了长输入在输入流中留下字符的可能性。如果输入可能较长,则应在下次调用fgets()之前添加代码以清除输入流。

这种方法的一个优点是,由于读取的整行包括\n,因此不需要像以前一样跳过前导空白字符。另一个优点是最终数字之后的虚假字符可以被忽略,或者由代码处理;由于存储了该线,因此可以根据需要多次检查和扫描。最终编号后面的字符会导致第一个版本出现问题,第一个版本只能跳过前导空格。

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

#define BUF_SZ     1000
#define MAX_TITLE  201

int main(void)
{
    /* Open file, and check for success */
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    char buffer[BUF_SZ];
    char title[MAX_TITLE];
    int price;
    size_t lnum = 0;

    while (fgets(buffer, BUF_SZ, fp) != NULL) {
        ++lnum;
        if (sscanf(buffer, "%200[^$]$$$%d", title, &price) == 2) {
            printf("Title: %s --- Price: $%d\n", title, price);
        } else {
            fprintf(stderr, "Format error in line %zu\n", lnum);
        }
    }

    fclose(fp);

    return 0;
}

此处使用fgets()可以更灵活地检查输入。要处理$是标题的一部分的情况,您可以使用strstr()首先找到分隔符" $$$",然后将字符复制到分隔符到title[]数组中在一个循环中。由于strstr()返回指向找到的字符串的指针,因此可以将此指针指定给sscanf()以选择最终的数字。如果找不到该字符串,strstr()函数将返回空指针,这可用于标识具有格式问题的行。请注意strstr()位于string.h

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

#define BUF_SZ     1000
#define MAX_TITLE  201

int main(void)
{
    /* Open file, and check for success */
    FILE *fp = fopen("data.txt", "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    char buffer[BUF_SZ];
    char title[MAX_TITLE];
    int copies;
    size_t lnum = 0;

    while (fgets(buffer, BUF_SZ, fp) != NULL) {
        ++lnum;

        /* Find delimiter string in buffer */
        char *title_end = strstr(buffer, " $$$");
        if (title_end == NULL) {
            fprintf(stderr, "Format error in line %zu\n", lnum);
            continue;
        } else {

            /* Copy characters into title until space before delimiter */
            char *curr = buffer;
            size_t i = 0;
            while (curr < title_end && i < MAX_TITLE) {
                title[i] = buffer[i];
                ++curr;
                ++i;
            }
            title[i] = '\0';
        }

        if (sscanf(title_end, " $$$%d", &copies) == 1) {
            printf("Title: %s --- Copies: %d\n", title, copies);
        } else {
            fprintf(stderr, "Format error in line %zu\n", lnum);
        }
    }

    fclose(fp);

    return 0;

}

以下是修改后的输入文件:

The Cheit and its Punishment $$$ 8
War and Remembrance $$$ 12
Winds of War $$$ 12
A million $ exit $$$ 16
How to Play Football $$$ 12
Ultrashort Pulses $$$ 8
Nonlinear Optics $$$ 8

以及结果输出:

Title: The Cheit and its Punishment --- Copies: 8
Title: War and Remembrance --- Copies: 12
Title: Winds of War --- Copies: 12
Title: A million $ exit --- Copies: 16
Title: How to Play Football --- Copies: 12
Title: Ultrashort Pulses --- Copies: 8
Title: Nonlinear Optics --- Copies: 8