ACM模式下C语言输入输出处理

C语言处理输入输出,无非就是使用scanf函数或者printf函数。当然还有其它函数比如putc,puts,getc,gets,但是对于绝大数场景来说,scanf和printf足够了,况且这两个是标准输入输出函数,干嘛还要去费劲记其它函数。在这里需要声明一个思想前提,我们在处理输入之前,首先要思考我们该用什么存储结构去保存输入。直接用变量接收,还是数组?链表。根据具体场景使用吧。

好了,在进入正题之前,我们需要非常熟练的掌握scanf和printf函数的用法,熟练,熟练!背也给我背下来,包括各种格式化参数。

scanf函数原型:

int scanf(const char *format, ....);

foramt:格式化字符串,也就是我们的第一个参数(回忆一下我们在使用的时候第一个参数是不是都是用双引号括起来)

....:可变数量的参数,读取的参数就放在这些参数里

返回值:成功读取,返回读取的输入项数,如果读取失败(这里的失败包括读取的内容和格式format不符合,还有缓冲区没有内容了也会读取失败),返回EOF(-1);

所以我们通常同while(scanf("farmat", 变量) != EOF) {} 来不断的读取输入

格式化控制符foramt:

我们的输入设备输入的字符会存入缓冲区,当执行到scanf函数时会从缓冲区读取内容,但是如果缓冲区是空的,即没有任何输入内容,那么程序就会在scanf函数处阻塞,直到缓冲区中出现内容。通常我们从输入设备(比如键盘)输入数据时,会先把内容放到缓冲区中,当我们按下回车时表示我们结束本次输入。因此当使用scanf读取输入时,缓冲区的最后一个字符一定有一个回车符(如果你用了什么奇怪的方法直接修改了缓冲区的内容,那大佬你直接无视我就好了),那个回车符被当作你结束本次外设输入操作的标志。

现在假设缓冲区中有:niukewangyyds\n1234\n

\n是换行符也即回车。在执行到scanf("%s", name);的时候,我们读取的是一个字符串。在scanf中,无论读取的是字符串还是数字,都是看见空白符(空白符包括回车即换行符\n,制表符\t, 空格符“ ”)才表示读取了一次数据,这也意味着,在读取字符串时,最后的回车符是不会被读取的。如果接下还要其它接受输入的变量,那么接着读取下一次数据。因此上述函数读取到了niukewangyyds存入name中(假设name是一个字符数组)。于是,缓冲区变成了\n1234\n。

接下来,如果读取的是数字,比如执行到的是scanf("%d", &num);那scanf函数会忽略空白符直接读取接下来的数字,因此1234被读取到num中(假设num是一个整型变量)。因此执行完scanf("%d", &num)后缓冲区只剩下\n了。这里还需要注意的一点是,无论读取的是字符串还是数字,对于scanf来说,缓冲区开头出现的所有空白符都会被忽略(除了scanf("%c", &c), 因为对于%c这个参数比较伟大,所有字符在它面前都是众生平等)。

这就有一个问题,那么在使用%c连续读取字符时,如果不需要空白符,就要特别注意使用if判断把它去掉。

比如缓冲区中内容为:abcd\na

首先使用scanf("%s", s)读取了前面的abcd,后面又想用scanf("%c", &c)读取a的话,就要注意我们需要连读两次,第二个才是我们需要的字符。通常scanf("%c", &c)也会被用了清空缓冲区(主要时可以读取空白符达到清空的目的)。

那么scanf就存在局限性:我们无法直接读取带有空格的字符串,非要使用scanf的话会比较麻烦(加判断条件呗)。因此,可以使用gets读取带有空格的字符串,这个函数无论什么都读进去,直到遇见回车符。gets函数通常在需要处理包含空格的字符串时使用。虽然用的少,但是别忘记了。

总结一下:不同的输入之间使用空白符(空格、回车换行、制表符)隔开。

当然还要高级一些的用法,比如:

1、数字输入

在ACM模式中,同一组的输入通常用空格隔开,不同组输入使用回车(换行)隔开。在使用scanf函数获取数字输入时,将会忽略空白符(回车符、制表符、空格符)

1.1 同一组数字使用空格隔开,不同组之间使用回车(换行符)隔开

整数输入又分为二进制、8进制、10进制和16进制输入形式,常用的是十进制输入输出形式,但是也会有要求16进制的输入形式,特别要注意。

直接使用变量接收输入,适用于输入不多且处理逻辑简单的题型。这个程序会一直执行while循环,原因就是除了scanf("%c", c)以为其它格式化输入都无法接收空白符,但是每次我们输入结束时都会敲入一个回车符,那么缓冲区中就会一直存在这个回车符直到缓存区有新的内容,下一次读取时才会忽略这个回车符,因此就会一直处于while的输入阻塞状态。这个带给我们启示就是,在连续读取数据保存到数组时,一定要清空缓冲区,否则无法跳出while1循环,下面的数组保存中将会讲解。可以在while循环体中对读取的变量进行处理。这样就可以连续处理多组数据。

使用数组接收输入

这种情况下,我们一般可以提前分配一个比较大的数组,当然也可以根据实际分配(除非输入中告诉了你这一次会输入多少个数字)。但是,我统一用大数组存储不香吗,省时省力,现在又不缺内存。我们同时也可以用一个变量inputNum记录读取的个数,也就是实际在数组中保存的数字的个数。这样,实际上我们就只操作区间[0, inputNum)这个左闭右开的子区间就行。每个数字之间使用空格隔开,最后使用回车符完成一组数据的输入,因此需要读取数字后的下一个字符(这个字符不是空格就是回车),如果是回车,那么跳出循环。如果没有这个判断,这个循环是跳不出来的。

上面的代码并没有解决连续输入多组数字的问题,但是,你也可以再加一层while即可。在ACM模式时,每一组输入它都会重新调用你的程序,所以没有连续处理多组数据也没问题。

1.2 输入第一行只有一个数表示数据组数,接下来是多组数据,不同组之间使用回车(换行)隔开,同组之间使用空格隔开

示例:

2

1 5

10 20

其实数字输入的排列方式很多,如果列举所有可能很是很麻烦的。我们只需要记住下面几点:

(1)你的缓冲区里有什么?千万别忽略里面的空白符(包括空格、换行和制表符),即使是数字输入,这些字符也很重要;

(2)你的scanf函数的行为是什么,它将会对缓冲区做什么操作;

(3)执行完这一次scanf后,你的缓冲区还剩下什么。

其次,我们要知道用什么样的存储方式存储数据,用变量,数组还是结构体?还是说变量数组混合使用?

最后,我们通常使用while(scanf("", ) != EOF)来连续读取数据,有时在能够从输入中得到输入个数(组数)的情况下,我们读取对应的个数后通过for循环计算次数的方式也可以连续读取输入。在循环读取过程中,还可以根据空白符或者题目所给的标志符进行判断输入是否结束,或者是不是要做其它什么操作?

最后强调,一定要知道你的scanf函数的行为和缓冲区中内容的变化,这一点对于你在循环读取过程中进行一些判断非常重要!比如你可能要读取一个空白符根据是否回车符来判断一行的输入是否结束?

2、字符串输入

2.1 第一行指定字符串个数,第二行是n个字符串,字符串之间使用空格隔开

示例:

4

ab bc cd efgh

2.2 多个测试用例,每个测试用例包含多个字符串,用空格隔开,不同测试用例用换行隔开

示例:

a b aa

bb aa

abcd

这里就涉及到了读取空白符并进行判断

2.3 多个测试用例,每个测试用例一行,同一个用例的不同字符串用逗号隔开,不同测试用例用换行隔开

示例:

a, c, bb

f, hh

jk

这里使用到了strtok函数

3、数字、字符串混合输入

数字,字符混合输入就分别读取分别处理就好。关键是要知道scanf的行为以及缓冲区里有什么。

4、C语言常用函数

排序函数qsort

参考链接:https://www.nowcoder.com/discuss/353158058350157824

字符串相关函数:https://www.runoob.com/cprogramming/c-standard-library-ctype-h.html

字符相关函数:https://www.runoob.com/cprogramming/c-standard-library-ctype-h.html

C语言中没有定义好的数据结构可以使用,因此我们需要记住一些常用的数据结构怎么实现。

栈的简单实现:https://www.nowcoder.com/discuss/502051233935478784?sourceSSR=users

队列的简单实现:

5、输出printf函数

6、各种变量表示范围及其字长

7、华为机试题目:https://www.nowcoder.com/exam/oj/ta?page=1&tpId=37&type=37

#C语言##ACM模式输入输出处理#
全部评论

相关推荐

5 20 评论
分享
牛客网
牛客企业服务