概要
在C语言中,往往常用到字符串、数组等,对于一个字符串,其输入很简单,一个语句即可完成,而对于整型数组,我们常遇到的情况都是已知数组长度,即元素的个数之后,用一个for循环完成输入。
然而,如果事先不知道需要输入元素的个数,要求从键盘输入多少个整数便记录多少个,回车之后即完成输入,对于这个问题,看似简单,但对于初学者来说,似乎一时还真没有思路。
长度未定的整型数组 1、关于scanf()
scanf(格式控制,地址列表)
格式控制部分是一个字符串,其中格式声明以 '%' 开始,以一个格式字符结束,中间可以插入附加字符,表示属性。除此之外,还可以含有其他的字符(空格字符、转义字符中的字符和非空格的普通字符),但需要注意的是,如果格式控制字符串中除了格式声明,还含有其他的普通字符,那么在输入时应该在对应位置输入相同字符,不能改写也不能漏写,因为系统是逐个对照检查的。
此外,之所以把空格字符和转义字符中的字符(如Enter、Tab)单独列出来,是基于其特殊之处:
(1).在使用 '%f' 、'%d' 作为格式声明进行数值的输入时,转义字符和空格是作为无效字符,仅仅是作为一个数值与另一个数值中间分隔的作用,或者说是作为一个数值的输入结束标志。
说着都是苍白的,“No photo you say a J8”。
示例1:在格式控制中,格式声明之间没有普通字符的
scanf("%d%d", &A, &B);
对于这种情形,必须在 一个数值输入完成后加一个结束标志作为分隔,可以是空格、回车、Tab。
输入1:520[空格]250↵
输入2:520[Tab]250↵
输入3:530↵250↵
输入4:520[空格][Tab]↵↵250↵
对于上述4组不同的输入方式,结果如下:
可见,在进行数值输入时,如果格式控制字符串中没有其他普通字符,空格、回车和Tab等仅仅在格式声明之间起分隔作用,个数不限。
同理可以推测,在格式控制字符串中如果格式声明间除普通字符外,含有的空格、回车或Tab等可以忽略,也可以随便输多少个,也即对于这三个,只作为分隔作用。
示例2:在格式字符串中,格式声明之间含有普通字符
scanf("%d/WNDMD/ /WRSNDM/%d", &A, &B);
该语句的格式控制字符串中一共有两个格式声明,中间有两段普通字符串,中间隔了一个空格。
由上文已知,在进行数值的输入时,空格、回车以及Tab的作用都是一样的,作为分隔,而且数目不限,所以,在以下测试中,就不再使用回车和Tab作为分隔,仅用空格就可以说明问题。此外,如果格式声明之间已经含有普通字符,那么就无需再加入分隔字符,也就是输入结束标志。
输入1:250/WNDMD/[空格]/WRSNDM/3600↵
输入2:250/WNDMD//WRSNDM/3600↵
输入3:250/WNDMD//WRSNDM/[空格]3600↵
输入4:250[空格]/WNDMD//WRSNDM/3600↵
结果如下:
可见,图1-3都是正常的,图4出现了问题,图1即为一一对应输入,图2是去掉了两个普通字符串之间的空格,图3是在输入完普通字符后再加入了空格,即结束标志,其结果都是正确的,验证了上述结论。
而图4出现错误,是因为在输入第一个数值后系统本该直接开始检查 "/WDNMD/" 却出现了空格,所以第二个数值便不能正常输入。
可以推测,如果在格式控制字符串中第一个格式声明后加上一个空格,那么输入时,第一个数值后可以输入多个空格,也可以直接忽略掉,后面照上述输入1-3都行(因为格式控制字符串中,格式声明之间如果有普通字符,那么所有的结束标志都可以忽略)。
scanf("%d /WNDMD/ /WRSNDM/%d", &A, &B);
输入1:250[空格][空格][空格]/WNDMD//WRSNDM/3600↵
输入2:250/WNDMD//WRSNDM/3600↵
结果如下:
各种情况都列了出来,其他的不必多言。
(2).在使用 '%c' 作为格式声明进行字符输入时,空格字符和转义字符中的字符都作为有效字符。
示例3:对这样一个输入语句
char A, B; scanf("%c%c", &A, &B);
对于如下输入:
输入1:NB↵
输入2:N[空格]↵
输入3:↵[空格]↵
结果如下:
结果明显和上文描述中的一致。
2、具体实施
在整型数组元素个数不确定的情况下,输入了多少个整数便放多少个到数组中。
scanf()说到底就是从stdin中格式化地读取数据,而数据在stdin中的存放形式也是字符串,所以实现的原理很简单,先取出第一个,判断是否为十进制数,是的话就从取出来的那个开始按整形的格式读,直到遇到结束标志,由上文已经知道,空格、回车、Tab以及普通字符都可以作为数值输入的结束字符,所以会自动读取到下一个非十进制数的位置;如果取出的那个字符不是十进制数,那么继续取下一个。
如此,代码如下:
int main(void) { //长度未定的整形数组输入 int Num[1000],Loc; char Temp; Loc = 0;//Loc表示已经输入[读取]的整形数的个数 while ((Temp = getchar()) != '\n') {//从stdin流中获取第一个字符 if (isdigit(Temp)) {//判断该字符是否为十进制数 ungetc(Temp, stdin);//若是十进制数,则将改字符退回输入流 scanf("%d", &Num[Loc++]);//开始从该字符处开始读取int型数 } //如果不是十进制数,直接跳过,开始读下一个[读一个少一个] } return 0; }
注释很清楚,无需多言。
补充 1、长度确定的整型数组的输入
//确定长度的整形数组输入 int Num[N];//此处N是个具体的数,不能是变量,这样写只是为了更好地展示 for (int i = 0; i < N; i++) { scanf("%d", &Num[i]);//输入时可使用逗号或者空格隔开,以回车键结束输入 }
2、字符的输入
char C; //method 1 scanf("%c", &C); //method 2 C=getchar();
3、字符串的输入
char Str; //method 1 scanf("%s",Str); //method 2 gets(Str); //method 3 int N; gets_s(Str, N); //method 4 int N; fgets(Str, N, stdin);
上述函数中,gets()、gets_s()和fgets有必要单独拿出来讲一下区别和用法,鉴于已经存在关于这个的总结得很优秀的文章,就不再赘述。
可参考: