以下部分内容转载自琴影老师博客:这是一个传送门 感谢帮助!
今天做了一道算法题,题目本身不是特别难,内容如下:
Problem Description
1 #include <iostream> 2 #include <cstdio> 3 #include <string> 4 #include <cstring> 5 #include <cctype> 6 #include <map> 7 using namespace std; 8 9 int main() 10 { 11 char buf[12], sign, s1[12], s2[12], ch; 12 map<string, string> mp; 13 int id = 0; 14 gets(buf); //strip START 15 while(scanf("%s%s", s1, s2), strcmp(s1, "END")){ 16 mp[s2] = s1; 17 } 18 getchar(); //注意此处! 19 while(scanf("%c", &ch)){ 20 if(isalpha(ch)) buf[id++] = ch; 21 else{ 22 buf[id] = ‘\0‘; id = 0; 23 if(strcmp(buf, "END") == 0) break; 24 if(mp.find(buf) != mp.end()){ 25 printf("%s", mp[buf].c_str()); 26 }else printf("%s", buf); 27 putchar(ch); 28 } 29 } 30 return 0; 31 }
(传送门2)
#include<cstdio> #include<string> #include<iostream> #include<map> #include<sstream> #include<cctype> using namespace std; int main() { string s,w1,w2,line,word; map<string,string> mp; cin>>s; while(cin>>w1&&w1!="END") { cin>>w2; mp[w2]=w1; } cin>>s; getchar(); //注意此处!! while(getline(cin,line)&&line!="END") { string str=""; for(int i=0;i<line.length();i++) { if(!isalpha(line[i]) ) { map<string,string>::iterator it=mp.find(str); if(it==mp.end()){cout<<str;} else cout<<mp[str]; str=""; cout<<line[i]; } else str+=line[i]; } cout<<endl; } return 0; }
注意我两处标注的getchar(),因为自己太弱,接触的题也少,当时我真是百思不得其解,只知道它是用来吃换行的,但并不知道这个换行到底是哪来的,只能大概猜测和缓冲区有关,于是上网搜了很多关于getchar、缓冲区、“吃”换行等等的资料。然后我发现一篇博客(链接在文章顶部),它是这么说的:
1)
1.1 scanf 输入字符时,会将‘\n‘吸收
1.2 scanf 输入字符串时,遇到空格或者回车就代表结束
输入一个字符串,如果在这之前有空格或回车,空格和回车不会给字符串。遇到下一个空格或回车才代表结束
1.3 读一行字符,可以用gets();
(2)
cin用法很简单,如果输入的是一个字符,那么,‘\n‘不会被吸收, 其他的情况和scanf差不多
(3)如果用gets()或者getline(),那么它一遇到‘\n‘就结束,比如定义 char c; char s[10]; scanf("%c", &c);gets(s);printf("%c\n", c);printf("%s",s);
如果一输入一个字符想给c,然后回车在下一行输入一行字符串给s;那么输出的时候会发现,第一行是字符c,第二行是个空行, 光标在第三行;
分析:输入的第一个字符给了c,然后回车‘\n‘,这个回车代表了s是个空串(很神奇),同时,如果在输入一个字符c之后,按两个空格再加一个字符a再回车,那么s包含的就是两个空格字符加字符a,在结束
还发现,如果定义 char c[10]; char s[10]; scanf("%s", c);gets(s);printf("%s\n", c);printf("%s",s);
输入asd SS
结果输出的是
asd
__SS(前面有有两个空格)
表明输入asd加个空格表示c字符串结束时,这个空格同时给了字符串s。
(4)如果定义的是字符数组 char c[10],那么读入一行只能用gets(),不能用getline();可以用cout输出字符数组,也可以用printf()输出;
如果定义string s;
输入不能用gets(),只能用getline();
输出不能用printf(),只能用cout;
结合以上内容和自己的一些代码测试,我总结出以下几点:
(1) 用cin以及scanf输入时,结束输入后的那个换行(‘\n‘)或是空格(‘ ‘)都会停留在缓冲区;
(2) 用scanf输入字符(注意不是字符串)时,会自动“吃”停留在缓冲区的换行符或者空格,把它当作是你输入的字符;
(3) 用cin输入字符时,就不会“吃”缓冲区内的换行或是空格;
(4) 用cin或是scanf输入字符串时,前面的所有空格或是换行都不会被“吃”;
(5) 用gets()以及getline()输入时,会自动“吃”停留在缓冲区的字符,把它们当作是你输入的字符;
(6) gets()以及getline()结束输入之后的那个换行(‘\n‘),不会被放到缓冲区。
这样一想,以上的问题就都迎刃而解。
比如上文给出的第一个代码,它的while循环条件是同时输入两个字符串并且第一个字符串不为"END",所以在字典部分结束输入“END”后并不会立即跳出循环,而是在输入后面书部分的"START"后才会跳出while循环。用scanf输入完"START"后有一个换行符,这个换行符停留在了缓冲区,而后面紧接着的while循环就是用scanf输入字符(注意区分字符和字符串),于是这个缓冲区内的换行符就会被“吃”掉,它会以为这是你输入的第一个字符,于是后面就全错了。所以要在第一个while循环与第二个while循环之间加一个getchar(),把缓冲区内的这个换行符给“吃”了,这样后面的程序才不会被影响。
另外,我在自己写代码的时候曾经把这里的scanf("%c",&ch)换成了cin>>ch,自以为少写了几个字符,结果一看输出,全错了。注意看我上文总结的第三点,cin不会“吃”换行或是空格,于是原文中的换行和空格都不会被输出,这样的结果怎么能对呢?所以这边必须要用scanf来输入字符。
再看第二个代码,它的while循环条件是输入一个字符串并且这个字符串不为"END",于是在输入完"END"后,就立刻跳出了第一个while循环。接下来是用cin输入书的开始标志"START",可能有人会有疑问,为什么在跳出循环后,输入"START"前,不用加getchar()呢?可以看我上文总结的第四点,因为输入的是字符串,所以缓冲区的换行不会对它产生影响。但是输入完"START"后的换行被放在了缓冲区,而接下来就是用getline读取一行字符串。注意上文总结第五点,这个换行会被getline吃掉,导致后面输出出现错误。所以要在输入完"START"后,getline()前,放一个getchar(),来“吃”掉这个换行。
另外,我发现第二个代码有一点小缺陷,若是书的输入部分,每行结尾处没有标点符号,则输出错误,而第一个代码就没有这种问题,大家可以测试一下。
下面给出我看完两位dalao的代码,并且理解了getchar()的用意后,自己敲的还原两种方法的代码,加上了厚厚的注释。
【法一】
1 #include <iostream> 2 #include <string> 3 #include <cstring> 4 #include <algorithm> 5 #include <map> 6 using namespace std; 7 8 int main() 9 { 10 string start,eng,mar; 11 map<string,string> m; 12 cin>>start; 13 while(cin>>eng&&eng!="END"){ 14 cin>>mar; 15 m[mar] = eng; 16 } 17 cin>>start; 18 //重要:输入start之后有回车,回车停留在缓冲区,读取字符时会先读取这个回车,所以要getchar(); 19 getchar(); 20 char buf[1024]; 21 char ch; 22 int n = 0; 23 while(scanf("%c",&ch)){ 24 if(isalpha(ch)) 25 buf[n++] = ch; //如果输入的一直是字母,就把输入的字母依次放进buf 26 else{ 27 buf[n] = ‘\0‘; //当输入的不再是字母,就放进去一个结束符‘\0‘ 28 //strcmp: 若str1==str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。 29 if(strcmp(buf,"END")==0) break; //如果此时的buf是END即结束循环 因为用的字符数组所以要用到strcmp 30 if(m.count(buf)!=0) //在map中查找buf是否为关键字 如果是 就输出buf在map中对应的值(英文) 31 cout<<m[buf]; 32 else cout<<buf; //如果不是关键字就直接输出 33 cout<<ch; //输出这个非字母的字符(空格,换行,tab,标点...) 34 n = 0; //让n重新等于0 buf重新开始储存字符 35 //因为会遇到‘\0‘,所以即使buf不重新初始化也没关系,因为每次读取到‘\0‘就会停止 36 //当然最好还是初始化一下 37 } 38 } 39 40 return 0; 41 }
【法二】
1 //与法一相比有缺陷 若每行结尾没有标点符号则输出错误 2 #include <iostream> 3 #include <string> 4 #include <cstring> 5 #include <algorithm> 6 #include <map> 7 using namespace std; 8 9 int main() 10 { 11 string start,eng,mar; 12 map<string,string> m; 13 cin>>start; 14 while(cin>>eng>>mar&&eng!="END"){ 15 m[mar] = eng; 16 } 17 //因为第一个while里面是同时输入eng和mar,所以在输入END后不会立即跳出循环,而是在输出紧接着的START后才会跳出 18 getchar(); //输出start后有个回车,下面的getline会读取这个缓冲区内的回车,把它当作我们的第一个输出,所以要getchar() 19 string line; 20 while(getline(cin,line)&&line!="END"){ //重要: getline 敲完一行之后的回车不会放到缓冲区!!! 21 string str = ""; //相当于一个空串 22 for(int i=0;i<line.size();i++){ 23 if(isalpha(line[i])) str += line[i]; //如果是字母 就依次连接在str后面 24 else{ 25 if(m.find(str)!=m.end()) //用map的find函数查找str是否为一个键值 返回值是一个迭代器 返回的是被查找元素的位置,没有则返回map.end() 26 cout<<m[str]; 27 else cout<<str; //若不为一个键值,则原样输出 28 cout<<line[i]; //输出此处的不为字母的字符 (注意不包含换行) 29 str = ""; 30 } 31 } 32 cout<<endl; //手动换行2333 33 } 34 35 return 0; 36 }
那么这道题就算是彻底过去了,以前在输入字符、字符串时我从来没有考虑过缓冲区的问题,以后就要多加注意啦!
关于cin scanf 和 gets() getline() 的反思与总结
原文:https://www.cnblogs.com/Aikoin/p/9312953.html