scanf() 中的跳过与字符匹配

您所在的位置:网站首页 scanf不能用 scanf() 中的跳过与字符匹配

scanf() 中的跳过与字符匹配

#scanf() 中的跳过与字符匹配| 来源: 网络整理| 查看: 265

标准怎么说:scanf() 的 “转换指令” 及其组成

这一部分主要是对标准的翻译与诠释,只想知道怎么用的同学可以忽略()

scanf() 的第一个参数由零个或多个转换指令(directives)组成,而每个指令又为一个或多个空白字符、既不是 % 也不是空白字符的原始字符串,或者一个 转换指示符(conversion specification)。

上面这个句子晦涩难懂,也许不是人话,但是下面的就好理解了 ~

每一个转换指示符 由 % 开头,并随其后 依次 出现:

一个 可选的、抑制赋值(assignment-suppressing) 的字符:*。

一个 可选的、以十进制正整数形式呈现的数字,用以指示最大宽度(以字符的形式)。

一个 可选的、用以指示待接收对象 “尺寸” 的 长度修饰符(length modifier)。

一个 类型指示符(conversion specifier)。

比如:

%d,d 为 “类型指示符”,表示读取的是整数。

%lld,ll 为 “长度修饰符”,表示读取整数的长度。

%20s,20 表示最大的域宽度,不会超过 20 个字符(实验结果表明,这个 20 不包含 \0, 实验参见 “附录”)。

让 scanf 读取变量但将其忽略

正如标准中所说,可以用 * 来让将读取到的东西忽略:

123int a;scanf("%*d%d", &a);printf("%d\n", a);

输给它两个整数,会忽略第一个,并将第二个整数读取:

12In [1]: 123 321Out[1]: 321

对字符串当然适用:

123char s[20];scanf("%*8s%s", s);printf("%s\n", s);

这时候会忽略前 8 个字符(假定输入没有空白字符),从第九个开始读取:

12In [2]: 1234567890Out[2]: 90 scanset 与指定字符匹配匹配指定字符

标准中提到,类型指示符除了通常的 d,c,s 等等,还有一个 [,用来读取字符串,但是只读取列表中指定的字符。看一个简单的例子,只读取 abcd 四个字母:

123char s[1024];scanf("%[abcd]", s);printf("%s\n", s);

当遇到其他字符(包括空白符)会 结束匹配,并将剩下的 留在缓冲区。

1234In [3]: acccbdb3210Out[3]: acccbdbIn [4]: acccbdb 3210Out[4]: acccbdb

那么如何匹配 ] 呢?要将它放在首位,即 %[]abcd],这样就可以匹配这样的五个字符。同时,%[] 是不合法的,因为这个匹配序列要求非空,实际的警告信息是:缺少 ]。

排除指定字符

使用 ^ 可以排除指定字符,使用时,必须将 ^ 放在 [ 后的第一个位置,这时候,整个列表中 的都是被排除的字符。

123char s[1024];scanf("%[^abcd]", s);printf("%s\n", s);

这样,可以排除这四个字母,一旦遇到,便会停止读取。注意,同 %s 不一样,空格、换行等字符会被读取(除非排除了它们)。

123456789In [5]: 123acdOut[5]: 123In [6]: 321 123 caOut[6]: 321 123

那么如何匹配 ^ 呢?不能 将它放在首位,如 %[abcd^],这样就可以匹配这样的五个字符。

那么如何排除 ^ 呢?%[^^],这样就可以啦 ^_^。

那么如何排除 ] 呢?同样要将它放在首位,即 %[^]abcd],这样就可以排这样的五个字符。同时,%[^] 是不合法的,因为缺少 ]。

你有没有想过,为什么不允许同时出现匹配列表和排除列表呢?如果这样的话,那不是多此一举吗?

匹配或者排除字符范围

标准中规定,当 - 不是第一个,或者不是最后一个,或者当第一个是 ^ 且 - 不是第二个字符时,这个行为是 依实现定义的行为(implementation-defined),要求编译器给出自己的定义,并在文档中写明。

但常用的编译器都会将这样的 - 用来表示字符范围,比如 a-d 可以被理解为 abcd。

123char s[1024];scanf("%[a-zA-Z]", s);printf("%s\n", s);

这个例子可以匹配所有的英文字母,当遇到非字母时便会停止。

12In [7]: qwErt123456yuIopOut[7]: qwErt

若将 ^ 置于首位,同样可以使用 - 来排除字符范围。

123char s[1024];scanf("%[^0-9+*/-]", s);printf("%s\n", s);

这个例子就排除了所有的数字与 +-*/ 这四个符号(注意,要将 - 放在最后一个,或者紧跟 ^,不然会被误解)。

1234In [8]: qwErt123456yuIopOut[8]: qwErtIn [9]: abcd+acbdOut[9]: abcd 综合应用实例读取函数名及参数

现有三种函数:plus(x,y),aver(x,y),multiply(x,y),分别表示对两数求和、平均(整除即可)、相乘。现需要读取这些函数,并分别计算结果。保证 x,y 均以字面量呈现,且均为正整数,函数调用中不含空格。

1234567891011121314151617#include #include

int main() { char name[10]; int opx, opy; while (scanf("%[^(](%d,%d%*[)\n]", name, &opx, &opy) != EOF) { if (strcmp("plus", name) == 0) { printf("%d\n", opx + opy); } else if (strcmp("aver", name) == 0) { printf("%d\n", (opx + opy) / 2); } else if (strcmp("multiply", name) == 0) { printf("%d\n", opx * opy); } } return 0;}

重点看一看第 7 行,转换的指示符可以分为三个部分:

%[^(] 读取函数名,遇到 ( 会结束。

(%d,%d 是最简单的读取方式,匹配类似 (234,432 这样子的,会读取两个变量。

%*[)\n] 用来过滤行尾,吸收 ) 与 可能存在 的换行,会直接丢弃。

输入:

1234plus(234,432)aver(1,6)multiply(4,1)plus(2,9)

输出:

12346663411

千万要注意换行的读取!使用方括号匹配不会自动忽略换行!

读取单词

从一篇文章中读取单词,并分行输出。单词仅有大小写字母组成,单词间由空格、换行、标点符号间隔。保证不会出现类似 I'm 形式的单词。

警告:此例子在实践中出现过奇怪现象,必须先单独过滤一下非单词字符,并在读取时先读取单词,否则会死循环。我的意思是,弄不清楚的情况下请务必谨慎使用!

1234567891011#include

int main() { FILE *in = fopen("in.txt", "r"); char word[1024]; fscanf(in, "%*[^a-zA-Z]"); while (fscanf(in, "%[a-zA-Z]%*[^a-zA-Z]", word) != EOF) { printf("%s\n", word); } return 0;}

在第 6 行中,读取了所有的非字母字符,并直接丢弃。在 第 7 行,使用 %[a-zA-Z] 读取单词,并在稍后使用 %*[^a-zA-Z] 清除非字母字符。

输入(in.txt):

12...Hello, world!.."The C Language is SO--NICE--!" You see

输出:

12345678910HelloworldTheCLanguageisSONICEYousee

正如上面的警告所说,关于开头有没有非单词字符的两种情况,可能会出现一些问题,如果弄不清楚就不要用了,或者使用通常的 while-getchar-ungetc 过滤,只使用 %[a-zA-Z] 读取单词。

术语表 / Glossary 标准中的原文 此文中的译文 directives 转换指令 conversion specification 转换指示符 assignment-suppressing 抑制赋值 conversion specifier 类型指示符 field width (in characters) 域宽度(以字符的形式) length modifier 长度修饰符 implementation-defined behavior 依实现定义的行为 附录 / Appendix实验证明最大域宽度不包含 \0

跳转回去

12345678910#include

char s[8], c;

int main() { scanf("%8s", s); c = '*'; printf("%s\n", s); return 0;}

如上,构造连续内存,使 c 紧跟 s 之后,以消除被添加的 \0。当输入为:

112345678

时,输出结果如下,可能未必相同,因为越界本身是未定义行为。

112345678*

如果不指定位宽 8,那么会像正常那样读到空白符,并一股脑儿丢掉。

参考文献 / Reference

[1] ISO/IEC 9899:TC3



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3