在C中用sscanf忽略空格

时间:2011-03-13 22:14:34

标签: c whitespace scanf

我有一个字符串,例如“4 Tom Tim 6”,我试图用sscanf扫描这些值,就像这样 sscanf(字符串,“%d%s%d”,& NUMBER1,NAME,& number2)

有没有办法做到这一点并在NUMBER1中存入值4,在NUMBER2中存储值6,在NAME中存储值“Tom Tim”?

我试过但sscanf拆分“Tom”和“Tim”,因为它们之间有一个空格,因此它也为NUMBER2返回了一个不正确的值。

更新

让我更具体一点。在我的字符串的开头和结尾总会有一个数字,以及这些数字之间的子字符串,它可以有任意长度和任意数量的空格,而我想要获得的是单个变量中的子字符串,以及开始和结束时的数字。

7 个答案:

答案 0 :(得分:5)

你是以

的形式阅读的
sscanf(string, "%d %s %s %d", &NUMBER1, &NAME, &SECONDNAME, &NUMBER2);

然后将它们连接起来

strcat(NAME," "); // Add space
strcat(NAME,SECONDNAME); // Add second name

确保NAME有足够的空间来容纳第一个和第二个名称。你还必须:

#include <string.h>

答案 1 :(得分:4)

为了提出解决方案(并告诉您是否可以使用sscanf),您需要提供有关字符串格式的更多信息。从目前为止提供的单个示例中无法得出任何结论。

在您的特定情况下,您需要知道名称的结束位置以及下一个号码的开头。你如何定义你的情况?我们是否应该假设第一个十进制数字字符表示名称的结尾和number2的开头?还是更复杂的东西?如果输入字符串包含"Tom16"序列,则整个"Tom16"应该是名称,或者我们应该将其拆分为"Tom"并将16留给number2 }?

基本上,如上所述,您的问题不允许有意义的答案,仅适用于随机建议。

更新:您对字符串格式的描述还远未完成,但我可以建议在sscanf中使用以下格式说明符

sscanf(string, "%d %[^0123456789]%d", &number1, name, &number2)

这将起作用,假设您所指的“数字”仅由十进制数字组成,并假设该名称不能包含任何十进制数字。另请注意,它不会在名称中包含前导空格,但会包含尾随空格。如果你不想要它,你必须自己修剪名称的尾随空格。

在任何情况下,sscanf的解析功能都相当有限。它们通常不足以解决像你这样的问题。我上面提到的可能是sscanf中最好的。如果你需要更精细的东西,你必须手动解析你的字符串,用令牌代替,而不是试图用sscanf一次性解析整个事物。

答案 2 :(得分:3)

不,不是sscanf() 您可以使用fgets()“轻松”执行此操作,并按字符

解析行
/* basic incomplete version; no error checking; name ends with whitespace */

#include <ctype.h>
#include <stdio.h>

  int num1, num2;
  char name[250], line[8192], *p;

  fgets(line, sizeof line, stdin);
  num1 = num2 = 0;
  p = line;
  while (isdigit((unsigned char)*p) {num1 = num1*10 + *p - '0'; p++};
  while (isspace((unsigned char)*p)) p++;
  while (!isdigit((unsigned char)*p)) *name++ = *p++;
  while (isdigit((unsigned char)*p) {num2 = num2*10 + *p - '0'; p++};

答案 3 :(得分:2)

你无法使用sscanf函数和带有任意数量空格的“中心”字符串来完成这项工作,因为空格也是下一个字段的分隔符。如果%s匹配带有空格的字符串,它也会“吃掉”6。

如果它只是你的“中心”字段是“特殊的”并且你只有那三个字段,你应该向后读取你的字符串以找到第三个字段的开头,并将其转换为数字;然后用\0替换6之前的字符,从而在第三个字段之前截断字符串。

然后你可以使用strtoul转换第一个字段并确定它的结束位置(使用第二个参数);考虑从那里开始并到截断字符串末尾的字符串,你得到第二个字段。

答案 4 :(得分:1)

@AndreyT非常正确。我猜想中场应该停在任何数字位置。如果是这种情况,那么是sscanf可以完成这项工作:

sscanf(string, "%d %[^0-9] %d", &NUMBER1, NAME, &number2);

您确实希望将读取的数量限制为缓冲区的长度:

char name[32];
sscanf(string, "%d %31[^0-9] %d", &number1, name, &number2);

我应该补充一点,从技术上讲,这不是原样的便携式。要完全移植,您应该使用[^0123456789]而不是[^0-9]。老版本的Borland编译器实际上将“0-9”视为三个字符'0',' - '和'9'。该标准允许这样做,但我不知道任何当前编译器将其权限视为愚蠢。

答案 5 :(得分:0)

你可以:

sscanf(string, "%d %s %s %d", &NUMBER1, NAME1 , NAME2, &number2 );
strcat(NAME , NAME1);
strcat(NAME , " ");
strcat(NAME , NAME2);

但如果NAME不够大,这会导致未定义的行为。

答案 6 :(得分:0)

我可以想到几种方法:

1)如果你总是知道“Tom Tim”字段的大小,请使用带有长度说明符的%c格式:

int num1;
int num2;
char name[8];

sscanf(string, "%d %7c %d", &num1, name, &num2);
name[7] = '/0';

请注意,NAME必须足够大以容纳读取的字符,并且它不会被空终止,因此必须手动完成。

2)如果您知道总有两个字段,请使用两个字符串说明符和strncat()它们在一起:

char name1[40];
char name2[20];
int num1;
int num2;
sscanf(string, "%d %s %s %d", &num1, name1, name2, &num2);
strncat(name1, name2, sizeof(name2)-1);

您还可以使用strtok_r()解析字符串。我将把它作为读者的练习。