赞
踩
碎碎念:因为csp他居然是用标准输入输出的,cin这东西,好几年没用过的东西了,于是去搜下回顾,发现很多文章都说的不清不楚的,到头还是得自己测试自己翻文档,于是给自己写一份详细到不需要再去测试的笔记。DEVCPP这个老古董的用法我就不给以后的自己更了,我肯定再也不用这个了,论小巧感觉不如notepad++,正常写不如vs,rider,尤其他太小巧没有相关优化的话去跑个ue源码肯定当场死机。快进到让玩家拿记事本写,公平竞技(划掉)
标准I/O:只说cin。头文件<iostream>
cin:cin不是函数,函数是>>,全名operator>>,cin是istream类的一个实例,是全局对象,不过提供了std名称空间保护。除非小测试不建议直接using namespace XXX;容易混淆自己手搓的版本、借用大佬的版本、std的版本,会让可读性极差。
- __PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT istream cin;
-
- basic_istream& __CLR_OR_THIS_CALL operator>>(int& _Val) {
- //同时重载了unsigned short& _Val、unsigned int& _Val、long& _Val、unsigned long& _Val、long long& _Val、unsigned long long& _Val、float& _Val、double& _Val、long double& _Val、void*& _Val、_Mysb* _Strbuf
- //其中using _Mysb = basic_streambuf<_Elem, _Traits>;
-
- //basic_streambuf是管理读写缓存的类
正如上面的函数声明,可以直接>>,传参需要一个几乎任意的左值,不支持的你给他重载一个就支持了(废话)
cin的多数调用(少数情况会直接拿缓冲区的数据,不进行请求,下面有说)会从标准输入(通常是键盘)中请求输入,输入完按回车键后,数据(包含最后的回车键)被送入键盘缓冲区。
1、cin>>val行为:
请求输入,从缓冲区中读取数据到val,跳过开头的 [空格] 、\t[制表符] 、\n[换行符],然后直到遇到 [空格] 、\t[制表符] 、\n[换行符],结束读取但不删除缓冲区中最后遇到的这个3贵物之一。(当然之前的已读取部分会从缓冲区删掉,只读不删有cin.peek()函数,这里不讲)。 碎碎念:测试时差点搞错了,它读到 空格\t\n 后没有删贵物,是之后的>>跳过了开头的贵物,还好同时看了网上大佬的结论。
如果缓冲区内此时还有数据,而此时你调用了>>,——cin会不进行输入请求,直接读取缓冲区的数据,读取过程同上。你不想让他这样?可以提前调用cin.sync();清空缓冲区。如果缓冲区内只有空格\t\n,它跳过开头的空格\t\n后发现没有数据,还是会请求输入的。
如果输入的数据不是val的数据类型,cin会卡住不向val输入错数据,同时cin的值==0(false);正常跑的过程中cin的值==1(true);有这个行为所以可以这样写:if(!cin){} 或 while(cin>>val){}
2、cin.get()行为:
请求输入,从缓冲区中get,get到哪?管他呢,数据,读取!无参数版本默认只读一个字符。
不管什么数值他都读(除了EOF,但凡读到了EOF直接吓得返回-1,所以可以这样while((c=cin.get())!=EOF) 。不同系统的EOF不同,windows系统是Ctrl+Z,实值通常为-1)
但是,我数据呢?哦,他只管读,不管什么数值他都读,读完的数据就是这条表达式的值,你大概应该这样:int val = cin.get();char c = cin.get();这个他只会读取一个字符就停。别问为什么,我也不知道为什么,标准就这么定的?
如果赋值过程中,类型不对赋值失,,,,成功!不愧是.get(),管他呢!于是——int b = std::cin.get();输入了’a‘,输出b的值为97,建议单纯当字符处理,后续自己转类型。
只读取一个字符就停。用int接受也是,double也是。他不接受char*,而且数据会被当成字符,值就是你所用字符集的码表值。(回车键也读 \n在ASCII表值为10)
另一种写法:cin.get(type& val);行为同上,cin.get(c);行为同上
3、cin.get(字符串指针,字符个数n,终止字符):
只能用来赋给字符串。
请求输入,从缓冲区读取n-1个字符,他自己偷偷摸摸加一个\0给凑到n个,缓冲区有多的就不管只要n-1个,赋给指定的数组,如果在读取n-1个字符之前遇到传参给它的终止字符或没有传参的话默认等于\n换行符,则提前结束读取。
哇,我测试回来了,这家伙是魅魔,输不够n-1个字符它不撒手的,自定义了终止字符的话它\n也吃,吃吃吃吃吃吃。
window10系统VS2022,c++17,如果抓到了终止字符才停止的,表达式返回1,如果是吃够了n-1个字符,表达式返回1;总之,通通返回1。
不知道啥系统,不知道啥标准,网上别人的,如果读取成功则函数返回非0值(真),如失败(遇文件结束符)则函数返回0值(假)。
4、cin.getline(字符串指针,字符个数n,终止字符):
和3、的一模一样,完全一致。目前我没找到有什么不一样的。
删了终止字符,缓冲区里面就没了这一个终止字符。吃满n-1个字符表达式返回0;遇到/n提前终止表达式返回1;返回值这里很怪,不做评价,应该是c++标准没有约定,不同系统编辑器各个放飞自我了。不过删了\n是挺舒服,但我传参了的终止字符你也给删了就很坑爹。
IO的O?cout<<,感觉经常用,就这个<<了,够用了!不看它了。
在cppreference中的cin.read()内部成员函数中(read和get差不多,差不多)有这样的说法:使用非转换的本地环境时(默认本地环境为非转换),此函数在 std::basic_ifstream 中的覆写者可以为零复制的大块 I/O 优化(通过覆写 std::streambuf::xsgetn )。如果毕业之后工作需要做大块IO优化再看吧,感兴趣的可以自己去看。
文件IO:只说ifstream、ofstream 头文件<fstream> (fstream可以一个类同时支持读写,但我不说)
我随便就搜到一篇大佬写的非常好的C++(十六)文件IO操作(读写)_c++文件io读写_SongpingWang的博客-CSDN博客
我就给未来的自己写点拍脑门经验书,内容看着乱还多不想看的去看大佬的吧
ifstream:进来进来in,我家还蛮大的(内存是第一视角
读写文件的类没有自带全局对象,因为需要指定特定目录而不是用操作系统给的默认设备。
ifstream构造函数:
basic_ifstream();做一些准备工作,比如std::basic_filebuf 成员基类。不实际做文件关联部分的初始化。std::ifstream a; char b[30];a.getline(b, 20);log(b); 虽然以上它不报错甚至能运行(无实际行为运行),但请不要这么做,迷惑
explicit basic_ifstream( const char* filename,
std::ios_base::openmode mode= std::ios_base::in );直接以输入的文件路径构造,默认在当前项目位置.project位置为相对路径查找,"./x64/test.txt"一个点+正斜杠开启正向相对路径,"../x64/test.txt"两个点+正斜杠开启反向回去一个路径后再正向查找,"../../x64/test.txt"叛逆一层路径不够的话就再来一发。 绝对路径是两个正斜杠"F://_个人自制素材库//c++//杂乱的各种cpp练习//test.txt"。在调试中我打满了断点,一般的字符串路径都是确定的跳入的这个而非下面的带path的参数。
第二个参数是打开类型,默认为in,有1、app每次写入前寻位到流结尾 2、binary 以二进制模式打开 3、in 为读打开 4、out 为写打开 5、trunc 在打开时舍弃流的内容 6、ate 打开后立即寻位到流结尾 7、noreplace (C++23) 以独占模式打开.....你一定很好奇这是个ifstream但是却可以输入out打开类型(我是用来读的,但我有写模式)我试了试,依然可以运行读出文件数据,但是我发现ifstream根本没有用来写文件的成员函数,我的评价是——摆设~杂鱼~(划掉,c++委员会看来还是当人的。使用起来就是std::ifstream aa("wahaha.txt");
explicit basic_ifstream( const std::string& filename,
std::ios_base::openmode mode= std::ios_base::in );C++11 特性
explicit basic_ifstream( const std::filesystem::path::value_type* filename,
std::ios_base::openmode mode= std::ios_base::in );C++17 特性,允许奇奇怪怪类型的文件名。和上一个基本等同。是个重载,你就当不存在。用的爽就完事了
basic_ifstream( basic_ifstream&& other );C++11 特性
basic_ifstream( const basic_ifstream& rhs) = delete;C++11 特性,总结:只允许右值构造。但平时用不到管他呢。
bool is_open() const;检查文件流是否有关联文件,与调用 rdbuf()->is_open() 的效果相同。因为构造函数没有返回值,你没办法凭空确定 std::basic_filebuf::open 啥情况,有没有设置 setstate(failbit),需要再调一次确定,很麻烦但必要。
void open( const char* filename,
std::ios_base::openmode mode= std::ios_base::in );以及其他针对filename的重载,用来打开。。。不是已经打开了?我想不通为啥会有人用这玩意,建议把basic_ifstream();和void open(xxxxxxx)删了(doge
void close();关闭关联文件,等效于调用 rdbuf()->close() 。操作期间出现错误,则调用 setstate(failbit) 。然鹅并没有提供给你关闭成功的判断,建议别关了关联文件再关联个别的,建议殉情。此函数为 basic_ifstream 的析构函数在流对象离开作用域时调用,通常不直接调用。那些强烈建议你一定要close的人肯定是代码习惯不好吃了奇怪的坑。记得及时殉情别占着文件不撒就行,局部性好好的那就没问题。
(补充,我后面看到了ios基类里提供了关闭成功的判断,流本身是否正常的判断,还有其他的,很多:good检查是否没有发生错误,例如是否可执行输入/输出操作。eof检查是否到达了文件末尾。fail检查是否发生了可恢复的错误。bad检查是否已发生不可恢复的错误。operator!检查是否有错误发生(fail() 的同义词)。。所以不殉情也行
basic_ifstream& operator=( basic_ifstream&& other );C++11 特性,简单说,是你印象中的=号。等效地移动赋值 std::basic_istream 基类和关联的 std::basic_filebuf ,并将 other 置为无关联文件的类。
而关于输入到内存的操作——你知道他的输入函数在哪吗,没错他继承自 std::basic_istream,是不是很眼熟,没错这个就是上面的标准IO的类,这也是为什么我把他们放到一起来说,为了偷懒(蹲防
简单说,标准IO和文件IO的输入接口是同一个,他们的行为也是完全相同的,如常用的operator>>,get,getline。这个设计很厉害呢,他们接口都一样,但buf不一样,他的类说明是:basic_istream包装给定的抽象设备(std::basic_streambuf)并提供高层输入接口,也就是说你也可以用他的现成的东西去套一个你自己的类。快点写完打游戏了,废话少点吧
还有不常用的函数所以没说的一些,为什么不常用呢,因为你可以自己在最上层动动手达到同样的效果,没必要棍棒铜锏七百门兵器样样精通。 不常用:peek读取下一个字符,但不会提取它 unget撤销上一个字符的提取 putback往输入流中放置一个字符 ignore持续提取并丢弃字符,直到找到给定字符 read 提取一块字符 readsome提取可用的若干块字符 gcount返回上次无格式输入操作提取的字符数量 。这些
常用的那些,详细内容请看上面~~完啦撒花,啊,怎么还有
ofstream:出去出去out,去文件呆着吧(内存是第一视角
构造函数:不能说非常相似,只能说一模一样——
basic_ofstream();
explicit basic_ofstream( const char* filename,
std::ios_base::openmode mode= std::ios_base::out );
explicit basic_ofstream( const std::filesystem::path::value_type* filename,
std::ios_base::openmode mode= std::ios_base::out ); C++17 特效
explicit basic_ofstream( const std::string& filename,
std::ios_base::openmode mode= std::ios_base::out ); C++11 特效
template< class FsPath >
explicit basic_ofstream( const FsPath& filename,
std::ios_base::openmode mode= std::ios_base::out ); C++17 特效
basic_ofstream( basic_ofstream&& other );
basic_ofstream( const basic_ofstream& rhs) = delete; C++11特性
然后是检查函数:不能说非常相似,只能说是一模一样——
bool is_open() const;检查流是否有关联文件
void open( const char* filename, //及其四个重载,支持各种奇奇怪怪的文件路径名
std::ios_base::openmode mode= std::ios_base::out );打开文件,并将它与流关联
void close();关闭关联文件,此函数为 basic_ofstream 的析构函数在流对象离开作用域时调用,通常不直接调用。
然后是输出:233333跟我上面的cout一样,基本上就只用<<就行了。
basic_ostream& operator<<( bool value );插入带格式数据,一共有20种重载(截止c++23标准),总之啥都往里灌,用着爽就行了。
basic_ostream& put( char_type ch );输出单个字符,无格式输出
basic_ostream& write( const char_type* s, std::streamsize count );输出字符串,无格式输出
pos_type tellp();返回当前关联的 streambuf 对象的输出位置指示器。若 fail()==true (ios基类里的流正常与否的判断),则返回 pos_type(-1) 。否则,返回 rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::out) 。这啥,我不知道啊,应该是指示streambuf里设备位置,下次不用找了什么的,用来优化性能的吧,有需要的时候再查吧
basic_ostream& seekp( pos_type pos );
basic_ostream& seekp( off_type off, std::ios_base::seekdir dir );设置输出位置指示器,有需要再查。查个锤子,我瞬间就回来了,这个不是直接操作设备指针(磁盘指针?),这个是操作缓冲区的指针,然后缓冲区再同步过去,相当于远古版本迭代器。太老破了。
tellp()直接返回当前指针位置,可以随意加减指针(别说是我说的)。 a.tellp() += 5; a.seekp(a.tellp() , 5);我这么瞎写都不报错。
seekp的第一个重载是将当前指针位置挪到pos处。
seekp的第二个重载是将当前指针位置挪到dir加off处。off可以是负数,往左漂移,刷刷刷~
偏移常量: std::ios_base::beg 流的开始。std::ios_base::end 流的结尾。std::ios_base::cur 流位置指示器的当前位置 这些常量就和用迭代器的常量的一样到处扔都行。
顺带一提ifstream类里面也有这些,但是名字改叫seekg、tellg,内容是一样的。。因为那会想着摸鱼就没看(
basic_ostream& flush();与底层存储设备同步。如果 rdbuf() 是空指针,那么不会构造 sentry 对象。否则,在构造并检查 sentry 对象后会调用 rdbuf()->pubsync()。如果该调用返回 -1,那么就会调用setstate(badbit)。我看不懂,文档里是这么说的,复制过来了,大概就是有设备关联的话就挑逗挑逗有反应没,同步失败就把流设置为失效,有反应就同步一下数据。大概。
ifstream中也有数据同步,名叫int sync();成功时返回 0,失败时或若流不支持此操作(无缓冲)时返回 -1。
好啦,就这样!(拍你脑门
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。