scanf advanced usage

Always powerless in solving some of the problems of limited input, until the discovery of advanced usage scanf, only to find the original can be so simple.

Article reproduced in: https://blog.csdn.net/C1664510416/article/details/80869470

 

In the previous sections, we demonstrated how to use scanf () to read a variety of data, summarizes the scanf () format that can be used to control characters, then also explain the buffer, eliminating the scanf fundamentally ( ) of those strange behavior, so far, many beginners think they have completely mastered the scanf ().

In fact, this is just scanf () of the basic usage, every C programmer should master the language, if you want your input more cool, more personal, more security, you also need to learn scanf () advanced usage, this is the great God and rookie of the watershed.

Well, Closer to home, we explain three aspects scanf () advanced usage.

1) Specify the read length

Remember you can specify the minimum output width in printf () in it? Is the intermediate format control character followed by a number, for example, %10dinteger representation of the output of the position occupied by at least 10 characters:

  • If the integer width of less than 10, then padded with spaces on the left;
  • If the width exceeds the integer 10, the width of the integer itself to the output 10 no longer functions.


In fact, Scanf () has a similar usage can also add a digital intermediate format control codes and for reading data represents the maximum length, for example:
%2drepresents an integer of two read up;
%10smaximum string representation read length is 10, or reading up to 10 characters.

Consider the following example:

  1. #include <stdio.h>
  2. int main(){
  3. int n;
  4. float f;
  5. char str[23];
  6. scanf("%2d"&n);
  7. scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区
  8. scanf("%5f"&f);
  9. scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区
  10. scanf("%22s", str);
  11. printf("n=%d, f=%g, str=%s\n", n, f, str);
  12. return 0;
  13. }

输入示例 ①:

20↙
100.5↙
http://c.biancheng.net↙
n=20, f=100.5, str=http://c.biancheng.net

输入示例 ②:

8920↙
10.2579↙
http://data.biancheng.net↙
n=89, f=10.25, str=http://data.biancheng.

这段代码使用了多个 scanf() 函数连续读取数据,为了避免受到缓冲区中遗留数据的影响,每次读取结束我们都使用scanf("%*[^\n]"); scanf("%*c");来清空缓冲区。

限制读取数据的长度在实际开发中非常有用,最典型的一个例子就是读取字符串:我们为字符串分配的内存是有限的,用户输入的字符串过长就存放不了了,就会冲刷掉其它的数据,从而导致程序出错甚至崩溃;如果被黑客发现了这个漏洞,就可以构造栈溢出攻击,改变程序的执行流程,甚至执行自己的恶意代码,这对服务器来说简直是灭顶之灾。

在用 gets() 函数读取字符串的时候,有一些编译器会提示不安全,建议替换为 gets_s() 函数,就是因为 gets() 不能控制读取到的字符串的长度,风险极高。

就目前学到的知识而言,虽然 scanf() 可以控制字符串的长度,但是字符串中却不能包含空白符,这是硬伤,所以 scanf() 暂时还无法替代 gets()。不过大家也不要着急,稍后我还会补充 scanf() 的高级用法,届时 scanf() 就可以完全替代 gets(),并且比 gets() 更加智能。

2) to match specific characters

%s 控制符会匹配除空白符以外的所有字符,它有两个缺点:

  • % S specific characters can not be read, just reading such as lower case letters, numbers, etc. or decimal,% s can not do anything;
  • % S read into the string can not contain white space, in some cases it would be more embarrassing, for example, more than one word can not be stored in a string, because that is a space between words is separated,% s encounter spaces I read over.


要想解决以上问题,可以使用 scanf() 的另外一种字符匹配方式,就是%[xxx][ ]包围起来的是需要读取的字符集合。例如,%[abcd]表示只读取字符abcd,遇到其它的字符就读取结束;注意,这里并不强调字符的顺序,只要字符在 abcd 范围内都可以匹配成功,所以你可以输入 abcd、dcba、ccdc、bdcca 等。

请看下面的代码:

  1. #include <stdio.h>
  2. int  main () {
  3. char str[30];
  4. scanf("%[abcd]", str);
  5. printf("%s\n", str);
  6. return 0;
  7. }

Input Example ①:

abcdefgh↙
abcd

Input Example ②:

baccbaxyz↙
baccba

Use connector

To simplify the set of characters written, Scanf () supports the use of a hyphen -to represent characters in a range of, for example% [az],% [0-9 ] and the like.

The left side of the hyphen character corresponds to an ASCII code, the right of the hyphen character also corresponds to an ASCII code, is located two characters to be read within the ASCII range of characters. Note that even the character to the left ASCII code to be less than the right, if, in turn, then its behavior is undefined.

Common hyphen Example:

  • %[a-z]Indicates the character abc ... xyz read range, i.e. lower case letters;
  • %[A-Z]Means to read characters in ABC ... XYZ range, ie capital letters;
  • %[0-9]Means to read characters in the range 012 ... 789, that is a decimal number.


You can also combine them, for example:

  • %[a-zA-Z]Means to read uppercase and lowercase letters, that all the letters of the alphabet;
  • %[a-z-A-Z0-9]Represents read all the decimal digits and letters of the alphabet;
  • %[0-9a-f]Means to read hexadecimal numbers.


Consider the following presentations:

  1. #include <stdio.h>
  2. int main(){
  3. char str[30];
  4. scanf("%[a-zA-Z]", str); //只读取字母
  5. printf("%s\n", str);
  6. return 0;
  7. }

输入示例:

abcXYZ123↙
abcxyz

Some characters do not match

假如现在有一种需求,就是读取换行符以外的所有字符,或者读取 0~9 以外的所有字符,该怎么实现呢?总不能把剩下的字符都罗列出来吧,一是麻烦,二是不现实。

C语言的开发者们早就考虑到这个问题了,scanf() 允许我们在%[ ]中直接指定某些不能匹配的字符,具体方法就是在不匹配的字符前面加上^,例如:

  • %[^\n]Which matches all characters except newline, the newline stopped reading;
  • %[^0-9]Which matches all characters except the decimal numbers, decimal numbers to stop reading experience.


请看下面的例子:

  1. #include <stdio.h>
  2. int  main () {
  3. char str1[30], str2[30];
  4. scanf("%[^0-9]", str1);
  5. Scanf ( "% * [^ \ n- ]" );  Scanf ( "% C *" );  // Clear Buffer
  6. scanf("%[^\n]", str2);
  7. printf("str1=%s \nstr2=%s\n", str1, str2);
  8. return 0;
  9. }

Input Example:

abcXYZ@#87edf↙
c c++ java python go javascript↙
str1=abcXYZ@#
str2=c c++ java python go javascript

Note that the code line 6, which reads a line of the string, and gets () function is exactly the same. You see, scanf () can also read the string with spaces Yeah, who says scanf () can not completely replace the gets (), which is obviously wrong to say.

Further, Scanf () can also specify the maximum length of the string, which specifies a character string can not contain, it is gets () does not have the function.

For example, read a line may not contain a string of decimal digits, and the length can not exceed 30:

  1. #include <stdio.h>
  2. int main(){
  3. char str[31];
  4. scanf("%30[^0-9\n]", str);
  5. printf("str=%s\n", str);
  6. return 0;
  7. }

输入示例 ①:

http://c.biancheng.net http://biancheng.net↙
str=http://c.biancheng.net http://

输入示例 ②:

I have been programming for 8 years.↙
str=I have been programming for 

总之,scanf() 不仅可以完全替代 gets(),并且比 gets() 的功能更加强大。

3) the character read is discarded

在前面的代码中,每个格式控制符都要对应一个变量,把读取到的数据放入对应的变量中。其实你也可以不这样做,scanf() 允许把读取到的数据直接丢弃,不往变量中存放,具体方法就是在 % 后面加一个*,例如:

  • %*dA represents an integer of read and discarded;
  • %*[a-z]Lowercase letters represent read and discarded;
  • %*[^\n]It said it would discard all characters other than newline.


请看下面的代码演示:

  1. #include <stdio.h>
  2. int  main () {
  3. int n;
  4. char str[30];
  5. scanf("%*d %d"&n);
  6. scanf("%*[a-z]");
  7. scanf("%[^\n]", str);
  8. printf("n=%d, str=%s\n", n, str);
  9. return 0;
  10. }

Input Example:

100 999abcxyzABCXYZ↙
n=999, str=ABCXYZ

Analysis of results: The first 100 is an integer scanf () in %*dthe read discarded, the integer 999 is first %dread, and assigned to n. At this time, the remaining buffer abcxyzABCXYZ, second scanf () to read and discard abcxyz, is the last remaining ABCXYZ scanf () to read and assigned to str.

We have not realized, will read characters directly discarded, which is in the input buffer emptied it, though a bit lame, but effective. In the " Clear (refresh) buffer to eliminate those strange behavior fundamentally " section, we have been given use scanf () empty the buffer solution is:

scanf("%*[^\n]"); scanf("%*c");

Here we have to explain.

You first need to understand is that, until the time required to empty the buffer, the buffer must be the last character in a line break \n, because the input buffer line buffering mode, the user presses the Enter key will produce a line break ends the current input and then enter the function to start reading.

scanf("%*[^\n]");All newline characters in front of the empty, scanf("%*c");the last remaining line breaks empty.

Some users merge these two statements, writing:

scanf("%*[^\n]%*c");

This is wrong. After the merge statement can not empty a single line break, because the statement is required to have a line break in front of at least one other character, single line break will lead the match fails.

to sum up

scanf () complete control string is written:

%{*} {width} type

Wherein {} represents optional. The specific meaning of each part are:

    • typeIt indicates what type of data is read, for example,% d,% s,% [az],% [^ \ n] and the like; type must have.
    • widthIt indicates the maximum reading width, dispensable.
    • *Represents read data is discarded, dispensable.

Guess you like

Origin www.cnblogs.com/Truedragon/p/12233400.html