如何让scanf继续使用空扫描集

时间:2014-04-09 21:50:11

标签: c unicode scanf

我目前正在尝试使用以下格式解析UnicodeData.txtftp://ftp.unicode.org/Public/3.0-Update/UnicodeData-3.0.0.html但是,当我尝试阅读时,我遇到了一个问题,请说出如下所示的行。

something;123D;;LINE TABULATION;

我尝试通过以下代码从字段中获取数据。问题是字段[3]没有填写,scanf返回2. in是当前行。

char fields[4][256];
sscanf(in, "%[^;];%[^;];%[^;];%[^;];%[^;];",
    fields[0], fields[1], fields[2], fields[3]);

我知道这是scanf()的正确实现,但是有没有办法让它工作,而不是自己创建scanf()

3 个答案:

答案 0 :(得分:2)

如果您想考虑以下替代方案,请使用scanf"%n"格式说明符(用于读取到目前为止已读取的字符数)为整数:< / p>

#include <stdio.h>
#define N 4

int main( ){

    char * str = "something;123D;;LINE TABULATION;";
    char * wanderer = str;
    char fields[N][256] = { 0 };
    int n;

    for ( int i = 0; i < N; i++ ) {
        n = 0;
        printf( "%d ", sscanf( wanderer, "%255[^;]%n", fields[i], &n ) );
        wanderer += n + 1;
    }

    putchar( 10 );

    for ( int i = 0; i < N; i++ )
        printf( "%d: %s\n", i, fields[i] );

    getchar( );
    return 0;
}

在每个循环中,它会将最多255个字符读入相应的fields[i],直到遇到分隔符分号;。读完之后,它会读取它已经阅读了多少个字符,进入n,之前已经归零(哦,我的......)。

它增加了指向字符串的指针,读取的字符数加上分隔符分号的一个。

printf表示sscanf的返回值,打印结果仅用于演示目的。您可以看到代码在http://codepad.org/kae8smPF上工作,而getchar();for声明已移到外面以符合C90标准。

答案 1 :(得分:2)

scanf无法处理“空”字段。所以你必须自己解析它。

以下解决方案是:

  • 快,因为它使用的是strchr而非慢sscanf
  • 灵活,因为它会检测到任意数量的字段,直到给定的最大值。

函数parse从输入str中提取以分号分隔的字段。四个分号给出五个区域,其中一些或全部可以是空白的。没有规定逃离分号。

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

static int parse(char *str, char *out[], int max_num) {
    int num = 0;
    out[num++] = str;
    while (num < max_num && str && (str = strchr(str, ';'))) {
        *str = 0;           // nul-terminate previous field
        out[num++] = ++str; // save start of next field
    }
    return num;
}

int main(void) {
    char test[] = "something;123D;;LINE TABULATION;";
    char *field[99];
    int num = parse(test, field, 99);
    int i;
    for (i = 0; i < num; i++)
        printf("[%s]", field[i]);
    printf("\n");
    return 0;
}

该测试程序的输出是:

[something][123D][][LINE TABULATION][]

更新:一个稍短的版本,不需要额外的数组来存储每个子字符串的开头,是:

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

static int replaceSemicolonsWithNuls(char *p) {
    int num = 0;
    while ((p = strchr(p, ';'))) {
        *p++ = 0;
        num++; 
    }
    return num;
}

int main(void) {
    char test[] = "something;123D;;LINE TABULATION;";
    int num = replaceSemicolonsWithNuls(test);
    int i;
    char *p = test;
    for (i = 0; i < num; i++, p += strlen(p) + 1)
        printf("[%s]", p);
    printf("\n");
    return 0;
}

答案 2 :(得分:1)

我认为sscanf无法满足您的需求:sscanf格式%[^;]将匹配非空 的序列 - 分号字符。另一种方法是使用readline,分隔符为';',如:

#include <iostream>
#include <sstream>
#include <string>

int main() {
  using namespace std;
  istringstream i { "something;123D;;LINE TABULATION;\nsomething;123D;;LINE TABULATION;\nsomething;123D;;LINE TABULATION;\n" };
  string a, b, c, d, newline;
  while( getline(i, a, ';') && getline(i, b, ';') && getline(i, c, ';') && getline (i, d, ';') && getline(i, newline) )
    cout << d << ',' << c << '-' << b << ':' << a << endl; 
}

(我现在只看到你从c++标记中删除了这个问题,如果你的问题只有c,我还有另一个解决方案,如下:)

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

int main() {
  typedef char buffer[2048];
  buffer line;
  while( fgets(line, sizeof(line), stdin) > 0 ) {
    printf("(%s)\n", line);
    char *end = line;
    char *s1 = *end == ';' ? (*end = '\0'), end++ : strtok_r(end, ";", &end);
    char *s2 = *end == ';' ? (*end = '\0'), end++ : strtok_r(end, ";", &end);
    char *s3 = *end == ';' ? (*end = '\0'), end++ : strtok_r(end, ";", &end);
    char *s4 = *end == ';' ? (*end = '\0'), end++ : strtok_r(end, ";", &end);
    printf("[%s][%s][%s][%s]\n", s4, s3, s2, s1);
  }
}