两手空空儿 发表于 2023-1-29 21:27:39

文件的定位读写问题,请各位大佬解惑

本帖最后由 两手空空儿 于 2023-1-29 21:41 编辑

我的设想是用一个文件来存放员工的【人数】和【员工信息】,添加或者删除时再更新文件的内容,示例代码如下

#include <iostream>
#include <fstream>
#include <string>
#define FILE "save.txt"

using namespace std;

class Employee{
public:
        //
        Employee(int num, string name, int age):m_number(num), m_name(name), m_age(age){               
        }
        //
//private:
        int m_number;
        string m_name;
        int m_age;               
};

int main(void)
{
        int cnt = 2; //员工的数量
        Employee emp ={
                {11, "张三", 25},
                {12, "李四", 33}};       
        //写入数据
        ofstream ofs(FILE, ios::out);
        ofs << cnt << endl;
        ofs << emp.m_number << " " << emp.m_name << " " << emp.m_age << endl;
        ofs << emp.m_number << " " << emp.m_name << " " << emp.m_age << endl;
        ofs.close();
       
        //增加数据,问题来了。。。。。。。。。。。。。。
        //单独写入cnt, 再往文件尾追加员工信息

        return 0;
}

在文件中,2(cnt)这个数字是以字符形式存放的,只占文件的1个位置, cnt = 10时,就会占两个位置
随着人数的增加,当cnt为两们数,叁位数,或者更多时,
   ofs << cnt << endl; 这一句会覆盖第一位员工的数据,
怎么才能解决这个问题?难道只能每次更新数据的时候把所有数据读出来,再重新写入一次?
(用另外的文件单独存放cnt是可以的,但是我不想分开存放)       
有没有一种方法,让文件的开头给cnt留出足够的位置,在这之后再写入员工数据
比如 cnt是 int类型,4个字节,就用4个位置用内存的形式放进去,
或者是其它什么方法

dolly_yos2 发表于 2023-1-29 21:36:01

可以插空格之类的字符填充预留的位置吧
另外如果不需要保持生成文件人类可读,可能用二进制存储这个数量也是不错的选择

两手空空儿 发表于 2023-1-29 21:54:41

dolly_yos2 发表于 2023-1-29 21:36
可以插空格之类的字符填充预留的位置吧
另外如果不需要保持生成文件人类可读,可能用二进制存储这个数量也 ...

多放很多空格会不会影响后面数据的读取?
怎么在打开文件后直接定位到第一个员工的数据的开头呢?
是不是只能在初始创建文件时就把10个空格放进去,
要是先写入cnt,再补空格,这个操作有点管繁琐,还要计算每次要补多少个空格, 一个空格占两们位置,当cnt为奇数位时,写入cnt之后的读取是不是也会不准确?

二进制存储也是会覆盖后面数据的吧, 用二进制的方式,空格来占位好像还行,我试试

人造人 发表于 2023-1-29 21:58:22

不要用ascii写,用二进制写

sh-5.1$ cat main.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class Employee {
public:
    Employee(int num = 0, const string &name = "", int age = 0): number(num), name(name), age(age) {}
    friend ostream &operator<<(ostream &os, const Employee &e) {
      return os << e.number << " " << e.name << " " << e.age;
    }
    friend ofstream &operator<<(ofstream &ofs, const Employee &e) {
      ofs.write((char *)&e.number, sizeof(e.number));
      size_t size = e.name.size();
      ofs.write((char *)&size, sizeof(size));
      ofs.write(e.name.c_str(), size);
      ofs.write((char *)&e.age, sizeof(e.age));
      return ofs;
    }
    friend ifstream &operator>>(ifstream &ifs, Employee &e) {
      ifs.read((char *)&e.number, sizeof(e.number));
      size_t size; ifs.read((char *)&size, sizeof(size));
      char buff; ifs.read(buff, size);
      e.name = string(buff, buff + size);
      ifs.read((char *)&e.age, sizeof(e.age));
      return ifs;
    }
private:
    int number;
    string name;
    int age;
};

int main(void) {
    Employee emp = {{11, "张三", 25}, {12, "李四", 33}};
    ofstream ofs("data.dat");
    ofs << emp << emp;
    ofs.close();
    ifstream ifs("data.dat");
    Employee buff;
    ifs >> buff >> buff;
    ifs.close();
    cout << buff << std::endl;
    cout << buff << std::endl;
    return 0;
}
sh-5.1$ ./main
11 张三 25
12 李四 33
sh-5.1$

人造人 发表于 2023-1-29 22:03:38

sh-5.1$ cat main.cpp
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

class Employee {
public:
    Employee(int num = 0, const string &name = "", int age = 0): number(num), name(name), age(age) {}
    Employee(ifstream &ifs) {ifs >> *this;}
    friend ostream &operator<<(ostream &os, const Employee &e) {
      return os << e.number << " " << e.name << " " << e.age;
    }
    friend ofstream &operator<<(ofstream &ofs, const Employee &e) {
      ofs.write((char *)&e.number, sizeof(e.number));
      size_t size = e.name.size();
      ofs.write((char *)&size, sizeof(size));
      ofs.write(e.name.c_str(), size);
      ofs.write((char *)&e.age, sizeof(e.age));
      return ofs;
    }
    friend ifstream &operator>>(ifstream &ifs, Employee &e) {
      ifs.read((char *)&e.number, sizeof(e.number));
      size_t size; ifs.read((char *)&size, sizeof(size));
      char buff; ifs.read(buff, size);
      e.name = string(buff, buff + size);
      ifs.read((char *)&e.age, sizeof(e.age));
      return ifs;
    }
private:
    int number;
    string name;
    int age;
};

int main(void) {
    Employee emp = {{11, "张三", 25}, {12, "李四", 33}};
    ofstream ofs("data.dat");
    ofs << emp << emp;
    ofs.close();
    /*
    ifstream ifs("data.dat");
    Employee buff;
    ifs >> buff >> buff;
    ifs.close();
    */
    ifstream ifs("data.dat");
    Employee buff = {ifs, ifs};
    ifs.close();
    cout << buff << std::endl;
    cout << buff << std::endl;
    return 0;
}
sh-5.1$ ./main
11 张三 25
12 李四 33
sh-5.1$

dolly_yos2 发表于 2023-1-29 22:10:22

两手空空儿 发表于 2023-1-29 21:54
多放很多空格会不会影响后面数据的读取?
怎么在打开文件后直接定位到第一个员工的数据的开头呢?
是 ...

C++ 标准库的输出流可以用 std::setw 来设置位宽,自动填充空格
先输入数量时会自动跳过前缀空白字符,应该不会影响后续读取?
不过还是建议用二进制,二进制指的是用 std::basic_ostream<CharT,Traits>::write 或类似方法直接把这个变量的内存表示写进文件,这个长度(基本)是固定的,如果配合 uint32_t 之类的类型使用则可以保证其写入文件必然是 32 / CHAR_BIT 个(通常是 4 个)字节,通常也不需要做填充之类的操作

两手空空儿 发表于 2023-1-29 22:25:09

人造人 发表于 2023-1-29 22:03


谢谢大佬~~~
你的用法好高级,我要好好消化一下,
有没有哪个地方可以查询 标准类库的函数的地方,>><<   和 read, write这些东西我要好好学习一下
还有string类内的函数,现在学的有点乱

人造人 发表于 2023-1-29 22:26:41

sh-5.1$ cat main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

class Employee {
public:
    Employee(int num = 0, const string &name = "", int age = 0): number(num), name(name), age(age) {}
    Employee(ifstream &ifs) {ifs >> *this;}
    friend ostream &operator<<(ostream &os, const Employee &e) {
      return os << e.number << " " << e.name << " " << e.age;
    }
    friend ofstream &operator<<(ofstream &ofs, const Employee &e) {
      ofs.write((char *)&e.number, sizeof(e.number));
      size_t size = e.name.size();
      ofs.write((char *)&size, sizeof(size));
      ofs.write(e.name.c_str(), size);
      ofs.write((char *)&e.age, sizeof(e.age));
      return ofs;
    }
    friend ifstream &operator>>(ifstream &ifs, Employee &e) {
      ifs.read((char *)&e.number, sizeof(e.number));
      size_t size; ifs.read((char *)&size, sizeof(size));
      char buff; ifs.read(buff, size);
      e.name = string(buff, buff + size);
      ifs.read((char *)&e.age, sizeof(e.age));
      return ifs;
    }
private:
    int number;
    string name;
    int age;
};

const vector<Employee> load(const string &filename) {
    vector<Employee> result;
    ifstream ifs(filename);
    size_t size; ifs.read((char *)&size, sizeof(size));
    for(size_t i = 0; i < size; ++i) result.push_back(Employee(ifs));
    return result;
}

void save(const vector<Employee> &v, const string &filename) {
    ofstream ofs(filename);
    size_t size = v.size();
    ofs.write((char *)&size, sizeof(size));
    for(size_t i = 0; i < size; ++i) ofs << v;
}

ostream &operator<<(ostream &os, const vector<Employee> &v) {
    bool flag = false;
    for(size_t i = 0; i < v.size(); ++i) {
      if(flag) os << std::endl;
      flag = true;
      os << v;
    }
    return os;
}

int main(void) {
    {
      vector<Employee> emp = {{11, "张三", 25}, {12, "李四", 33}};
      save(emp, "data.dat");
    }
    vector<Employee> emp = load("data.dat");
    cout << emp << endl;
    return 0;
}
sh-5.1$ ./main
11 张三 25
12 李四 33
sh-5.1$

两手空空儿 发表于 2023-1-29 22:27:09

dolly_yos2 发表于 2023-1-29 21:36
可以插空格之类的字符填充预留的位置吧
另外如果不需要保持生成文件人类可读,可能用二进制存储这个数量也 ...

用二进制的方式 ,10个空格占位,这个方法是可行的
//写入数据
        ofstream ofs(FILE, ios::trunc | ios::binary);
        ofs << "          " <<endl;
        ofs.seekp(ios::beg);
        ofs << cnt;
        ofs.seekp(10);
        ofs << emp.m_number << " " << emp.m_name << " " << emp.m_age << endl;
        ofs << emp.m_number << " " << emp.m_name << " " << emp.m_age << endl;
        cout << "第一次写入完成" << endl;
        system("pause");
        //更改人数
        ofs.seekp(ios::beg);
        cnt = 9999;
        ofs << cnt;
        ofs.close();

人造人 发表于 2023-1-29 22:29:25

两手空空儿 发表于 2023-1-29 22:25
谢谢大佬~~~
你的用法好高级,我要好好消化一下,
有没有哪个地方可以查询 标准类库的函数的地方,>>

https://cplusplus.com/reference/iostream/
https://zh.cppreference.com/w/cpp/header
https://www.apiref.com/cpp/index.html
https://cn.bing.com/
有这4个大概就够了

两手空空儿 发表于 2023-1-29 22:34:57

dolly_yos2 发表于 2023-1-29 22:10
C++ 标准库的输出流可以用 std::setw 来设置位宽,自动填充空格
先输入数量时会自动跳过前缀空白字符, ...

好的,谢谢~~~
C++标准库从哪里可以查到啊,学C的时候查了库函数让自己明白了不少,学C++又不行了,
iostream就会 << , >> , string 类用起来也不太清楚

dolly_yos2 发表于 2023-1-29 22:37:36

两手空空儿 发表于 2023-1-29 22:34
好的,谢谢~~~
C++标准库从哪里可以查到啊,学C的时候查了库函数让自己明白了不少,学C++又不行了,
i ...

我赞同人造人大佬的回答,这四个网站应该足够使用了

两手空空儿 发表于 2023-1-29 22:40:35

人造人 发表于 2023-1-29 22:26


辛苦大佬啦,模版,容器 ,这些我还没学,还看不懂,哈哈哈哈哈

加油学,学完再回过头来再学习大佬的这段代码

人造人 发表于 2023-1-29 22:43:44

两手空空儿 发表于 2023-1-29 22:40
辛苦大佬啦,模版,容器 ,这些我还没学,还看不懂,哈哈哈哈哈

加油学,学完再回过头来再学习大佬的 ...

^_^

两手空空儿 发表于 2023-1-29 23:22:37

人造人 发表于 2023-1-29 22:29
https://cplusplus.com/reference/iostream/
https://zh.cppreference.com/w/cpp/header
https://www.a ...

谢谢~~~~ 全部收藏啦,把常用的东西仔细学一下

两手空空儿 发表于 2023-1-29 23:24:01

dolly_yos2 发表于 2023-1-29 22:10
C++ 标准库的输出流可以用 std::setw 来设置位宽,自动填充空格
先输入数量时会自动跳过前缀空白字符, ...

谢谢~~~ ,帮我解决了暂时的问题,二进制读写的操作我还要好好学习一下

两手空空儿 发表于 2023-1-31 12:11:28

本帖最后由 两手空空儿 于 2023-1-31 12:16 编辑

人造人 发表于 2023-1-29 21:58
不要用ascii写,用二进制写

我终于悟了,打开文件的方式是不是用binary和读写完全没什么关系,我前面以为打开方式不同决定写入和读取的方式
是不是用binary的打开方式,只影响换行符这个东西{:5_107:}
用write的方式写入数字,都不用考虑会覆盖问题,内存里怎么放的,文件里就是怎么放的
字符串加长的话会有覆盖的问题,这个要自己注意
用<<或者 fprintf 这种方式向文件里写东西是把数字转成了字符串写进去的
谢谢大佬提点

人造人 发表于 2023-1-31 12:20:33

两手空空儿 发表于 2023-1-31 12:11
我终于悟了,打开文件的方式是不是用binary和读写完全没什么关系,我前面以为打开方式不同决定写入和读 ...

^_^

dolly_yos2 发表于 2023-1-31 12:22:38

两手空空儿 发表于 2023-1-31 12:11
我终于悟了,打开文件的方式是不是用binary和读写完全没什么关系,我前面以为打开方式不同决定写入和读 ...

可以看看这里对 Binary and text modes 的说法
https://en.cppreference.com/w/cpp/io/c/FILE#Binary_and_text_modes
页: [1]
查看完整版本: 文件的定位读写问题,请各位大佬解惑