将连续的标签页读取为空白字段fscanf

时间:2018-11-27 06:41:30

标签: c

我有一个文件,其中某些字段由制表符分隔。一行中总是会有12个标签,某些标签是连续的,表示一个空字段。我想使用fscanf将连续的选项卡读取为空字段并将其存储在结构中。但是似乎有一个问题。这是我的文件:

   usrid    User Id 0   15  string  d   k   y   y           0   0

当我尝试使用fscanf进行读取时,制表符之间的制表符不能识别为空字段,并且数据存储在错误的结构字段中。解决问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

fscanf不是入门者。读取空字段的唯一方法是使用"%c"读取定界符(这将要求您事先知道哪些字段为空-不太有用),否则,取决于 format说明符时,fscanf会简单地将tabs用作前导空白或遇到匹配失败输入失败

从注释继续,为了基于可能分隔空字段的分隔符进行标记化,您将需要使用strsep,因为strtok会将连续的分隔符视为一个。

虽然您的字符串不太清楚tabs的位置,但是使用strsep标记的简短示例如下。请注意,strsep将指针指向指针的第一个参数,例如

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

int main (void) {

    int n = 0;
    const char *delim = "\t\n";
    char *s = strdup ("usrid\tUser Id 0\t15\tstring\td\tk\ty\ty\t\t\t0\t0"),
        *toks = s,   /* tokenize with separate pointer to preserve s */
        *p;

    while ((p = strsep (&toks, delim)))
        printf ("token[%2d]: '%s'\n", n++ + 1, p);

    free (s);
}

注意:,由于strsep将修改字符串指针所保存的地址,因此您需要保留指向s开头的指针,以便在出现以下情况时可以将其释放不再需要-感谢JL)

使用/输出示例

$ ./bin/strtok_tab
token[ 1]: 'usrid'
token[ 2]: 'User Id 0'
token[ 3]: '15'
token[ 4]: 'string'
token[ 5]: 'd'
token[ 6]: 'k'
token[ 7]: 'y'
token[ 8]: 'y'
token[ 9]: ''
token[10]: ''
token[11]: '0'
token[12]: '0'

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:0)

  

我想使用fscanf将连续的选项卡读取为空字段并将其存储在结构中。

理想情况下,代码应该像fgets()一样读取,然后解析 string

还留在fscanf()上,可以循环执行。


主要思想是使用"%[^/t/n]"读取一个令牌。如果下一个字符是'\t',则返回值将不是1。为此进行测试。宽度限制是明智的。

然后阅读分隔符并查找制表符,行尾或是否发生文件结束/错误。

#define TABS_PER_LINE 12
#define TOKENS_PER_LINE (TABS_PER_LINE + 1)
#define TOKEN_SIZE 100
#define TOKEN_FMT_N "99"

int fread_tab_delimited_line(FILE *istream, int n, char token[n][TOKEN_SIZE]) {
  for (int i = 0; i < n; i++) {
    int token_count = fscanf(istream, "%" TOKEN_FMT_N "[^\t\n]", token[i]);
    if (token_count != 1) {
      token[i][0] = '\0';  // Empty token
    }
    char separator;
    int term_count = fscanf(istream, "%c", &separator);  // fgetc() makes more sense here
    // if end-of-file or end-of-line
    if (term_count != 1 || separator == '\n') {
      if (i == 0 && token_count != 1 && term_count != 1) {
        return 0;
      }
      return i + 1;
    }
    if (separator != '\t') {
      return -1;  // Token too long
    }
  }
  return -1;  // Token too many tokens found
}

示例驾驶代码

void test_tab_delimited_line(FILE *istream) {
  char token[TOKENS_PER_LINE][TOKEN_SIZE];
  long long line_count = 0;
  int token_count;
  while ((token_count = fread_tab_delimited_line(istream, TOKENS_PER_LINE, token)) > 0) {
    printf("Line %lld\n", ++line_count);
    for (int i = 0; i < token_count; i++) {
      printf("%d: <%s>\n", i, token[i]);
    }
  } while (token_count > 0);
  if (token_count < 0) {
    puts("Trouble reading any tokens.");
  }
}