int getchar(void);
一次读入一个字符并作为返回值返回。因为返回的实际是个ascii码,所以只要是ascii码表里有的字符都能读。包括空格,换行 ‘\n’,Tab '\t’等分隔符。
getchar不要拿来读入换行符来进行行末的判断。因为表示一行终止的符号不止有\n(换行),还有\r(回车),这是个历史遗留问题。
不同的操作系统对换行字符的规定不太一样,所以循环getchar然后判断’\n’停止可能导致不停读入导致爆字符数组,然后RE(或者其他奇奇怪怪的错误)。
char *gets(char *str);
可以读入一行字符串,到换行符和文件末尾(EOF)为止,会读入换行符,自动在字符末尾后加入\0,读入成功会返回首个字符首地址str,否则返回null
因为安全性不高,C11之后被移除,洛谷的编译器版本C++14是不支持的(提交前可以用洛谷提供的IDE运行一遍,可以排除兼容性问题)
char *gets_s(char *str,int count)
C11后用于代替gets,功能和用法与gets相同,不过到达count-1长度就会直接结束读入(补上\0正好占用count个空间)
char *fgets(char *str,int count,FILE *stdin)
作用与gets_s相似,可以选择输入文件流,如果还是标准输入流就写stdin就行了
istream& getline (char* s, streamsize n, char delim='\n')
delim可以省略,默认是’\n’(也就是默认遇到’\n’就结束读入),会读入delim字符但是不会放进字符数组里(也就是会把光标移动到delim字符后面,而不是停留在这上面,下次读入从delim下一个字符开始读取),n表示读入的最大的字符串长度,包括字符串结束标识符\0。
这个getline作为istream对象的一个成员函数,调用需要有明确的对象来调用,比如:
cin.getline(ch,100);
istream& getline (istream& is, string& str, char delim='\n')
和上面的getline差不多,delim可以省略,默认遇到’\n’就结束读入。这里是cstring里对上面getline函数进行了重载(好像也不能算重载),这里的getline作为istream的一个友元函数,不需要使用具体的对象来调用。但是参数表要求提供一个istream&类型的参数(标准输入流给cin就行了),比如:
getline(cin,s)
scanf("%c",&ch)
和getchar差不多,一次一个字符,会读入分隔符比如空格和换行,读到的字符直接赋值给字符ch。scanf本身有返回值,如果读入成功就返回读入成功的个数,失败时返回-1。
但是! 如果你往scanf的格式字符串(就是引号中的字符串)里加入空格,它就会变成一个通配符一样的东西,会“吃掉”0个或多个连续的分隔符。比如:
第一个scanf的格式串读入a和b中间有个空格,但是它实际上可以吃掉多个空格(tab和换行符等分隔符也是可以的),其他的字符却没有这样的效果,只会跳过单个这样的字符,而且如果实际读入过程中如果这里没有给出这个字符就会出错。如果读入单个字符时光标正好指向分隔符的话,还是会读入分隔符的。
scanf("%s",ch)
ch是一个字符数组的首地址,它会连续读入字符串直到读到分隔符。有几个细节:
另外scanf也可以利用c_str()实现对string的读入,不过十分不建议这样用。
我个人猜测:string存储字符串也是使用的字符数组,string开辟一块可变长度的连续空间(类似于vector<char>),本身保存字符数组的首地址来进行访问和修改。string是一种对象,char* 是一个字符串指针,编译器不会把它们当作一种东西,所以参数表里两者不互通,不能混用。
不过string里提供了一个直接访问字符数组的办法(函数),也就是c_str(),在printf中我们可以用它来进行输出,一些需要传递char*的场合也可以通过c_str()来传递string。但是!这样是有危险的
因为传递走char*后,函数所作的所有操作都是直接针对这一块空间上的,string完全不知道发生了什么,就有可能发生一些错误,比如我们使用scanf来向string里读入,就有可能喜提CE,RE,WA全家桶。举个例子:
string对象s 一开始没有分配空间,直接读入导致数据丢失。分配了一些空间后,读入时直接从字符首地址开始覆盖,然后末尾加\0,但是string还是按照自己认为的字符串原本的长度进行输出。如果你读入的字符长度远大于string分配的空间,在触及到一些非法内存时,就会RE。因此不建议使用scanf向string读入。
cin>>ch
ch可以是一个 字符数组的首地址 或 字符,也就是char* 和char&的区别,两者的细节基本一致,即:
可以看到cin读入单个字符的时候和scanf是不一样的,最主要的区别是cin不会读入分隔符,而scanf会,所以cin的这个机制有时候就会比较有用
比如给了你一个01串,你就是想一个字符一个字符读入,那你就用cin,而用scanf会比较麻烦。
简单提一嘴int sscanf(const char *str, const char *format, ...)
这个。
它是从字符串中去读取,作用和scanf几乎一致,只不过是从char* str读取而不是标准输入流,ssprintf同理,是向字符数组中输出。
其实sstream里的istreamstring和ostreamstring实现的功能也和它们差不多,想了解的可以自行查阅资料,这里不多赘述。
读入一整行一般用getline,读入单个字符或者一个词块就用cin,没了。
其实我个人不太建议使用那三种gets,理由如下:
因为字符数组实际上传递的都是首地址指针,所以不一定只能写数组名,还能这样:cin>>ch+1
。
表示从ch[1]开始存放字符串。
参考:
菜鸟教程scanf
scanf(个人推荐)
菜鸟教程printf(个人推荐)
scanf格式说明符一般形式为:“%[*] [输入数据宽度] [长度] 类型”
方括号包括的选项为可选项。可以不加。
比如:
ld表示long int
llu表示unsigned long long int(也就是unsigned long long)
lf表示double(双精度浮点型,long float的感觉)
用十进制整数指定输入的宽度(即字符数),比如%4d就只看四个字符的宽度,输入12345678,只会读入1234。
用以表示该输入项,读入后不赋予相应的变量,即跳过该输入值。
除了上面的常规用法,还有一个扫描字符集合的用法
一般格式是:“%[*] [输入数据宽度] [字符集合] \color{red}\text{[字符集合]} [字符集合]”
前面两个方括号是可选项,作用和上面说的一致。后面红色标红的为字符集合(个人感觉这个东西类似于%s里的那个s)。这里的这对 方括号不能省略。
scanf 函数的格式说明字符串中 %[] 是一个扫描集合,用于匹配一组字符。当使用 %[] 时,你可以指定一个字符集合,scanf 将会读取输入直到遇到不在指定集合中的字符为止。
用法如下:
使用例:
sscanf 和 scanf 中的格式说明字符串的用法是一致的。
printf格式说明符一般形式为:% [*][ flags ][ width ][ .precision ][ length ] specifier
flag标志,width宽度,precision精度,length长度,specifier类型(直译规范)。
同样的方括号包括的选项为可选项。可以不加。
注:
输出八进制数和十六进制数的时候是直接把整数补码输出,比如int的-1按十六进制来输出,会得到ffffffff。
有些输出格式会输出字母,如果你格式控制符使用大写,输出时就会使用大写字母。比如%X,%E,%G,%A
用十进制整数指定输出的宽度(即字符数),比如%4d就输出四个字符的宽度,超长的话就不管宽度了,继续输出。
一般用在浮点数中,效果如下:
使用例:
上面g舍入后会消除末尾无效的0
在精度设置方面,cout的setprecision()也可以实现差不多的功能(至少我看不出区别,但是有些b题用cout是对的,有的用printf是对的,邪门的很,尤其是01分数规划那玩意)
要用cout设置精度首先要包含 iomanip头文件(io+manipulator),里面有两个manipulator流操纵器,分别是fixed(小数形式)和scientific(科学计数法形式,指数形式)。另外还有setprecision(precision)可以控制输出位数。用法如下:
cout<<fixed<<precision(2)<<f;
cout<<scientific<<precision(2)<<f;
fixed相当于printf中的%f,scientific相当于%e,一般模式相当于%g。setprecision相当于在设置精度。(所以设置了precision后,一般模式该不输出末尾无效0仍然不会输出)。
欢迎勘误
string
不写了,写的太多了,之后写个STL,扔那里面得了
string用法:
总结:
string读入和输出建议用cin和cout,非要用printf输出可以用s.c_str()
即printf("%s",s.c_str());
string的+操作就是在后面接上另一个string或字符
+=和+复杂度不一样,+=是在原来的基础上添加所以快,+会创建一个中间变量来存放,慢!
string的判断操作<,>,==,!=
大小比较是按照字典序大小来的
string s,s1;//s是母串,四种操作,start不是地址,可以当作是数组下标
s.insert(start,dis,s1);
s.erase(start,dis);
s.clear();
s.replace(start,dis,s1);
string s,s1; //s是母串,查找操作
s.find(s1,start); //从start位置向后寻找s1子串
s.rfind(s1,start); //从start位置向前寻找s1子串
s.find_first_of(s1,start)
//从start位置向后查找s1集合包含的字符,返回找到的第一个字符的数组下标(位置)
s.find_last_of(s1,start)
//从start位置向前查找s1集合包含的字符,返回找到的第一个字符的数组下标(位置)
s.find_first_not_of(s1,start)
//从start位置向后查找s1集合不包含的字符,返回找到的第一个字符的数组下标(位置)
s.find_last_not_of(s1,start)
//从start位置向前查找s1集合不包含的字符,返回找到的第一个字符的数组下标(位置)