我应该得到一个可以采用以下任何格式的输入行:
如何分离1,2和3个单词的情况并将数据放入正确的变量?
word1
word1 word2
word1 word2 , word3
word1 word2,word3
我想到了类似的东西:
sscanf("string", "%s %s,%s", word1, word2, word3);
但它似乎不起作用。
我使用严格的C89。
答案 0 :(得分:23)
int n = sscanf("string", "%s %[^, ]%*[, ]%s", word1, word2, word3);
n
中的返回值会告诉您已成功完成了多少作业。 %[^, ]
是一个否定的字符类匹配,它找到一个不包括逗号或空格的单词(如果你愿意,可以添加标签)。 %*[, ]
是匹配,可以找到逗号或空格,但会禁止分配。
我不确定我是否会在实践中使用它,但它应该可行。但是,这是未经测试的。
可能更严格的规范是:
int n = sscanf("string", "%s %[^, ]%*[,]%s", word1, word2, word3);
不同之处在于非分配字符类只接受逗号。 sscanf()
在word2
之后停留在任何空格(或EOS,字符串末尾),并在分配给word3
之前跳过空格。上一版允许在第二个和第三个单词之间留一个空格来代替逗号,这个问题并不严格允许。
正如pmg在注释中建议的那样,分配转换规范应该给出一个长度以防止缓冲区溢出。请注意,长度不包括空终止符,因此格式字符串中的值必须小于数组的大小(以字节为单位)。另请注意,虽然printf()
允许您使用*
动态指定大小,但sscanf()
等使用*
来禁止分配。这意味着您必须专门为手头的任务创建字符串:
char word1[20], word2[32], word3[64];
int n = sscanf("string", "%19s %31[^, ]%*[,]%63s", word1, word2, word3);
(Kernighan& Pike建议在其(优秀)图书'The Practice of Programming'或亚马逊The Practice of Programming 1999中动态格式化格式字符串。)
刚发现问题:给定
"word1 word2 ,word3"
,它不会显示word3
。有治疗方法吗?
是的,有一种治疗方法,实际上也是微不足道的。在非赋值的逗号匹配转换规范之前,在格式字符串中添加一个空格。因此:
#include <stdio.h>
static void tester(const char *data)
{
char word1[20], word2[32], word3[64];
int n = sscanf(data, "%19s %31[^, ] %*[,]%63s", word1, word2, word3);
printf("Test data: <<%s>>\n", data);
printf("n = %d; w1 = <<%s>>, w2 = <<%s>>, w3 = <<%s>>\n", n, word1, word2, word3);
}
int main(void)
{
const char *data[] =
{
"word1 word2 , word3",
"word1 word2 ,word3",
"word1 word2, word3",
"word1 word2,word3",
"word1 word2 , word3",
};
enum { DATA_SIZE = sizeof(data)/sizeof(data[0]) };
size_t i;
for (i = 0; i < DATA_SIZE; i++)
tester(data[i]);
return(0);
}
示例输出:
Test data: <<word1 word2 , word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2 ,word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2, word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2,word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
Test data: <<word1 word2 , word3>>
n = 3; w1 = <<word1>>, w2 = <<word2>>, w3 = <<word3>>
一旦'非赋值字符类'只接受逗号,您可以将其缩写为格式字符串中的文字逗号:
int n = sscanf(data, "%19s %31[^, ] , %63s", word1, word2, word3);
将其插入测试工具会产生与以前相同的结果。请注意,所有代码都受益于审核;即使在工作之后,它也经常(基本上总是)得到改善。
答案 1 :(得分:4)
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="word1 word2,word3";
char* pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok(str," ,");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
答案 2 :(得分:3)
<强>摘要:强> 答案分为三个部分。第一部分回答了正确使用sscanf&#34;的一般问题,描述了使用sscanf的好处,以及什么时候最好使用sscanf。第二部分是回答问题的具体部分。第三部分对问题的一般部分和具体部分至关重要,并尽可能简单地描述sscanf的内部工作。
第1部分使用sscanf的优势:使用sscanf正在解决一个大问题 (原始输入线)一次到较小的问题(输出令牌)。
如果行规则定义明确(例如,问题中的行规则定义明确:单词1和单词之间必须有空格。单词2和单词3之间必须有逗号。空格不是必须的单词2和单词3 - 但是任何数量的空格都是可能的。)而不是sscanf可以带来问题的是/否答案&#34;当前的读取行是否符合行规则?&#34; (不试图分析和理解输入文件中输入的内容,或者打算在那里键入的内容),它也可以给出行的输出标记;两个都立即。
为此,将输入字符串分离为令牌,使用%c很方便。我们应该记住,默认情况下,sscanf会跳过空格字符(空格,制表符和换行符),但不会跳过%c,其中sscanf读取空格并将其指定为相应字符变量的值。
使用strtok,确实更加通用和灵活,但它不具备一次读取整行,并使用丰富的词法分析(即%d,%f,%c *,^和所有sscanf的词汇。并且如果线规则被很好地定义,并且是/否答案,那么问题&#34;当前读取线是否符合线规则?&#34 ;;足以使用这些优点。
第2部分回答具体问题:这里是一个似乎有用的sscanf代码行,下面是对代码行的解释。 (假设数字100大于最大输入行大小。)
电话:
n = sscanf(" sssfdf wret , 123 fdsgs fdgsdfg",
"%100[^ ]%c%100[^,] %c %100[^\0]", s1, &ch1, s2, &ch2, s3);
将导致:
s1 = ""sssfdf";
ch1=' ';
s2=""wret ";
ch2=',';
s3=""123 fdsgs fdgsdfg";
读取至少100个字符或所有字符,直到第一个空格为s1。 (请记住,条件是第一个单词和第二个单词之间应该只有一个空格。)
将下一个字符读入ch1(稍后我们可以检查ch1是否具有空格值)。
读取最少100个字符或所有字符,直到第一个逗号为s2,s2可能包含稍后将删除的空格。 (第二个单词与第三个单词之间应该有逗号,逗号前后可选空格)。
请注意,%100 [^]%c%100 [^,]没有空格,因为第一个%c之前的空格将导致空格后的字符为erad,即%100之前的空格[ ^,]将在第一个单词和第二个单词之前启用多个空格。
将下一个字符读入ch2(稍后我们可以检查ch2是否具有逗号值)。
将输入字符串的剩余部分读取到s3(从第一个无空格读取,直到字符串终结符字符)。
剩下的就是检查s1,s2和s3的有效性(并测试ch1和ch2的值是快速和逗号)。
第3部分sscanf的内部工作: sscanf()函数,一次开始读取一个字符的格式字符串。这个角色有3个可能的值,一个空格,&#39;%&#39;或者其他。
如果下一个字符不是空格而不是&#39;%&#39;,那么它开始读取输入字符串 1.1如果输入字符串中的下一个字符不是该字符中的字符 格式字符串,sscanf停止它的工作并返回给调用者 到目前为止它读取的参数数量。 例如:
n = sscanf(&#34; 2 22.456&#34;,&#34; 2%f&#34;,&amp; FloatArg); / * n为0 * /
1.2如果输入字符串中的下一个字符是格式中的字符 字符串,比sscanf继续从格式中读取下一个字符 串。
n = sscanf(&#34; 2 22.456&#34;,&#34; 2%f&#34;,&amp; FloatArg); // n是1 FloatArg = 22.456
如果格式字符串中的下一个字符是%,则sscanf会跳过 空格并等待以%格式读取字符串。例如对于%f, 它等待以下列格式读取和输入: [+/-] [IntDigiT1] ... [IntDigiTn]&LT; ....取代。 例子:31.25,32,3 2.1如果sscanf没有找到该格式,则返回数字 到目前为止它已经读过的论点。 例如:
n = sscanf(&#34; aaa&#34;,&#34;%f&#34;,&amp; FloatArg); // n = 0
2.2如果sscanf读取至少一个数字,或一系列数字后跟a &#39;。&#39;,比遇到非数字时,它会得出结论 到了漂浮的尽头。 sscanf()将非数字放回 输入,并将读取的值赋给浮点变量。 例1:
n = sscanf(&#34; 2 22.456&#34;,&#34; 2%f&#34;,&amp; FloatArg); // FloatArg是22.456
例2:
n = sscanf(&#34; 22.456&#34;,&#34; 2%f&#34;,&amp; FloatArg); // FloatArg是2.456
如果格式字符串中的下一个字符是空格,则表示跳过 在下一个输入字符之前的任何空格上。
一个。读取字符(%c):如果下一个输入字符是空格(例如空格),则为指定的变量分配空格。
B中。读取字符串(%s):除了空格之外的任何字符都可以接受, 所以scanf()跳过空白到第一个非空白字符,然后保存非空白字符,直到再次碰到空白。 sscanf将字符串终止符添加到指定字符串变量的末尾。
℃。答案没有输入格式%变体。 [=%[*] [宽度] [改性剂]类型=]。这部分的一个很好的描述是http://docs.roxen.com/(en)/pike/7.0/tutorial/strings/sscanf.xml 请注意,上面链接中的%[字符]用于回答私有问题,并启用字符串灵活操作。
d。以上是我在互联网上搜索并在Dev-C ++ 5.11中测试时发现的各种字符串,它不承诺是完整的,建设性的评论,将被感谢接受,并将帮助我改进答案。
答案 3 :(得分:0)
这超出了scanf和朋友的范围,完全诚实;除了“编写你自己的简单解析器”的答案之外,你可以投资yacc来解析语法(词法分析器留给读者练习):
line: oneword | twowords | threewords;
oneword: word;
twowords: word word;
threewords: word word word;
word: STRING;
这对你来说可能有点矫枉过正,但如果你需要解析甚至超过简单复杂的格式,它就是救星。