c++笔记08---I/O 流,格式化 I/O,非格式化 I/O,随机 I/O,二进制 I/O

来源:互联网 发布:火电厂仿真软件 编辑:程序博客网 时间:2024/05/02 08:24
1.    输入输出 I/O 流        
    C: fopen/fclose/fread/fwrite/fprintf/fscanf/fseek/ftell/fput/fget...
    C++: 对基本的 I/O 操作做了类的封装,其功能没有任何差别,用法也相似;
    
2.    格式化 I/O 流:<< / >>
        #include <iostream>
        #include <fstream>                        // 打开文件
        using namespace std;
        int main() {
            // 写文件:
            ofstream ofs("hello.txt");            // 创建文件,类似于 fopen 用 w 模式,只读且清空
            // ofs 在 ofstream 的构造函数里面被构造,打开成功返回 ture,失败返回 false,是 bool 型;
            if(! ofs) {
                perror("error");                    // perror 打印出错信息;
                return -1;
            }
            ofs << "hello word!"  << endl;        // hello word! 被输入文件 hello.txt 里;
            ofs.close();

            ofs.open("hello.txt", ios::app);    // 打开文件,追加内容,类似于 fopen 的 a 模式
            if(! ofs) {
                perror("error");
                return -1;
            }
            ofs << "another\n";
            ofs.close();
            // 读文件:
            ifstream ifs("hello.txt");
            if(! ifs) {
                perror("error");
                return -1;
            }
            steing s1, s2;
            ifs >> s1 >> s2;                        // 读入上面输入的两个字符串;以空白字符作为分隔(空格、换行、制表);
            return 0;
        }
        
        ios::app    以追加的方式打开文件;
        ios::ate    文件打开后定位到文件尾;
        ios::binary    以二进制方式打开文件;缺省是文本方式;
        ios::in        以输入方式打开;
        ios::out        以输出方式打开;
        ios::nocreate    不建立文件,所以文件不存在时,打开失败;
        ios::noreplace    不覆盖文件,所以打开文件时,如果文件存在则失败;
        ios::trunc        如果文件存在,把文件长度设为 0;
        ios::beg            移动到文件头;
        ios::cure            移动到文件当前位置;
        ios::end            移动到文件尾;
        可以用 或 把以上文件属性连接起来:
        
    
3.    非格式化 I/O 流:put/get
        #include <iostream>
        #include <fstream>
        using namespace std;
        int mian() {
            // 输入
            ofstream ofs("putget.txt");
            if(! ofs) {
                perror("error");
                return -1;
            }
            for(char c = ' '; c <= '~'; ++c)    // 把 ASIIC 表里的字符从第一个到最后一个写入;
            // 前++返回引用,后++返回副本,所以建议用前++,效率高;
                if(! ofs.put(c)) {
                    preeor("error");
                    return -1;
                }
            ofs.close();
            // 输出
            ifstream ifs("putget.txt");
            if(! ifs) {
                perror("error");
                return -1;
            }
            char c;
            while((c = ifs.get()) != EOF)        // 因为 ASIIC 里没有 EOF,所以用 EOF 作判断;
            // 无论是读取失败或者读完文件,都返回 EOF,所以下面加 if 语句判断是否出错;
                cout << c;
            if(ifs.error()) {                        // error 判断读取是否出错
            // 相当于:if(! ifs.eof()) {            // eof 判断是否到文件尾;        
                perror("error");
                return -1;
            }
            ifs.close();
            return 0;
        }
        
        EOF:成员函数 eof();用来检测是否到达文件尾;
        如果到达文件尾,返回非 0;否则返回 0;

4    随机 I/O 流:seekp/seekg, tellp/tellg(文件指针)
        #include <iostream>
        #include <fstream>
        using namespace std;
        int mian() {
            // 输入
            fstream fs("seek.txt", ios::in | ios::out);    // 可读可写,相当于:w+
            if(! ofs) {
                perror("error");
                return -1;
            }
            fs << "0123456789";
            cout << fs.tellp() << endl;
            cout << fs.tellg() << endl;
            fs.seekp(-3, ios::cur);                // 当前位置往前挪 3 个位置
            fs << "xyz";                            // 0123456xyz(文件默认操作只有覆盖,没有插入)
            fs.seekg(4, ios::beg);                // 从文件头开始挪;
            int i;
            fs >> i;
            cout << i << endl;
            cout << fs.tellg() << endl;
            cout << fs.tellp() << endl;
            fs.seekg(-6, ios::end);                // 从文件尾开始
            fs << "abc";
            return 0;
        }
        这里只打开一次文件,可以不用 fs.close(),析构函数会自己析构;
        但如果向上面的例子,多次打开同一个文件,则必须手动关闭;
        
        seekg ()    设置读位置;
        seekp ()    设置写位置;
        tellg ()    返回流中 get 指针当前位置;
        tellp ()    返回流中 put 指针当前位置;
        
5.    二进制 I/O 流:read/write

    read 读取数据,成功返回读取的字节数;出错返回 -1 并设置 error;
    如果在调用 read 之前已经到文件尾,则返回 0;
    fread 和 read 区别:
    1)fread 是 c 语言的库,而 read 是系统调用;
    2)read 每次读取的数据是用户要求的大小,fread 为了加快读取速度,会读取一个缓冲区大小,读到的内容放入缓冲区;
    在 32 位机器下,一般是 4096 个字节;
    
    write 写入数据,成功返回写入的字节数;出错返回 -1 并设置 error;

    实现文件加解密:
    原理:运用异或实现加解密
    
        #include <iostream>
        #include <fstream>
        #include <stdexcept>
        #include <cstdlib>
        using namespace std;
        #define BUFSIZE (1024*10)                // 缓冲区
        int _xor(const char* src, const char* dst, unsigned char key) {    // 加解密
            ifstream ifs(src, ios::binary);        // 二进制方式创建,window 有区别,linux 无区别
            if(! ifs) {
                perror("write error");
                return -1;
            }
            ofstream ofs(dst, ios::binary);
            if(! ofs) {
                perror("read error");
                return -1;
            }
            char* buf = NULL;
            try {
                buf = new char[BUFSIZE];            // 放入缓冲区,日后作比较用
            }
            catch (bad_alloc& ex) {
                cout << ex.what() << endl;
                return -1;
            }
            while(ifs.read(buf, BUFSIZE)) {
            // BUFSIZE 期望值,如果读到的比期望字节少返回 false,一样返回 ture
                for(size_t i = 0; i < BUFSIZE; ++i)
                    buf[i] ^= key;
                ofs.write(buf, BUFSIZE);
                if(! ofs.write(uf, BUFSIZE)) {
                    perror("write error");
                    return -1;
                }
            }
            if(! ifs.eof()) {(char*)&dog
                perror("read error");
                return -1;
            }
            for(size_t i = 0; i < ifs.gcount(); ++i)
                buf[i] ^= key;
            if(! ofs.write(uf, ifs.gcount()) {
                    perror("write error");
                    return -1;
            }
        }
        int enc(const char* plain, const charI cipher) {
            srand(time(NULL));                            // 随机因子
            unsigned char key = rand() % 256;            // 取出随机数
            if(_xor(plain, cipher,key) == -1)
                return -1;
            cout << "秘匙 :" << (unsigned int)key << endl;
            return 0;
        }
        int dec(const char* cipher, const char* plain, unsigned char key) {
            return _xor(cipher,plain,key);
        }
        int main(int argc, char* argv[]) {
            if(argc < 3) {
                cerr << "用法:" << argv[0]
                    << "明文文件/密文文件" >> endl;
                cerr << "用法:" << argv[0]
                    << "密文文件/明文文件/秘匙" >> endl;
                return -1;
            }
            if(argc < 4)
                return enc(argv[1], argv[2]);
            else
                return dec(argv[1], argv[2], atoi[3])
            return 0;
        }

6.    格式控制 I/O 流
    方法一:通过流函数实现;
    方法二:流控制符;需要包含头文件 #include <iomanip>
    #include <iomanip>    
    #include <cmath>
    cout << sqrt(2);                                        // 输出 2 的平方根;默认输出六位;
    cout.precision(10);                                    // 流函数实现输出 10 位有效数字;对下面的所有语句都有作用;
    cout << sqrt(3);                                        // 输出 10 位有效数字;
    cout << setprecision(5) << sqrt(2);                    // 改变精度,输出 5 位有效数字;
    cout << cout.precision();                            // 返回当前精度;输出:5
    cout << setprecision(2) << 1.25 << 1.26;            // 前者输出:1.2,后者输出:1.3
    cout << showbase << hex << 127;                        // 十六进制
    cout << oct << 127;                                    // 八进制
    cout << dec << 127;                                    // 十进制
    cout << noshowbase << hex << 127 << dec;            // 不显示 0x
    cout << setw(12) << 127;                                // 占用12个字符,127靠右显示,空格填充
    cout << setw(12) << 127 << 99;                        //          12799(前面 9 个空格)
    cout << setfill('&') << left << setw(12) << 127;    // 输出:127&&&&&&&&&
    cout << setw(12) << showpos << internal << 4;        // 输出:+          4
    cout << setw(12) << showpos << internal << -4;    // 输出:-          4
    cout.precision(10);
    cout.setf(ios::scientific);                            // 科学计数法
    cout << sqrt(2);
    cout.setf(ios::fixed);                                // 普通
    cout << sqrt(2);
    cout << 12.00;                                        // 输出:12
    cout << showpoint << 12.00;                            // 输出:12.00000000(因为前面设置精度为 10 )
    cout << noshowpoint << 12.00;                        // 输出:12
    bool b = 1;
    cout << boolalpha << b;                                // 输出:true
    小结:
    cout.setw(4);                // 对其后所有行都有影响;
    cout << set(4) << b;        // 对其后一行有影响;
    
    举例:
    假设 hello.txt 内容为:a    b    c
    #include <fstream>
    ifstream ifs("hello.txt");
    char c;
    while(ifs >> c)                // 用格式化 >> 读取
        cout << c;                // 输出:abc(中间的空格不见了)
    ifs.close();
    ------------解决办法----------------
    #include <fstream>
    ifstream ifs("hello.txt");
    ifs.unsetf(ios::skipws);        // 取消空格、制表符、回车等符号标志
    char c;
    while(ifs >> c)
        cout << c;                // 输出:a        b        c
    ifs.clear();                    // 清除之前的流状态,否则无法再次读出;
    ifs.setf(ios::skipws);        // 恢复
    ifs.seekg(ios::beg);            // 指针移到文件头
    while(ifs >> c)
        cout << c;
    ifs.close();
    
7.    字符串 I/O 流
    #include <sstream>
    #include <fstream>
    int i = 1234;
    double d = 26.45;
    string s = "tarena";
    ostringstream oss;
    oss << i << d << s;            // 自动把上面的 i d s 三个变量值装入 oss,保存于内存中;
    string str = oss.str();
    cout << str;
    // 读取字符串
    str = "hello 1234";
    istringstream iss;
    iss.str(str);
    iss << s << i;                // 把 hello 放入 s,1234 放入 i
    cout << s << i;                // 输出:hello 1234
    
    举例:输入输出文件对类类型的影响
    #include <fstream>
    class Dog {
        public:
            Dog(const string& name = "", int age = 0) : m_name(name), m_age(age) {}
            void print() { cout << "hello" << endl; }
        private:
            string m_name;
            int m_age;
    };
    int main() {
        ofstream ofs("dog.dat");
        Dog dog("bai", 25);
        ofs.write((char*)&dog, sizeof(dog));
        // 首先:write 要求传入的是 T* 格式,所以这里强制类型转换为 char*
        // 其次:传入的是 dog 地址,也就传入了 m_name 的首地址,而 m_name 的内容没有传入;
        ofs.close();
        // 读取文件:
        ifstream ifs("dog.dat");
        Dog dog2;
        ifs.read((char*)&dog2, sizeof(dog2));    // 也读取 m_name 首地址;
        dog2.print();
        ifs.close();
        return 0;
    }
    运行会提示吐核错误(两次释放内存);
    -----------------解决办法-----------------------
    #include <fstream>
    #include <cstring>
    class Dog {
        public:
            Dog(const string& name = "", int age = 0) : m_age(age)
            { strcpy(m_name, name.c_str); }                // 利用 strcpy 函数赋值
            void print() { cout << "hello" << endl; }
        private:
            char m_name[128];                                // 首先把名字字符串放到数组里
            int m_age;
    };
    int main() {
        ofstream ofs("dog.dat");
        Dog dog("bai", 25);
        ofs.write((char*)&dog, sizeof(dog));
        ofs.close();
        
        ifstream ifs("dog.dat");
        Dog dog2;
        ifs.read((char*)&dog2, sizeof(dog2));
        dog2.print();
        ifs.close();
        return 0;
    }



0 0
原创粉丝点击