C/C++/Qt 文件操作 & 效率比较

这篇具有很好参考价值的文章主要介绍了C/C++/Qt 文件操作 & 效率比较。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 介绍

C++文件操作、QT文件操作因为使用了面向对象,具有更好的封装性。

2 比较结果

2.1 Linux平台上运行程序普遍比Windows上快;Windows下VC编译的程序一般运行比MINGW(MINimal Gcc for Windows)快

  • 引用 探寻C++最快的读取文件的方案

  • Linux平台上运行程序普遍比Windows上快。

  • Windows下VC编译的程序一般运行比MINGW(MINimal Gcc for Windows)快。

  • VC对cin取消同步与否不敏感,前后效率相同。反过来MINGW则非常敏感,前后效率相差8倍。

  • read本是linux系统函数,MINGW可能采用了某种模拟方式,read比fread更慢。

  • Pascal程序运行速度实在令人不敢恭维。

C/C++/Qt 文件操作 & 效率比较

2.2 二进制文件的操作要快于文本文件;写文件的操作要快于读文件;C语言文件指针操作方式要快于C++流式读写文件

  • 引用 C/C++文件操作效率比较——FILE/fstream

如下程序运行结果

C语言写文本文件操作运行时间为: 157 ms 
C++写文本文件操作运行时间为: 593 ms 

C语言读文本文件操作运行时间为: 172 ms
C++读文本文件操作运行时间为: 235 ms

C语言写二进制文件操作运行时间为: 78 ms
C++写二进制文件操作运行时间为: 203 ms

C语言读二进制文件操作运行时间为: 125 ms
C++读二进制文件操作运行时间为:156 ms
#include <iostream>
#include <fstream>
#include <windows.h>
#include <string.h>
#include <stdio.h>
 
using namespace std;
 
void WriteTXTFile_C()
{
	FILE* fp = fopen("c.txt","wt");
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			fputc('C', fp);
			fputs("Hello, world!\n", fp);
		}
	}
	fclose(fp);
}
 
void ReadTXTFile_C()
{
	FILE* fp = fopen("c.txt","rt");
	char str[15];
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			fgetc(fp);
			fgets(str, 15, fp);
		}
	}
	fclose(fp);
}
 
void WriteBINFile_C()
{
	FILE* fp = fopen("c.bin","wb");
	char* str = "CHello, world!\n";
	int len = strlen(str);
	
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			fwrite(str, 1, len, fp);
		}
	}
	fclose(fp);
}
 
void ReadBINFile_C()
{
	FILE* fp = fopen("c.bin","rb");
	char str[16];
	
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			fread(str, 1, 16, fp);
		}
	}
	fclose(fp);
}
 
void WriteTXTFile_CPlus()
{
	fstream file;
	file.open("cp.txt", ios::in | ios::out | ios::trunc);//注意ios::in,或者ios::in | ios::out两种方式在文件不存在时会执行写操作但不会建立文件。
 
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			file.put('C');
			file<<"Hello, world!\n";
		}
	}
	file.close();
}
 
void ReadTXTFile_CPlus()
{
	fstream file;
	file.open("cp.txt", ios::in | ios::out);
	char str[15];
 
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			file.get(str, 15);
		}
	}
	file.close();
}
 
void WriteBINFile_CPlus()
{
	fstream file;
	file.open("cp.bin", ios::in | ios::out | ios::trunc | ios::binary);
	char* str = "CHello, world!\n";
	int len = strlen(str);
 
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			file.write(str, len);
		}
	}
	file.close();
}
 
void ReadBINFile_CPlus()
{
	fstream file;
	file.open("cp.bin", ios::in | ios::out | ios::binary);
	char str[16];
 
	for(int i = 0; i < 1000; i++)
	{
		for(int j = 0; j < 1000; j++)
		{
			file.read(str, 16);
		}
	}
	file.close();
}
 
int main()
{
	DWORD start, end;
	start = GetTickCount();
	WriteTXTFile_C();
	end = GetTickCount();
	printf("C语言写文本文件操作运行时间为:%d ms\n", end - start);
	start = GetTickCount();
	WriteTXTFile_CPlus();
	end = GetTickCount();
	cout<<"C++写文本文件操作运行时间为:"<<end - start<<" ms"<<endl;
 
	cout<<endl;
 
	start = GetTickCount();
	ReadTXTFile_C();
	end = GetTickCount();
	printf("C语言读文本文件操作运行时间为:%d ms\n", end - start);
	start = GetTickCount();
	ReadTXTFile_CPlus();
	end = GetTickCount();
	cout<<"C++读文本文件操作运行时间为:"<<end - start<<" ms"<<endl;
 
	cout<<endl;
 
	start = GetTickCount();
	WriteBINFile_C();
	end = GetTickCount();
	printf("C语言写二进制文件操作运行时间为:%d ms\n", end - start);
	start = GetTickCount();
	WriteBINFile_CPlus();
	end = GetTickCount();
	cout<<"C++写二进制文件操作运行时间为:"<<end - start<<" ms"<<endl;
 
	cout<<endl;
 
	start = GetTickCount();
	ReadBINFile_C();
	end = GetTickCount();
	printf("C语言读二进制文件操作运行时间为:%d ms\n", end - start);
	start = GetTickCount();
	ReadBINFile_CPlus();
	end = GetTickCount();
	cout<<"C++读二进制文件操作运行时间为:"<<end - start<<" ms"<<endl;
}

3 知识点

文本文件和二进制文件

  • 都是以二进制方式存储的。
  • 文本文件通常用来保存肉眼可见的字符,比如.txt文件、.c文件、.dat文件等,用文本编辑器打开这些文件,我们能够顺利看懂文件的内容。文本文件中采用的是 ASCII、UTF-8、GBK 等字符编码,文本编辑器可以识别出这些编码格式,并将编码值转换成字符展示出来。
  • 二进制文件通常用来保存视频、图片、程序等不可阅读的内容,用文本编辑器打开这些文件,会看到一堆乱码,根本看不懂。 二进制文件使用的是 mp4、gif、exe 等特殊编码格式,文本编辑器并不认识这些编码格式。

换行符

  • 类 UNIX/Linux 系统在处理文本文件时也将\n作为换行符。
  • Windows 系统却不同,它将\r\n作为文本文件的换行符。

wins下fwrite函数0x0A变成0x0D 0x0A

fwrite 在以文本方式写文件时,碰到0x0A,会自动在前面加上0x0D,以构成回车换行符,因为Windows平台的换行符默认是:0x0D 0x0A。
以二进制方式打开文件,然后进行写文件。

fopen( filename, "a+" );     ------> 改成    fopen( filename, "a+b" );
ofstream outFile(strFilePath, ios::binary | ios::trunc);

4 C

4.1 介绍

文件 硬件设备
stdin 标准输入文件,一般指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。
stdout 标准输出文件,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。
stderr 标准错误文件,一般指显示器;perror() 等函数默认向 stderr 输出数据(后续会讲到)。
stdprn 标准打印文件,一般指打印机。

操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。

4.2 基本函数

fopen 打开文件

<stdio.h>
FILE *fopen(char *filename, char *mode);
filename为文件名(包括文件路径),mode为打开方式,它们都是字符串。

只读方式打开当前目录下的 demo.txt 文件
FILE *fp = fopen("demo.txt", "r");

以二进制方式打开 D 盘下的 demo.txt 文件,允许读和写
FILE *fp = fopen("D:\\demo.txt","rb+");
控制读写权限的字符串(必须指明)
打开方式 说明
“r” 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。
“w” 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
“a” 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
“r+” 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。
“w+” 以“写入/更新”方式打开文件,相当于wr+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
“a+” 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
控制读写方式的字符串(可以不写)
打开方式 说明
“t” 文本文件。如果不写,默认为"t"
“b” 二进制文件。
  • r(read):读
  • w(write):写
  • a(append):追加
  • t(text):文本文件
  • b(binary):二进制文件
  • +:读和写

fclose 关闭文件

int fclose(FILE *fp);

fgetc 字符读取( file get char)

int fgetc (FILE *fp);
成功返回读取到的字符,读取到文件末尾或读取失败时返回EOF(end of file)

在屏幕上显示 F:\bot.txt 文件的内容

#include<stdio.h>
int main(){
    FILE *fp;
    char ch;
   
    //如果文件不存在,给出提示并退出
    if( (fp=fopen("F:\\bot.txt","rt")) == NULL ){
        puts("Fail to open file!");
        exit(0);
    }
    //每次读取一个字节,直到读取完毕
    while( (ch=fgetc(fp)) != EOF ){
        putchar(ch);
    }
    putchar('\n');  //输出换行符
    fclose(fp);
    return 0;
}

ferror 判断文件操作是否出错

int ferror ( FILE *fp );
出错时返回非零值,否则返回零值。

fputc 字符写入

int fputc ( int ch, FILE *fp );

char ch = 'a';
fputc(ch, fp);

fgets和fputs 以字符串的形式读写文件

char *fgets ( char *str, int n, FILE *fp );
int fputs( char *str, FILE *fp );

fread和fwrite 以数据块的形式读写文件

size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );

ptr 为内存区块的指针,它可以是数组、变量、结构体等。
fread() 中的 ptr 用来存放读取到的数据,fwrite() 中的 ptr 用来存放要写入的数据。
size:表示每个数据块的字节数。
count:表示要读写的数据块的块数。
fp:表示文件指针。
理论上,每次读写 size*count 个字节的数据。

fscanf和fprintf 格式化读写文件

fscanf() 和 fprintf() 函数与前面使用的 scanf() 和 printf() 功能相似,都是格式化读写函数,两者的区别在于 fscanf() 和 fprintf() 的读写对象不是键盘和显示器,而是磁盘文件。

fp 为文件指针,format 为格式控制字符串,… 表示参数列表。
int fscanf ( FILE *fp, char * format, ... );
int fprintf ( FILE *fp, char * format, ... );

rewind和fseek 随机读写文件(定位)

  • 实际开发中经常需要读写文件的中间部分,要解决这个问题,就得先移动文件内部的位置指针,再进行读写。这种读写方式称为随机读写,也就是说从文件的任意位置开始读写。
  • 实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。
rewind() 用来将位置指针移动到文件开头
void rewind ( FILE *fp );
fseek() 用来将位置指针移动到任意位置
int fseek ( FILE *fp, long offset, int origin );
  • fp 为文件指针,也就是被移动的文件。
  • offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。
  • origin 为起始位置,也就是从何处开始计算偏移量。C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示。
起始点 常量名 常量值
文件开头 SEEK_SET 0
当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2

fread 复制

文件复制思路:开辟一个缓冲区,不断从原文件中读取内容到缓冲区,每读取完一次就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完。
注意:以二进制形式打开,保证txt文件外的mp4,jpg等格式拷贝无异常。

  • 开辟多大的缓冲区合适?缓冲区过小会造成读写次数的增加,过大也不能明显提高效率。目前大部分磁盘的扇区都是4K对齐的,如果读写的数据不是4K的整数倍,就会跨扇区读取,降低效率,所以我们开辟4K的缓冲区
  • 缓冲区中的数据是没有结束标志的,如果缓冲区填充不满,如何确定写入的字节数?最好的办法就是每次读取都能返回读取到的字节数。
size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
#include <stdio.h>
#include <stdlib.h>
int copyFile(char *fileRead, char *fileWrite);
int main(){
    char fileRead[100];  // 要复制的文件名
    char fileWrite[100];  // 复制后的文件名
   
    // 获取用户输入
    printf("要复制的文件:");
    scanf("%s", fileRead);
    printf("将文件复制到:");
    scanf("%s", fileWrite);
    // 进行复制操作
    if( copyFile(fileRead, fileWrite) ){
        printf("恭喜你,文件复制成功!\n");
    }else{
        printf("文件复制失败!\n");
    }
    return 0;
}
/**
* 文件复制函数
* @param    fileRead    要复制的文件
* @param    fileWrite   复制后文件的保存路径
* @return   int         1: 复制成功;2: 复制失败
**/
int copyFile(char *fileRead, char *fileWrite){
    FILE *fpRead;  // 指向要复制的文件
    FILE *fpWrite;  // 指向复制后的文件
    int bufferLen = 1024*4;  // 缓冲区长度
    char *buffer = (char*)malloc(bufferLen);  // 开辟缓存
    int readCount;  // 实际读取的字节数
    if( (fpRead=fopen(fileRead, "rb")) == NULL || (fpWrite=fopen(fileWrite, "wb")) == NULL ){
        printf("Cannot open file, press any key to exit!\n");
        getch();
        exit(1);
    }
    // 不断从fileRead读取内容,放在缓冲区,再将缓冲区的内容写入fileWrite
    while( (readCount=fread(buffer, 1, bufferLen, fpRead)) > 0 ){
        fwrite(buffer, readCount, 1, fpWrite);
    }
    free(buffer);
    fclose(fpRead);
    fclose(fpWrite);
    return 1;
}

FILE 文件指针

FILE  *fp;

typedef struct _iobuf {
    int cnt;  // 剩余的字符,如果是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
    char *ptr;  // 下一个要被读取的字符的地址
    char *base;  // 缓冲区基地址
    int flag;  // 读写状态标志位
    int fd;  // 文件描述符
    // 其他成员
} FILE;

ftell 获取文件大小

long int ftell ( FILE * fp );

先使用 fseek() 将文件内部指针定位到文件末尾,再使用 ftell() 返回内部指针距离文件开头的字节数,这个返回值就等于文件的大小。
long fsize(FILE *fp){
    long n;
    fpos_t fpos;  //当前位置
    fgetpos(fp, &fpos);  //获取当前位置
    fseek(fp, 0, SEEK_END);
    n = ftell(fp);
    fsetpos(fp,&fpos);  //恢复之前的位置
    return n;
}

4.3 提高速度

  • 内存映射
  • 使用WINAPI
  • 优化算法

FILE自己维护了一套缓存机制
FILE会使用默认的一个缓存值作为io缓存(4k),或者也可以通过setbuf来设置这个缓存的大小。

假 设你fread 1字节 会导致ReadFile 4k,然后fread再将要读取的数据copy到指定的缓冲区中。以后访问只要不过这个边界,就一直从该io缓存中读取,fwrite也是,直到超过io 缓存边界才真正的调用WriteFile。可以调用flush主动强制刷新从而调用WriteFile 或者fclose被动刷新调用WriteFile(这时fclose会阻塞)。

再说一下硬盘的 硬盘的cache由硬盘控制器管理和使用 就像处理器的cache没法直接操作一样 写硬盘的时候会先写入cache 然后硬盘内部会把数据慢慢写入磁盘 这个过程中没有优化 也就是说硬盘驱动按什么顺序写的 写入磁盘就是什么顺序。
而实际上 硬盘是个随机访问设备 先写哪个后写哪个无所谓 所以一般在把应用层的io访问转化为底层的io请求后 内核层会做io请求优化排序。

假设一个io队列上目前挂着10个请求 内核层会事先计算每个请求在物理上的位置 然后进行排序 以保证磁头转动一周,尽量让10个请求中的多个在一周内完成,想像一下 最好的情况 10个请求都在一个盘面上 磁头旋转1周 10个请求全部完成 最坏的情况 要转10周 10周的原因是一次只能操作一个磁头 而10个请求可能不幸的在10个盘面上(这时候内核也不会再排序了)。

因此让自己的io操作尽可能维持在连续的磁盘空间 且在物理上不跨越盘面 这样效果最好。为此你可能需要硬盘的准确的参数 并精确计算。

缓存的优势在高强度的io操作会被抵消,因为硬盘的写入速度始终跟不上处理器的请求,cache只能帮助缓冲一下。 cache越大,缓冲的时间越长,当cache填满,硬件上ready信号为无效,硬盘驱动不能再写了。只能挂起内核的io队列,这时候上层还在不停的请求,内核层要么继续往io请求队列上挂装请求 要么阻塞发起io的进程,等到cache有空间了,硬件使能ready信号,驱动重新从内河的io请求队列上摘取io请求,再填cache 又满 。。。。 也就是说cache的优势只在一开始的缓存时间上,这个优势对于小的io请求特别有好处,因为能在填满cache之前不会遭到阻塞或挂起。

5 C++

5.1 介绍

重定向后的 cin 和 cout 可分别用于读取文件中的数据和向文件中写入数据。除此之外,C++ 标准库中还专门提供了 3 个类用于实现文件操作,它们统称为文件流类。

  • ifstream:专用于从文件中读取数据;
  • ofstream:专用于向文件中写入数据;
  • fstream:既可用于从文件中读取数据,又可用于向文件中写入数据。
    C/C++/Qt 文件操作 & 效率比较

fstream 类一些常用的成员方法

成员方法名 适用类对象 功 能
open() fstream ifstream ofstream 打开指定文件,使其与文件流对象相关联。
is_open() 检查指定文件是否已打开。
close() 关闭文件,切断和文件流对象的关联。
swap() 交换 2 个文件流对象。
operator>> fstream ifstream 重载 >> 运算符,用于从指定文件中读取数据。
gcount() 返回上次从文件流提取出的字符个数。该函数常和 get()、getline()、ignore()、peek()、read()、readsome()、putback() 和 unget() 联用。
get() 从文件流中读取一个字符,同时该字符会从输入流中消失。
getline(str,n,ch) 从文件流中接收 n-1 个字符给 str 变量,当遇到指定 ch 字符时会停止读取,默认情况下 ch 为 ‘\0’。
ignore(n,ch) 从文件流中逐个提取字符,但提取出的字符被忽略,不被使用,直至提取出 n 个字符,或者当前读取的字符为 ch。
peek() 返回文件流中的第一个字符,但并不是提取该字符。
putback© 将字符 c 置入文件流(缓冲区)。
operator<< fstream ofstream 重载 << 运算符,用于向文件中写入指定数据。
put() 向指定文件流中写入单个字符。
write() 向指定文件中写入字符串。
tellp() 用于获取当前文件输出流指针的位置。
seekp() 设置输出文件输出流指针的位置。
flush() 刷新文件输出流缓冲区。
good() fstream ofstream ifstream 操作成功,没有发生任何错误。
eof() 到达输入末尾或文件尾。

5.2 基本函数

open 打开文件

void open(const char* szFileName, int mode)
第一个参数是指向文件名的指针,第二个参数是文件的打开模式标记。
模式标记 适用对象 作用
ios::in ifstream fstream 打开文件用于读取数据。如果文件不存在,则打开出错。
ios::out ofstream fstream 打开文件用于写入数据。如果文件不存在,则新建该文件;如果文件原来就存在,则打开时清除原来的内容。
ios::app ofstream fstream 打开文件,用于在其尾部添加数据。如果文件不存在,则新建该文件。
ios::ate ifstream 打开一个已有的文件,并将文件读指针指向文件末尾(读写指 的概念后面解释)。如果文件不存在,则打开出错。
ios:: trunc ofstream 打开文件时会清空内部存储的所有数据,单独使用时与 ios::out 相同。
ios::binary ifstream ofstream fstream 以二进制方式打开文件。若不指定此模式,则以文本模式打开。
ios::in | ios::out fstream 打开已存在的文件,既可读取其内容,也可向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。
ios::in | ios::out ofstream 打开已存在的文件,可以向其写入数据。文件刚打开时,原有内容保持不变。如果文件不存在,则打开出错。
ios::in | ios::out | ios::trunc fstream 打开文件,既可读取其内容,也可向其写入数据。如果文件本来就存在,则打开时清除原来的内容;如果文件不存在,则新建该文件。

ifstream 构造函数时打开文件

定义流对象时,在构造函数中给出文件名和打开模式也可以打开文件。

ifstream::ifstream (const char* szFileName, int mode = ios::in, int);

close 关闭文件

bool Append4Log(const string_t &sFileName, uint8_t *sValue, int size)
{
    if (sFileName.empty())
    {
        return false;
    }
    fstream_t outfile(sFileName.c_str(), std::ios::out | std::ios::app);
    if (outfile)
    {
        outfile << sValue; // << std::endl;	// out put to file
        outfile.close();
        outfile.clear();
    }
    return true;
}

flush 刷新缓冲区

在很多实际场景中,即便已经对文件执行了写操作,但后续还可能会执行其他的写操作。对于这种情况,我们可能并不想频繁地打开/关闭文件,可以使用 flush() 方法及时刷新输出流缓冲区,也能起到防止写入文件失败的作用。

>> 和 << 读写文件(文本)

fstream 或者 ifstream 类负责实现对文件的读取,它们内部都对 >> 输出流运算符做了重载;同样,fstream 和 ofstream 类负责实现对文件的写入,它们的内部也都对 << 输出流运算符做了重载。

read() 和 write() 成员方法读写文件(二进制)

ostream & write(char* buffer, int count);
istream & read(char* buffer, int count);

get()和put()读写文件

ostream& put (char c);
istream& get (char& c);
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    char c;
    //以二进制形式打开文件
    ofstream outFile("out.txt", ios::out | ios::binary);
    if (!outFile) {
        cout << "error" << endl;
        return 0;
    }
    while (cin >> c) {
        //将字符 c 写入 out.txt 文件
        outFile.put(c);
    }
    outFile.close();
    return 0;
}

注意,由于文件存放在硬盘中,硬盘的访问速度远远低于内存。如果每次写一个字节都要访问硬盘,那么文件的读写速度就会慢得不可忍受。因此,操作系统在接收到 put() 方法写文件的请求时,会先将指定字符存储在一块指定的内存空间中(称为文件流输出缓冲区),等刷新该缓冲区(缓冲区满、关闭文件、手动调用 flush() 方法等,都会导致缓冲区刷新)时,才会将缓冲区中存储的所有字符“一股脑儿”全写入文件。

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    char c;
    //以二进制形式打开文件
    ifstream inFile("out.txt", ios::out | ios::binary);
    if (!inFile) {
        cout << "error" << endl;
        return 0;
    }
    while ( (c=inFile.get())&&c!=EOF )   //或者 while(inFile.get(c)),对应第二种语法格式
    {
        cout << c ;
    }
    inFile.close();
    return 0;
}

和 put() 方法一样,操作系统在接收到 get() 方法的请求后,哪怕只读取一个字符,也会一次性从文件中将很多数据(通常至少是 512 个字节,因为硬盘的一个扇区是 512 B)读到一块内存空间中(可称为文件流输入缓冲区),这样当读取下一个字符时,就不需要再访问硬盘中的文件,直接从该缓冲区中读取即可。

getline 从文件中读取一行字符串

istream & getline(char* buf, int bufSize);
istream & getline(char* buf, int bufSize, char delim);

seekp、seekg、tellg、tellp 移动和获取文件读写指针(p-put,g-get ?)

* ifstream 类和 fstream 类有 seekg 成员函数,可以设置文件读指针的位置;
* ofstream 类和 fstream 类有 seekp 成员函数,可以设置文件写指针的位置。
ostream & seekp (int offset, int mode);
istream & seekg (int offset, int mode);

mode:

  • ios::beg:让文件读指针(或写指针)指向从文件开始向后的 offset 字节处。offset 等于 0 即代表文件开头。在此情况下,offset 只能是非负数。
  • ios::cur:在此情况下,offset 为负数则表示将读指针(或写指针)从当前位置朝文件开头方向移动 offset 字节,为正数则表示将读指针(或写指针)从当前位置朝文件尾部移动 offset字节,为 0 则不移动。
  • ios::end:让文件读指针(或写指针)指向从文件结尾往前的 |offset|(offset 的绝对值)字节处。在此情况下,offset 只能是 0 或者负数。
* ifstream 类和 fstream 类还有 tellg 成员函数,能够返回文件读指针的位置;
* ofstream 类和 fstream 类还有 tellp 成员函数,能够返回文件写指针的位置。
int tellg();
int tellp();

6 Qt

6.1 介绍

QFile 类支持对文件进行读取、写入、删除、重命名、拷贝等操作,它既可以操作文件文件,也可以操作二进制文件。

6.2 基本函数

QFile 构造函数确定操作文件

QFile::QFile()
QFile::QFile(const QString &name)

参数 name 用来指定要操作的目标文件,包含文件的存储路径和文件名,存储路径可以使用绝对路径(比如 “D:/Demo/test.txt”)或者相对路径(比如"./Demo/test.txt"),路径中的分隔符要用 “/” 表示。

open 打开文件

bool QFile::open(OpenMode mode)

mode 参数用来指定文件的打开方式。mode 参数一次性指定多个值,值和值之间用|分割。

表 1 QFile文件打开方式
打开方式 含 义
QIODevice::ReadOnly 只能对文件进行读操作
QIODevice::WriteOnly 只能对文件进行写操作,如果目标文件不存在,会自行创建一个新文件。
QIODevice::ReadWrite 等价于 ReadOnly | WriteOnly,能对文件进行读和写操作。
QIODevice::Append 以追加模式打开文件,写入的数据会追加到文件的末尾(文件原有的内容保留)。
QIODevice::Truncate 以重写模式打开,写入的数据会将原有数据全部清除。注意,此打开方式不能单独使用,通常会和 ReadOnly 或 WriteOnly 搭配。
QIODevice::Text 读取文件时,会将行尾结束符(Unix 系统中是 "\n",Windows 系统中是 "\r\n")转换成‘\n’;将数据写入文件时,会将行尾结束符转换成本地格式,例如 Win32 平台上是‘\r\n’。

常用函数

表 2 QFile常用方法
普通成员方法 功 能
qint64 QFile::size() const 获取当前文件的大小。对于打开的文件,该方法返回文件中可以读取的字节数。
bool QIODevice::getChar(char *c) 从文件中读取一个字符,并存储到 c 中。读取成功时,方法返回 true,否则返回 false。
bool QIODevice::putChar(char c) 向文件中写入字符 c,成功时返回 true,否则返回 false。
QByteArray QIODevice::read(qint64 maxSize) 从文件中一次性最多读取 maxSize 个字节,然后返回读取到的字节。
qint64 QIODevice::read(char *data, qint64 maxSize) 从文件中一次性对多读取 maxSize 个字节,读取到的字节存储到 data 指针指定的内存控件中。该方法返回成功读取到的字节数。
QByteArray QIODevice::readAll() 读取文件中所有的数据。
qint64 QIODevice::readLine(char *data, qint64 maxSize) 每次从文件中读取一行数据或者读取最多 maxSize-1 个字节,存储到 data 中。该方法返回实际读取到的字节数。
qint64 QIODevice::write(const char *data, qint64 maxSize) 向 data 数据一次性最多写入 maxSize 个字节,该方法返回实际写入的字节数。 
qint64 QIODevice::write(const char *data) 将 data 数据写入文件,该方法返回实际写入的字节数。
qint64 QIODevice::write(const QByteArray &byteArray) 将 byteArray 数组中存储的字节写入文件,返回实际写入的字节数。
bool QFile::copy(const QString &newName) 将当前文件的内容拷贝到名为 newName 的文件中,如果成功,方法返回 true,否则返回 false。

copy 方法在执行复制操作之前,会关闭源文件。
bool QFile::rename(const QString &newName) 对当前文件进行重命名,新名称为 newName,成功返回 true,失败返回 false。
bool QFile::remove() 删除当前文件,成功返回 true,失败返回 false。

QFile+QTextStream 读写文件

QTextStream(QIODevice *device)
表 3 QTextStream常用方法
成员方法 功 能
bool QTextStream::atEnd() const 判断是否读到文件末尾,如果已经达到末尾,返回 true,否则返回 false。
QString QTextStream::read(qint64 maxlen) 从文件中读最多 maxlen 个字符,返回这些字符组成的 QString 字符串。
QString QTextStream::readAll() 从文件中读取所有内容,返回由读取内容组成的 QString 字符串。
QString QTextStream::readLine(qint64 maxlen = 0) 默认读取一行文本,如果手动指定 maxlen 的值,则最多读取 maxlen 个字符,并返回读取内容组成的 QString 字符串。
void QTextStream::setFieldAlignment(FieldAlignment mode) 设置对齐方式,通常与 setFieldWidth() 一起使用。
void QTextStream::setFieldWidth(int width) 设置每份数据占用的位置宽度为 width。
表 4 QTextStream常用格式描述符
描述符 功能相同的方法 功 能
Qt::hex QTextStream::setIntegerBase(16) 将指定整数对应的 16 进制数写入到文件中。
Qt::showbase QTextStream::setNumberFlags(numberFlags() | ShowBase) 对于非十进制数,写入到文件中时带上相应的前缀。二进制数前缀是 0b,八进制数前缀是 0,十六进制数前缀是 0x。
Qt::forcesign QTextStream::setNumberFlags(numberFlags() | ForceSign) 将数字写入文件时,带上正负号。
Qt::fixed QTextStream::setRealNumberNotation(FixedNotation) 将浮点数以普通小数的形式写入文件。
Qt::scientific QTextStream::setRealNumberNotation(ScientificNotation) 将浮点数以科学计数法的形式写入文件。
Qt::left QTextStream::setFieldAlignment(AlignLeft) 左对齐
Qt::right QTextStream::setFieldAlignment(AlignRight) 右对齐
Qt::center QTextStream::setFieldAlignment(AlignCenter) 居中对齐

QFile+QDataStream 读写二进制文件

QDataStream::QDataStream(QIODevice *d)
表 5 QDataStream常用方法
成员方法 功 能
bool QDataStream::atEnd() const 判断是否读到文件末尾,如果已经达到末尾,返回 true,否则返回 false。
QDataStream &QDataStream::readBytes(char *&s, uint &l) 对于用 writeBytes() 方法写入文件的 l 和 s,只能使用 readBytes() 方法读取出来。 
int QDataStream::readRawData(char *s, int len) 从文件中读取最多 len 字节的数据到 s 中,返回值表示实际读取的字节数。注意,调用该方法之前,需要先给 s 参数分配好内存空间。
void QDataStream::setVersion(int v) 不同版本的 Qt 中,同名称的数据类型也可能存在差异,通过调用此方法手动指定版本号,可以确保读取数据的一致性。
int QDataStream::skipRawData(int len) 跳过文件中的 len 个字节,返回实际跳过的字节数。
QDataStream &QDataStream::writeBytes(const char *s, uint len) 将长度 len 和 s 一起写入到文件中,对于 writeBytes() 写入的数据,只能用 readBytes() 方法读取。
int QDataStream::writeRawData(const char *s, int len) 将 s 中前 len 字节的数据写入文件,返回值表示成功写入的字节数。

参考

1、
2、C++入门教程,C++基础教程(更新完毕)
3、探寻C++最快的读取文件的方案
4、C语言文件操作、C++文件操作、QT文件操作汇总及对比分析
5、C/C++文件操作效率比较——FILE/fstream
6、C\C++文件操作对比
7、C语言文件操作
8、C++文件操作
9、QT文件操作
10、Qt QFile文件操作详解
11、fseek、ftell和rewind函数,C语言fseek、ftell和rewind函数详解
12、缓冲区与setvbuf函数
13、C++ 各类输入输出方式效率比较+输入输出优化[含fread、fwrite版]
14、Windows平台下fwrite函数0x0A变成0x0D 0x0A文章来源地址https://www.toymoban.com/news/detail-469401.html

到了这里,关于C/C++/Qt 文件操作 & 效率比较的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • QT文件操作

    时间记录:2024/1/10 文件读写的一些注意点,读文件时文件名可以是相对路径,但是向文件写内容时文件名需要是绝对路径 1.文件对话框 QFileDialog 静态方法getOpenFileName弹出文件选择框,选择要打开的文件,返回打开文件的路径+文件名 2.文件操作 QFile类 常用属性: (1)setFil

    2024年02月03日
    浏览(37)
  • Qt--文件操作

    在Qt中对于文件操作,利用一个例子讲明白,就说通过按钮选择一个文件并将其内容打开到文本框中,注意:需要包含对应的头文件方可执行 读文件的方式: 首先要得到这个要打开的文件的路径,先默认打开的界面是桌面,选择路径时需要判断是否选中,这个通过isEmpty这个

    2024年02月12日
    浏览(29)
  • Qt——文件的读写操作

    文件的读写是很多应用程序具有的功能,甚至某些应用程序就是围绕着某一种格式文件的处理而开发的,所以文件读写是应用程序开发的一个基本功能。 Qt 提供了两种读写纯文本文件的基本方法: 用 QFile 类 的 IODevice 读写功能直接进行读写 利用 QFile 和 QTextStream 结合起来,

    2024年02月13日
    浏览(45)
  • 「Qt」文件读写操作

            我们知道 C 和 C++ 都提供了文件读写的类库,不过 Qt 也有一套自己的文件读写操作;本文主要介绍 Qt 中进行文件读写操作的类 —— QFile 。         一般的桌面应用程序,当我们想要打开一个文件时,通常会弹出一个文件对话框。在 Qt 中,文件对话框使用 QFileDial

    2024年02月12日
    浏览(40)
  • go 、rust、python 语言 编码效率、性能比较

    1、 Rust适合内存使用苛刻、无GC、超高性能的场景 , 如果是实时计算系统,那rust的吞吐量对于Go还是有一定优势的,基于线程和goroutine的调度模式还是有差别的。能用他的都是高手,代码量大,内存占用不高, 20个线程,每个线程运行50亿次,rust和Go用时,16.5s vs 36秒,内存占

    2024年02月10日
    浏览(40)
  • 十、Qt 操作PDF文件

     《一、QT的前世今生》 《二、QT下载、安装及问题解决(windows系统)》 《三、Qt Creator使用》 ​​​ 《四、Qt 的第一个demo-CSDN博客》 《五、带登录窗体的demo》 《六、新建窗体时,几种窗体的区别》  《七、Qt 信号和槽》  《八、Qt C++ 毕业设计》 《九、Qt C++ 数据库开发》

    2024年01月17日
    浏览(40)
  • 【Qt学习】08:文件读写操作

    文件操作是应用程序必不可少的部分,Qt 作为一个通用开发库提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有 读写字节块 的能力,以下是 I/O 设备的类图: QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;

    2024年02月11日
    浏览(38)
  • 【Qt】Qt中的拖放操作实现——拖放文件以及自定义拖放操作

    文章参考《Qt Creator快速入门(第三版)》。 拖放操作分为拖动Drag和放下Drop,Qt提供了强大的拖放机制,可在帮助文档中通过Drag and Drop查看。 在Qt中,数据拖动时会被存储为MIME类型(Multipurpose Internet Mail Extensions)。Qt提供QMimeData类表示MIME类型的数据,并使用QDrag类完成数

    2023年04月19日
    浏览(56)
  • Qt文件 I/O 操作

    逐行读取 逐行写入(文件不存在即自动创建) 读取全部内容 使用流式操作符读取 QIODevice::ReadWrite   以读写打开 QIODevice::WriteOnly    以只写打开 QIODevice::Append    以追加的方式打开,新增加的内容将被追加到文件末尾 QIODevice::Truncate    以重写的方式打开,在写入新的数据

    2024年02月07日
    浏览(29)
  • 十二、Qt 操作PDF文件(2)

    一、在《十、Qt 操作PDF文件-CSDN博客》中我们用Poppler类库打开了PDF文件,并显示到窗体上,但只能显示一页,功能还没完善,在本章节中,加入了: 通过选择框选择PDF文件并打开,默认打开第一页。 通过上一页、下一页按钮实现翻页功能。 通过第一页、最后一页、跳转到某

    2024年01月19日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包