C中文件输入的模式识别

Pattern Recognition for File input in C

我试图使用 scanf 从 C 语言的文件中获取输入。文件中的数据如下:

223234       <Justin>      <Riverside>  

这是我试过的以下正则表达式:

FILE* fid;


    int id;
    char name[100], city[100];
    char dontcare1[40], dontcare3[40];
    char dontcare2,dontcare4[40],dontcare5;


    fid = fopen("test.txt", "r");

    fscanf(fid,"%d%[^<]%c%[^<]%c%[>]%c ",&id,&dontcare1[0],
                                            &dontcare2,&dontcare3[0],&dontcare4[0],
                                            &city[0],&dontcare5);

我想知道是否有更好的方法来做到这一点,我将如何在不创建额外变量的情况下解释文件中的空格,这似乎无法获取括号中的城市名称。

*scanf() 中,您可以使用文字字符,一个 space 可以匹配多个分隔符。

为了避免处理文件,我的示例使用 sscanf() 进行了简化,但它与 fscanf() 的工作方式相同。

这里的技巧是使用 %n 来获取到该点为止的读取字符数;这样,我们确保最后一个 > 字面量确实已被读取 (我们无法通过 *scanf() 的结果得知)

/**
  gcc -std=c99 -o prog_c prog_c.c \
      -pedantic -Wall -Wextra -Wconversion \
      -Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <stdio.h>

int
main(void)
{
  const char *line="223234       <Justin>      <Riverside>";
  int id;
  char name[100], city[100];

  int n_read=-1;
  sscanf(line, "%d <%[^>]> <%[^>]>%n",
         &id, name, city, &n_read);
  if(n_read!=-1) // read till the end then updated
  {
    printf("id=%d\n", id);
    printf("name=%s\n", name);
    printf("city=%s\n", city);
  }
  return 0;
}

尝试打开文件时,确保文件实际打开成功很有用。

FILE *fid;
fid = fopen("path/to/file", "r");

if (fid == NULL){
    printf("Unable to open file. \n");
    return -1;
}

实际上解决你的问题,我可能只是使用 string.hstrtok 函数,然后使用 space 作为分隔符。

此外,我不会使用 scanf,而是 fgets... 可以在各种其他 SO 文章中找到其原因。以下是未经测试的解决方案。

        char line[100], line_parse[100]; // Buffer(s) to store lines upto 100
        char *ret; // token used for strtok

        // Read an integer and store read status in success.
        while (fgets(line, sizeof(line), fPtrIn) != NULL)
        {
            // Copy the line for parsing, as strtok changes original string
            strcpy(line_parse, line);

            // Separate the line into tokens
            ret = strtok(line_parse, " ");
            
            while (ret != NULL)
            {/*do something with current field*/
                ret = strtok(NULL, " "); // Move onto next field
            }

请注意 strtok 不是 线程安全的 。因此,在多线程代码中,您不应使用此函数。不幸的是,ISO C 标准本身不提供该函数的线程安全版本。但是许多平台都提供了这样一个功能作为扩展:在 POSIX 兼容的平台上(例如 Linux),您可以使用 strtok_r. On Microsoft Windows, you can use the function strtok_s 功能。这两个函数都是线程安全的。

如果非要用scanf(),其他的回答似乎面面俱到。这是一种替代方法,使用 fgetc()strcpy().

逐字符输入
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAX_SIZE 100

int main(void)
{
    int id = 0, c = 0;
    char buff1[MAX_SIZE], buff2[MAX_SIZE];
    size_t i = 0U;
    FILE *fptr = NULL;

     if (!(fptr = fopen("test.txt", "r")))
     {
        perror("error opening file");
        return -1;
     }

    while ((c = fgetc(fptr)) != EOF)
    {
        if (isdigit(c)) /* maybe check INT_MAX here if you are planning to scan big numbers */
        {
            id = (id * 10) + (c - '0');
        }

        if (i != 0 && c == ' ')
        {
            buff2[i] = '[=10=]';
            strcpy(buff1, buff2);
            i = 0U;
        }

        if (isalpha(c))
        {
            if (i < MAX_SIZE - 1)
            {
                buff2[i++] = c;
            }
            else
            {
                fputs("Buff full", stderr);
                return -1;
            }
        }
    }
    buff2[i] = '[=10=]';

    return 0;
}

您实际上可以非常简单地通过将行读入数组(缓冲区)然后从带有 sscanf() 的行中解析您需要的内容来完成此操作。不要直接使用 scanf(),因为这会让您面临一系列与输入流中未读字符相关的陷阱。相反,通过一次读取一行来完成所有输入,然后使用 sscanf() 解析缓冲区中的值,就像使用 scanf() 一样,但是通过使用 fgets() 来读取,您一次消耗一整行,输入流中剩余的内容不取决于转换的成功或失败。

例如,您可以这样做:

#include <stdio.h>

#define MAXC 1024
#define NCMAX 100

int main (void) {
    
    char buf[MAXC],
        name[NCMAX],
        city[NCMAX];
    unsigned n;
    
    if (!fgets (buf, MAXC, stdin))
        return 1;
    
    if (sscanf (buf, "%u <%99[^>]> <%99[^>]>", &n, name, city) != 3) {
        fputs ("error: invalid format", stderr);
        return 1;
    }
    
    printf ("no.  : %u\nname : %s\ncity : %s\n", n, name, city);
}

sscanf() 格式字符串是关键。 "%u <%99[^>]> <%99[^>]>" 将数字作为无符号值读取,<%99[^>]> 消耗 '<' 然后字符 class %99[^>] 使用 field-width 99 的修饰符来保护你的数组边界,class [^>] 将读取所有不包括 > 的字符(它对 city 下一个)。通过 检查Return[=44,验证 =] 以确保发生三个有效转换。如果不是,则处理错误。

例子Use/Output

根据您在文件 dat/no_name_place.txt 中的输入,该文件被简单地重定向到 stdin 并由程序读取,结果为:

$ ./bin/no_name_city < dat/no_name_place.txt
no.  : 223234
name : Justin
city : Riverside