文件读写(C++)
文件操作与内存分配一样,是在程序中频繁使用的操作。包括文件的创建、打开、读写、关闭等。
下面总结C语言、C++语言、VC、Win32、内核的文件操作:
需要特别指出的是,在读写文件时,文件中的换行标志在Windows中为"\r\n",而在Linux等系统中, 为"\n"。
C语言文件访问:
1)标准C方式:
FILE *fp;
//打开
fp = fopen("path\to\file","rwt");
//文件读写
fread(buffer, size, ntime, fp);
fwrite(buffer, size, ntime, fp);
//格式化输入/输出
fscanf(fp,"%s,...", String1, ...);
fprintf(fp, "%d,..", a, ..);
//字符输入/输出
fgetc(fp);
fputc(c,fp);
//字符串输入/输出
fgets(name[], maxsize, fp);
fputs(name, fp);
//调整读写的指针
rewind(fp);
fseek(fp, 50, 0/1/2);
//关闭文件句柄
fclose(fp);
//判断文件是否结束
feof(pf);
在打开文件时候,需要设置打开文件的标志。一些常见的标志见下表:
标志 | 含义 |
r | 读模式,如果文件不存在则会失败 |
w | 写模式,如果文件存在则会清空其内容 |
a | 追加模式,写入的内容追加在原有内容之后,如果文件不存在则会自动创建 |
r+ | 读/写模式,文件必须存在 |
a+ | 读+追加模式 |
t | 文本方式 |
b | 二进制方式 |
css=ENCODING | 文件编码方式,可指定ENCODING为UTF-8, UTF-16LE, UNICODE |
eg:关于文件操作的面试题:设计一个程序算法,使其读文件file1.txt中的数据:
12
34
56
按以下顺序输出到file2.txt:
56
34
12
代码如下:
void reversefile(FILE* fread, FILE* fwrite)
{
assert(fread != NULL & fwrite != NULL);
char buf[1024] = {0};
if(!fgets(buf, sizeof(buf), fread))
return;
reverse(fread, fwrite);
fputs(buf, fwrite);
}
int reveref1tof2(const char * rfile, const char * wfile)
{
FILE * fr = NULL;
FILE * fw = NULL;
fr = fopen(rfile, "rb");
if(fr == NULL)
exit(-1);
fw = fopen(wfile, "wb");
if(fw == NULL)
exit(-1);
reversefile(fr, fw);
fclose(fr);
fclose(fw);
return 0;
}
2) UNIX方式
在UNIX中,文件读写方式如下:
int fd;
fd = open("path\to\file", O_CREATE|O_WRONLY|O_RDONLY);
write(fd, buffer, size);
read(fd, buffer, &size);
close(fd);
open与fopen都可以打开和访问一个文件,那么它们的区别是什么呢?
实际上, open是UNIX系统调用如下(图1示),它返回一个文件句柄,fopen是一个C的标准库函数,返回一个文件指针。open不是标准IO库的函数,用open打开的文件读写是不带缓存的,直接读写磁盘数据。fopen是C标准IO库的函数(如图2),用fopen打开的文件读写是带缓存的,即用fwrite向文件里写一个字节,一般来讲它不会立刻调用write将该操作提交给kernel,而是积累到一定程度再一起写。从返回值来看,fopen返回FILE *的指针,这个结构维护着一些关于这个文件的信息,而open返回的是这个被打开的文件的id,这个id是内核来维护的。但fopen返回的FILE结构记录着这个文件的id。
图1示(open()打开方式):
图2示(fopen()打开方式):
C++语言文件访问
在C++中,文件访问方式如下:
ofstream outFile("path\to\file",ios::out);
ifstream inFile("path\to\file", ios::in);
inFile.read(buffer, size);
outFile.wreite(buffer,size);
inFile.close();
outFile.close();
C++对文件的操作如下图求:
VC文件访问
在VC中,文件访问方式如下:
CFile file;
file.Open("path\to\file", CFile::modeCreate|CFile::modeRead|CFile::modeWrite);
file.Read(buffer, size);
file.Write(buffer,size);
file.Close();
Win32文件访问:
在Win32中,文件访问方式如下:
HANDLE hHandle;
DWORD dwError;
hHandle = CreateFile(TEXT("Path\\to\\file"),
GENERIC_READ|GENERIC_WRITE,
0, // shareMode
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
if(hHandle == INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
}
BOOL bWrite, bRead;
char buf[100] = "Hello, world";
DWORD n;
bWrite = WriteFile(hHandle, buf, sizeof(buf), &n, 0);
if(bWrite == 0)
{
dwError = GetLastError();
}
CloseHandle(hHandle);
hHandle = CreateFile(TEXT("Path\\to\\file"),
GENERIC_READ|GENERIC_WRITE,
0, //shareMode
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
bRead = ReadFile(hHandle, buf, sizeof(buf), &n, 0);
if(bRead == 0)
{
dwError = GetLastError();
}
CloseHandle(hHandle);
内核文件访问:
在内核态要访问文件,不能直接调用 上面的方法,而必须调用内核态专门访问文件的函数。
1)Linux内核文件访问方式
struct file * kopen(const char * filename, int flags, int mode)
{
struct file * f = NULL;
f = filp_open(filename, flags, mode);
return f;
}
ssize_t kread(struct file* f1, char *buf, size_t count)
{
ssize_t ret = 0;
struct file * file = NULL;
mm_segment_t fs;
file = f1;
if(file)
{
ssize_t(*read)(struct file*, char*, size_t, loff_t *);
fs = get_fs();
set_fs(KERNEL_DS);
if(file->f_op && (read=file->f_op->read) != NULL)
red = read(file,buf,count,&file->f_pos);
set_fs(fs);
}
return ret;
}
ssize_t kwrite(struct file* f1, const char * buf, size_t count)
{
ssize_t ret;
struct file * file;
mm_segment_t fs;
file = f1;
if(file)
{
ssize_t(*write)(struct file*, const char*, size_t, loff_t*);
fs=get_fs();
set_fs(KERNEL_DS);
if(file->f_op && (write=file->f_op->write) != NULL)
ret = write(file, buf, count, &file->f_pos);
set_fs(fs);
}
return ret;
}
2)Windows内核文件访问方式:
Windows内核文件访问方式如下:
NTSTATUS KOpen(const WCHAR* filename, HANDLE &h)
{
UNICODE_STRING uFile;
OBJECT_ATTRIBUTES objAttrib;
NTSTATUS status;
RtlInitUnicodeString(&uFile, filename);
InitializeObjectAttributes(&objAttrib,
&uFile,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateFile(
&h,
FILE_READ_DATA|FILE_READ_ATTRIBUTES,
&objAttrib,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
return status;
}
NTSTATUS KRead(HANDLE h, PVOID buff, ULONG * len)
{
LARGE_INTEGER offset ={0};
IO_STATUS_BLOCK io_status={0};
NTSTATUS status;
status = ZwReadFile(
h,NULL, NULL,NULL,
&io_status, buffer, *len, &offset,
NULL);
*len = (ULONG)io_status.Information;
return status;
}
NTSTATUS KWrite(HANDLE h, PVOID buff, ULONG len)
{
LARGE_INTEGER offset ={0};
IO_STATUS_BLOCK io_status ={0};
NTSTATUS status;
status = ZwWriteFile(
h,NULL, NULL, NULL,
&io_status,buffer, len,offset,
NULL);
offset.QuadPart += len;
return status;
}