我目前正在尝试使用以下格式解析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
,事先已经归零(哦,天哪......(。
它按读取的字符数增加指向字符串的指针,加上分隔符分号的字符数。
printf
为 sscanf
的返回值,结果的打印仅用于演示目的。您可以看到代码在没有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);
}
}