鱼C论坛

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

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

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

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

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

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

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

  1. #include <iostream>
  2. #include <fstream>
  3. #include <string>
  4. #define FILE "save.txt"

  5. using namespace std;

  6. class Employee{
  7. public:
  8.         //
  9.         Employee(int num, string name, int age):m_number(num), m_name(name), m_age(age){               
  10.         }
  11.         //
  12. //private:
  13.         int m_number;
  14.         string m_name;
  15.         int m_age;               
  16. };

  17. int main(void)
  18. {
  19.         int cnt = 2; //员工的数量
  20.         Employee emp[2] =  {
  21.                 {11, "张三", 25},
  22.                 {12, "李四", 33}};       
  23.         //写入数据
  24.         ofstream ofs(FILE, ios::out);
  25.         ofs << cnt << endl;
  26.         ofs << emp[0].m_number << " " << emp[0].m_name << " " << emp[0].m_age << endl;
  27.         ofs << emp[1].m_number << " " << emp[1].m_name << " " << emp[1].m_age << endl;
  28.         ofs.close();
  29.        
  30.         //增加数据,问题来了。。。。。。。。。。。。。。
  31.         //单独写入cnt, 再往文件尾追加员工信息

  32.         return 0;
  33. }
复制代码

在文件中,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 个)字节,通常也不需要做填充之类的操作
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

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

使用道具 举报

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

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

二进制存储也是会覆盖后面数据的吧, 用二进制的方式,空格来占位好像还行,我试试
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-1-29 21:58:22 | 显示全部楼层
不要用ascii写,用二进制写

  1. sh-5.1$ cat main.cpp
  2. #include <iostream>
  3. #include <fstream>
  4. #include <string>

  5. using namespace std;

  6. class Employee {
  7. public:
  8.     Employee(int num = 0, const string &name = "", int age = 0): number(num), name(name), age(age) {}
  9.     friend ostream &operator<<(ostream &os, const Employee &e) {
  10.         return os << e.number << " " << e.name << " " << e.age;
  11.     }
  12.     friend ofstream &operator<<(ofstream &ofs, const Employee &e) {
  13.         ofs.write((char *)&e.number, sizeof(e.number));
  14.         size_t size = e.name.size();
  15.         ofs.write((char *)&size, sizeof(size));
  16.         ofs.write(e.name.c_str(), size);
  17.         ofs.write((char *)&e.age, sizeof(e.age));
  18.         return ofs;
  19.     }
  20.     friend ifstream &operator>>(ifstream &ifs, Employee &e) {
  21.         ifs.read((char *)&e.number, sizeof(e.number));
  22.         size_t size; ifs.read((char *)&size, sizeof(size));
  23.         char buff[size]; ifs.read(buff, size);
  24.         e.name = string(buff, buff + size);
  25.         ifs.read((char *)&e.age, sizeof(e.age));
  26.         return ifs;
  27.     }
  28. private:
  29.     int number;
  30.     string name;
  31.     int age;
  32. };

  33. int main(void) {
  34.     Employee emp[2] = {{11, "张三", 25}, {12, "李四", 33}};
  35.     ofstream ofs("data.dat");
  36.     ofs << emp[0] << emp[1];
  37.     ofs.close();
  38.     ifstream ifs("data.dat");
  39.     Employee buff[2];
  40.     ifs >> buff[0] >> buff[1];
  41.     ifs.close();
  42.     cout << buff[0] << std::endl;
  43.     cout << buff[1] << std::endl;
  44.     return 0;
  45. }
  46. sh-5.1$ ./main
  47. 11 张三 25
  48. 12 李四 33
  49. sh-5.1$
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-1-29 22:03:38 | 显示全部楼层
  1. sh-5.1$ cat main.cpp
  2. #include <iostream>
  3. #include <fstream>
  4. #include <string>

  5. using namespace std;

  6. class Employee {
  7. public:
  8.     Employee(int num = 0, const string &name = "", int age = 0): number(num), name(name), age(age) {}
  9.     Employee(ifstream &ifs) {ifs >> *this;}
  10.     friend ostream &operator<<(ostream &os, const Employee &e) {
  11.         return os << e.number << " " << e.name << " " << e.age;
  12.     }
  13.     friend ofstream &operator<<(ofstream &ofs, const Employee &e) {
  14.         ofs.write((char *)&e.number, sizeof(e.number));
  15.         size_t size = e.name.size();
  16.         ofs.write((char *)&size, sizeof(size));
  17.         ofs.write(e.name.c_str(), size);
  18.         ofs.write((char *)&e.age, sizeof(e.age));
  19.         return ofs;
  20.     }
  21.     friend ifstream &operator>>(ifstream &ifs, Employee &e) {
  22.         ifs.read((char *)&e.number, sizeof(e.number));
  23.         size_t size; ifs.read((char *)&size, sizeof(size));
  24.         char buff[size]; ifs.read(buff, size);
  25.         e.name = string(buff, buff + size);
  26.         ifs.read((char *)&e.age, sizeof(e.age));
  27.         return ifs;
  28.     }
  29. private:
  30.     int number;
  31.     string name;
  32.     int age;
  33. };

  34. int main(void) {
  35.     Employee emp[2] = {{11, "张三", 25}, {12, "李四", 33}};
  36.     ofstream ofs("data.dat");
  37.     ofs << emp[0] << emp[1];
  38.     ofs.close();
  39.     /*
  40.     ifstream ifs("data.dat");
  41.     Employee buff[2];
  42.     ifs >> buff[0] >> buff[1];
  43.     ifs.close();
  44.     */
  45.     ifstream ifs("data.dat");
  46.     Employee buff[2] = {ifs, ifs};
  47.     ifs.close();
  48.     cout << buff[0] << std::endl;
  49.     cout << buff[1] << std::endl;
  50.     return 0;
  51. }
  52. sh-5.1$ ./main
  53. 11 张三 25
  54. 12 李四 33
  55. sh-5.1$
复制代码
小甲鱼最新课程 -> https://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 个)字节,通常也不需要做填充之类的操作
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

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

使用道具 举报

发表于 2023-1-29 22:26:41 | 显示全部楼层
  1. sh-5.1$ cat main.cpp
  2. #include <iostream>
  3. #include <fstream>
  4. #include <string>
  5. #include <vector>

  6. using namespace std;

  7. class Employee {
  8. public:
  9.     Employee(int num = 0, const string &name = "", int age = 0): number(num), name(name), age(age) {}
  10.     Employee(ifstream &ifs) {ifs >> *this;}
  11.     friend ostream &operator<<(ostream &os, const Employee &e) {
  12.         return os << e.number << " " << e.name << " " << e.age;
  13.     }
  14.     friend ofstream &operator<<(ofstream &ofs, const Employee &e) {
  15.         ofs.write((char *)&e.number, sizeof(e.number));
  16.         size_t size = e.name.size();
  17.         ofs.write((char *)&size, sizeof(size));
  18.         ofs.write(e.name.c_str(), size);
  19.         ofs.write((char *)&e.age, sizeof(e.age));
  20.         return ofs;
  21.     }
  22.     friend ifstream &operator>>(ifstream &ifs, Employee &e) {
  23.         ifs.read((char *)&e.number, sizeof(e.number));
  24.         size_t size; ifs.read((char *)&size, sizeof(size));
  25.         char buff[size]; ifs.read(buff, size);
  26.         e.name = string(buff, buff + size);
  27.         ifs.read((char *)&e.age, sizeof(e.age));
  28.         return ifs;
  29.     }
  30. private:
  31.     int number;
  32.     string name;
  33.     int age;
  34. };

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

  42. void save(const vector<Employee> &v, const string &filename) {
  43.     ofstream ofs(filename);
  44.     size_t size = v.size();
  45.     ofs.write((char *)&size, sizeof(size));
  46.     for(size_t i = 0; i < size; ++i) ofs << v[i];
  47. }

  48. ostream &operator<<(ostream &os, const vector<Employee> &v) {
  49.     bool flag = false;
  50.     for(size_t i = 0; i < v.size(); ++i) {
  51.         if(flag) os << std::endl;
  52.         flag = true;
  53.         os << v[i];
  54.     }
  55.     return os;
  56. }

  57. int main(void) {
  58.     {
  59.         vector<Employee> emp = {{11, "张三", 25}, {12, "李四", 33}};
  60.         save(emp, "data.dat");
  61.     }
  62.     vector<Employee> emp = load("data.dat");
  63.     cout << emp << endl;
  64.     return 0;
  65. }
  66. sh-5.1$ ./main
  67. 11 张三 25
  68. 12 李四 33
  69. sh-5.1$
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

用二进制的方式 ,10个空格占位,这个方法是可行的
  1. //写入数据
  2.         ofstream ofs(FILE, ios::trunc | ios::binary);
  3.         ofs << "          " <<endl;
  4.         ofs.seekp(ios::beg);
  5.         ofs << cnt;
  6.         ofs.seekp(10);
  7.         ofs << emp[0].m_number << " " << emp[0].m_name << " " << emp[0].m_age << endl;
  8.         ofs << emp[1].m_number << " " << emp[1].m_name << " " << emp[1].m_age << endl;
  9.         cout << "第一次写入完成" << endl;
  10.         system("pause");
  11.         //更改人数
  12.         ofs.seekp(ios::beg);
  13.         cnt = 9999;
  14.         ofs << cnt;
  15.         ofs.close();
复制代码
小甲鱼最新课程 -> https://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个大概就够了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

好的,谢谢~~~
C++标准库从哪里可以查到啊,学C的时候查了库函数让自己明白了不少,学C++又不行了,
iostream就会 << , >> , string 类用起来也不太清楚
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

我赞同人造人大佬的回答,这四个网站应该足够使用了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

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

加油学,学完再回过头来再学习大佬的这段代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

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

^_^
小甲鱼最新课程 -> https://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 ...

谢谢~~~~ 全部收藏啦,把常用的东西仔细学一下
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

谢谢~~~ ,帮我解决了暂时的问题,二进制读写的操作我还要好好学习一下
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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


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

使用道具 举报

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

^_^
小甲鱼最新课程 -> https://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
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-22 19:44

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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