都有哪些可以做app的网站,网站建设杭州公司,seo咨询顾问,宣传海报制作C语言中我们学习过文件IO的相关函数#xff0c;那么在C中也一定有各种IO流的
函数或者功能#xff0c;由我今天来简单介绍一下C中IO流的大致原理及使用。在C语言中我们经常会使用到scanf、printf、sscanf、sprintf等等来实现进程和文件之间数据的流动#xff0c;在C中虽然由…C语言中我们学习过文件IO的相关函数那么在C中也一定有各种IO流的
函数或者功能由我今天来简单介绍一下C中IO流的大致原理及使用。在C语言中我们经常会使用到scanf、printf、sscanf、sprintf等等来实现进程和文件之间数据的流动在C中虽然由于面向对象的特性使得C和C语言的文件IO不一样但他们都有IO流的说法那么说到很多次“流”这个字那么“流”究竟是什么
1. 流的简单认识
“流”即是流动的意思是物质从一处向另一处流动的过程是对一种有序连续且具有方
向性的数据 其单位可以是bit,byte,packet 的抽象描述。C流是指信息从外部输入
设备如键盘向计算机内部如内存输入和从内存向外部输出设备显示器输出
的过程。这种输入输出的过程被形象的比喻为“流”。
它的特性是有序连续、具有方向性。那我们们直接步入主题来认识C中的IO流
2. CIO流
a. CIO特点 C中的IO体系中使用了基类派生类的方式以一个最基本的IO方式来衍生出各种合适的IO方式。和C语言有区别的是C语言中是有着stdin、stdout、stderr。而C中有cin、cout、cerr、clog多了个clog它们的基本特性还是差不多例如cin和scanf一样默认以空格和换行作为隔断不同的是C语言中是格式化输入输出而C中没有这一直接功能而是需要使用函数来实现 比如这一函数就是用来控制浮点数的精度 这里后面出现了26是因为计算机存储浮点数精度的问题。 但是C中解决了一个问题那就是C语言无法对自定义类型有着很好的输出输入
class Student
{friend istream operator(istream in, Student st);friend ostream operator(ostream out, Student st);
private:string name;string id;int age;
};istream operator(istream in, Student st)
{in st.name st.id st.age;return in;
}ostream operator(ostream out, Student st)
{out 姓名 st.name endl 学号 st.id endl 年龄 st.age endl;return out;
}int main()
{Student s;cin s;cout s;return 0;
}这在C语言中写的可就不止这两行了。 还有在oj题中我们经常会遇到下面的代码
int main()
{int a 0;while (cin a){}return 0;
}可是我们知道流提取的返回值是ostream类型的但是我们while中判断的值直接判断的是bool或者整型值也可以作为判断的依据那么凭什么ostream类型能够作为判断依据呢这是因为 它的成员函数中重载了bool可以让ostream转为bool类型这也算是一种重载但是跟普通的重载不一样它没有返回值就可以实现这样的玩法 class Student
{friend istream operator(istream in, Student st);friend ostream operator(ostream out, Student st);public:operator bool(){if (name aaa)return false;else return true;}operator int(){return 1;}private:string name;string id;int age;
};istream operator(istream in, Student st)
{in st.name st.id st.age;return in;
}ostream operator(ostream out, Student st)
{out 姓名 st.name endl 学号 st.id endl 年龄 st.age endl;return out;
}int main()
{Student st;while(cin st){if (st)cout hello world endl;else{int a st;cout a endl;}}return 0;
}b. C文件IO
跟C语言一样对于文件IO操作都是以f开头。 关于文件IO有两种方式一种是二进读写一种是文本读写二进制读写是当你写到文件的数据形式是二进制无法直接观看而文本读写就是以字符串的方式来写入文件中可以直接观察 我们使用一个类来封装一下文件IO
class Student
{friend istream operator(istream in, Student st);friend ostream operator(ostream out, Student st);public:operator bool(){if (name aaa)return false;else return true;}operator int(){return 1;}//private:char name[10];char id[20];int age;
};istream operator(istream in, Student st)
{in st.name st.id st.age;return in;
}ostream operator(ostream out, Student st)
{out 姓名 st.name endl 学号 st.id endl 年龄 st.age endl;return out;
}class FileIO
{
public:FileIO(string s):_filename(s){}void BinRead(Student st){ifstream ifs(_filename, ios_base::in | ios_base::binary);ifs.read((char*)st, sizeof(st));}void BinWrite(Student st){ofstream ofs(_filename, ios_base::out | ios_base::binary);ofs.write((const char*)st, sizeof(st));}void TextRead(Student st){ifstream ifs(_filename);ifs st.name st.id st.age;}void TextWrite(Student st){ofstream ofs(_filename);ofs st.name endl st.id endl st.age;}private:string _filename;
};int main()
{FileIO fio1(test1.txt);Student st, st1, st2;cin st;fio1.BinWrite(st);fio1.BinRead(st1);cout st1 endl;FileIO fio2(test2.txt);cin st;fio2.TextWrite(st);fio2.TextRead(st2);cout st2 endl;return 0;
}有人可能注意到我对Student类做了一些小改动我改变了它的成员变量将string变成了char的数组那我们假如使用string呢 VS2022下 文件中 这与我们刚开始的结果并不一样。这里明显多了一些东西那假如我们重新运行程序只进行读呢 存的东西再长一些呢 直接崩了
这是因为自定义类型数组是在栈上在二进制读写的时候就是把自己数组的内容直接写进文件中了但是自定义类型string不一样它的内部结构我们大致直到应该是一个char*、size_t、size_t类型的他的字符串是存在堆上的我们要进行写的时候会把string中的char*的地址写到文件中两个整形写到文件中。
如果我们这个时候结束程序系统会回收我们的资源但是我们文件中存的还是那个地址那么再次打开程序读那个地址发生访问越界问题那自然就崩了。
而在同一程序下进行读写的时候则是因为同一进程还没有回收资源那个地址指向的仍然是我们的字符串那为什么还是崩了呢这个是因为发生了浅拷贝的原因st和st1中的string指向的地址是同一个在main函数结束的时候会释放两次所以还是会崩。 还有一个问题那为什么短一点的字符串看起来没有上面的在两个进程下读写崩了呢那是因为VS编译器中的string中有一段自己的缓存区。 所以使用二进制读写时尽量不要用容器
c. 内存间IO
我们在C语言中如果要将一个整形转成字符串或者字符串转成整形会怎么办呢我们可能会使用函数atoi、itoa但是我们还有一种方法就是使用ssprintf、sscanf 负数也可以 而在C中自然也可以有这种方式 那就是sstream。
1). 字符串整数之间转换
int main()
{//整数转字符串int a 123456;string s;stringstream ssm;ssm a;ssm s;cout s endl;//字符串转整数ssm.clear();ssm s;int b 0;ssm b;cout b endl;return 0;
}clear()注意多次转换时必须使用clear将上次转换状态清空掉 stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换但是clear()不会将stringstreams底层字符串清空掉s.str();
将stringstream底层管理string对象设置成, 否则多次转换时会将结果全部累积在底
层string对象中2). 字符串拼接
int main()
{string s1 hello;string s2 world;stringstream ssm;ssm s1;ssm s2;string s3;ssm s3;cout s3 endl;return 0;
}3). 结构化数据
class Student
{friend istream operator(istream in, Student st);friend ostream operator(ostream out, Student st);
public:Student(string name , string id , int age 0):_name(name),_id(id),_age(age){}string _name;string _id;int _age;
};istream operator(istream in, Student st)
{in st._name st._id st._age;return in;
}ostream operator(ostream out, Student st)
{out 姓名 st._name endl 学号 st._id endl 年龄 st._age endl;return out;
}int main()
{Student st(张三, 2024, 12);stringstream ssm;ssm st._name st._id st._age;Student st1;ssm st1._name st1._id st1._age;cout st1 endl;return 0;
}4). 小点总结
stringstream实际是在其底层维护了一个string类型的对象用来保存结果。多次数据类型转化时一定要用clear()来清空才能正确转化但clear()不会将 stringstream底层的string对象清空。可以使用s. str(“”)方法将底层string对象设置为空字符串。可以使用s.str()将让stringstream返回其底层的string对象。stringstream使用string类对象代替字符数组可以避免缓冲区溢出的危险而且其会对参 数类型进行推演不需要格式化控制也不会出现格式化失败的风险因此使用更方便更 安全。