鱼C论坛

 找回密码
 立即注册
查看: 2503|回复: 18

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

[复制链接]
发表于 2023-1-29 21:27:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 两手空空儿 于 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[2] =  {
                {11, "张三", 25},
                {12, "李四", 33}};        
        //写入数据
        ofstream ofs(FILE, ios::out);
        ofs << cnt << endl;
        ofs << emp[0].m_number << " " << emp[0].m_name << " " << emp[0].m_age << endl;
        ofs << emp[1].m_number << " " << emp[1].m_name << " " << emp[1].m_age << endl;
        ofs.close();
        
        //增加数据,问题来了。。。。。。。。。。。。。。
        //单独写入cnt, 再往文件尾追加员工信息

        return 0;
}
在文件中,2(cnt)这个数字是以字符形式存放的,只占文件的1个位置, cnt = 10时,就会占两个位置
随着人数的增加,当cnt为两们数,叁位数,或者更多时,
   ofs << cnt << endl; 这一句会覆盖第一位员工的数据,
怎么才能解决这个问题?难道只能每次更新数据的时候把所有数据读出来,再重新写入一次?
(用另外的文件单独存放cnt是可以的,但是我不想分开存放)       
有没有一种方法,让文件的开头给cnt留出足够的位置,在这之后再写入员工数据
比如 cnt是 int类型,4个字节,就用4个位置用内存的形式放进去,
或者是其它什么方法
最佳答案
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 个)字节,通常也不需要做填充之类的操作
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-1-29 21:36:01 From FishC Mobile | 显示全部楼层
可以插空格之类的字符填充预留的位置吧
另外如果不需要保持生成文件人类可读,可能用二进制存储这个数量也是不错的选择
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

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

二进制存储也是会覆盖后面数据的吧, 用二进制的方式,空格来占位好像还行,我试试
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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[size]; 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[2] = {{11, "张三", 25}, {12, "李四", 33}};
    ofstream ofs("data.dat");
    ofs << emp[0] << emp[1];
    ofs.close();
    ifstream ifs("data.dat");
    Employee buff[2];
    ifs >> buff[0] >> buff[1];
    ifs.close();
    cout << buff[0] << std::endl;
    cout << buff[1] << std::endl;
    return 0;
}
sh-5.1$ ./main
11 张三 25
12 李四 33
sh-5.1$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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[size]; 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[2] = {{11, "张三", 25}, {12, "李四", 33}};
    ofstream ofs("data.dat");
    ofs << emp[0] << emp[1];
    ofs.close();
    /*
    ifstream ifs("data.dat");
    Employee buff[2];
    ifs >> buff[0] >> buff[1];
    ifs.close();
    */
    ifstream ifs("data.dat");
    Employee buff[2] = {ifs, ifs};
    ifs.close();
    cout << buff[0] << std::endl;
    cout << buff[1] << std::endl;
    return 0;
}
sh-5.1$ ./main
11 张三 25
12 李四 33
sh-5.1$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-1-29 22:10:22 From FishC Mobile | 显示全部楼层    本楼为最佳答案   
两手空空儿 发表于 2023-1-29 21:54
多放很多空格会不会影响后面数据的读取?
怎么在打开文件后直接定位到第一个员工的数据的开头呢?  
是 ...

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

使用道具 举报

 楼主| 发表于 2023-1-29 22:25:09 | 显示全部楼层

谢谢大佬~~~
你的用法好高级,我要好好消化一下,
有没有哪个地方可以查询 标准类库的函数的地方,>>  <<   和 read, write  这些东西我要好好学习一下
还有string类内的函数,现在学的有点乱
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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[size]; 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[i];
}

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[i];
    }
    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$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 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[0].m_number << " " << emp[0].m_name << " " << emp[0].m_age << endl;
        ofs << emp[1].m_number << " " << emp[1].m_name << " " << emp[1].m_age << endl;
        cout << "第一次写入完成" << endl;
        system("pause");
        //更改人数
        ofs.seekp(ios::beg);
        cnt = 9999;
        ofs << cnt;
        ofs.close();
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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个大概就够了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

好的,谢谢~~~
C++标准库从哪里可以查到啊,学C的时候查了库函数让自己明白了不少,学C++又不行了,
iostream就会 << , >> , string 类用起来也不太清楚
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

我赞同人造人大佬的回答,这四个网站应该足够使用了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-1-29 22:40:35 | 显示全部楼层

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

加油学,学完再回过头来再学习大佬的这段代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-1-29 22:43:44 | 显示全部楼层
两手空空儿 发表于 2023-1-29 22:40
辛苦大佬啦,模版,容器 ,这些我还没学,还看不懂,哈哈哈哈哈

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

^_^
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 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 ...

谢谢~~~~ 全部收藏啦,把常用的东西仔细学一下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

谢谢~~~ ,帮我解决了暂时的问题,二进制读写的操作我还要好好学习一下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-1-31 12:11:28 | 显示全部楼层
本帖最后由 两手空空儿 于 2023-1-31 12:16 编辑
人造人 发表于 2023-1-29 21:58
不要用ascii写,用二进制写


我终于悟了,打开文件的方式是不是用binary和读写完全没什么关系,我前面以为打开方式不同决定写入和读取的方式
是不是用binary的打开方式,只影响换行符这个东西
用write的方式写入数字,都不用考虑会覆盖问题,内存里怎么放的,文件里就是怎么放的
字符串加长的话会有覆盖的问题,这个要自己注意
用<<  或者 fprintf 这种方式向文件里写东西是把数字转成了字符串写进去的
谢谢大佬提点
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

^_^
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

可以看看这里对 Binary and text modes 的说法
https://en.cppreference.com/w/cpp/io/c/FILE#Binary_and_text_modes
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-17 17:39

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表