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



我目前正在尝试使用以下格式解析UnicodeData.txt: ftp://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()之外,有没有办法让它工作?

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

以防万一您想考虑以下替代方案,使用 scanf s 和 "%n" 格式说明符,用于读取到目前为止已读取的字符数,成一个整数:

#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: %sn", i, fields[i] );
    getchar( );
    return 0;
}

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

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

printfsscanf 的返回值,结果的打印仅用于演示目的。您可以看到代码在没有getchar();的情况下 http://codepad.org/kae8smPF 工作,并且for声明已移出以符合 C90。

我认为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 = ''), end++ : strtok_r(end, ";", &end);
    char *s2 = *end == ';' ? (*end = ''), end++ : strtok_r(end, ";", &end);
    char *s3 = *end == ';' ? (*end = ''), end++ : strtok_r(end, ";", &end);
    char *s4 = *end == ';' ? (*end = ''), end++ : strtok_r(end, ";", &end);
    printf("[%s][%s][%s][%s]n", s4, s3, s2, s1);
  }
}

相关内容

  • 没有找到相关文章

最新更新