C++中随机存取文件的处理

开发 后端
在本文中,我们所讲的是怎样使用C++中的随机存取文件。除了最简单的应用程序以外,大多数程序都需要读写文件。或许只是为了读取一个配置文件,一个文本解析器或更为复杂的什么东西。

和许多的C++程序一样,有些人更喜欢用原先的C语言方式处理问题,如果你恰好也是这些人中的一员,就应该学习一下这篇文章。

基本的文件操作有

◆fopen——打开文件,指定文件以怎样的方式打开(读/写)以及类型(二进制/文本)

◆fclose——关闭已经打开的文件

◆fread——读取文件

◆fwrite——写文件

◆fseek/fsetpos——将文件指示器转移到文件中的某一地方

◆ftell/fgetpos——可以告诉你文件指示器所在的位置

文件有两种基本类型:文本和二进制。在这两者之中,通常二进制类型是较容易解决的。由于在文本中处理随机存取并不常用,我们会在本文中重点关注二进制文件的处理。上面列出的操作中的前四项可用于文本文件和随机存取文件。后面的两项则仅用于随机存取。

随机存取意味着我们可以在文件的任意部分之间进行切换,且可以从中读写数据而不需要通读整篇文件。

二进制文件

二进制文件是任意长度的文件,它保存有从0到0xff(0到255)不等的字节值。这些字节在二进制文件中没有任何意义,与此不同的是,在文本文件中,值为13就意味着回车,10意味着换行,26意味着文件结束,而读取文本文件的软件要能够解决这些问题。

在现在的术语中,我们将二进制文件称为包含了字节的字符流,大多数语言倾向于将其理解为字符流而不是文件。重要的部分是数据流本身而不是其来源。在C语言中,你能从文件或数据流方面来考虑数据。或者,你可以将其理解为一组长的数组。通过随机存取,你可以读写数组的任意部分。

      例一:
// ex1.c : Defines the entry point for the console application.

//

#include < stdio.h>

#include < string.h>

#include < windows.h>

int FileSuccess(FILE * handle,const char * reason, const char * path) {

OutputDebugString( reason );

OutputDebugString( path );

OutputDebugString(" Result : ");

if (handle==0)

{

OutputDebugString("Failed");

return 0;

}

else

{

OutputDebugString("Suceeded");

return 1;

}

}

int main(int argc, char * argv[])

{

const char * filename="test.txt";

const char * mytext="Once upon a time there were three bears.";

int byteswritten=0;

FILE * ft= fopen(filename, "wb");

if (FileSuccess(ft,"Opening File: ", filename)) {

fwrite(mytext,sizeof(char),strlen(mytext), ft);

fclose( ft );

}

printf("len of mytext = %i ",strlen(mytext));

return 0;

}
 

这段代码显示了一个简单的打开待写的二进制文件,文本字符(char*)会写入该文件。通常你会使用文本文件但是笔者想证明你可以向二进制文件写入文本。

     // ex1.c

     #include < stdio.h>

     #include < string.h>

     int main(int argc, char * argv[])

    {

     const char * filename="test.txt";

     const char * mytext="Once upon a time there were three bears.";

     int byteswritten=0;

     FILE * ft= fopen(filename, "wb") ;

     if (ft) {

     fwrite(mytext,sizeof(char),strlen(mytext), ft) ;

     fclose( ft ) ;

     }

      printf("len of mytext = %i ",strlen(mytext)) ;

     return 0;

     }
 

例一的作用

这个例子打开了一个待写的二进制文件。FILE*变量从fopen()调用中返回。如果这一操作失败那么它会返回为0。

Fopen()命令试图打开指定的文件,在这个案例中则是位于相同文件夹的test.txt。记住,如果文件包含一个路径那么所有的退格必须重叠。“c:\folder\test.txt”是错误的,你必须使用“c:\\folder\\test.txt”。

由于文件样式是wb,我们正准备写入二进制文件。如果文件不存在则系统会创建一个文件,如果存在,则里面的内容都会被删除。如果调用fopen失败了,或许由于文件被打开了,或者其名称包括无效字符又或者一个无效路径那么fopen会返回0值。

虽然你可以只检查ft是否为0(值为0则成功),但是笔者还是添加了一个FileSuccess()函数来确保这一操作。在窗口中,它会显示调用是否成功以及文件名称。如果你失败了则可能需要修复。注意在Windows中一般没有多少输出文本可供系统调试器使用,

 fwrite(mytext,sizeof(char),strlen(mytext), ft) ; fwrite()调用输出了指定文本。第二,三个参数分别是字符的大小和字符串的长度。它们两个都是被size_t定义了。注意有了二进制文件后,即便你正在向文件中写入(char*)字符串,它也没有任何附加换行字符。如果你想要这些字符,你必须明确将这些字符包含到字符串中。

读写文件

打开一个文件的时候,必须指定打开的方式。这意味着如果你打算要为文件附加东西,那么是否要创建新文件并对其进行覆盖?它是文本文件还是二进制文件呢?是要读取文件还是要写文件呢?这样要通过使用一个或更多的文件模式分类符来完成,文件模式分类符是一些单独的字母“r”,“b”,“w”,“a”和+。“r”意思是打开文件以便读取。如果文件不存在或找不到文件这一操作会失败。“w”意思是待写方式或空文件方式打开文件。如果文件存在,则文件内容会被损坏。“a”表示打开文件,并准备从文件末端写入而不需要在写入新数据前删除EOF标记;如果不存在该文件则首先会创建一个文件。向文件模型添加+会创建下列三种新模型:

“r+”打开文件等待读取或写入。“w+”以空文件方式打开文件等待读取或写入。如果文件存在,则文件内容会被损毁。

“a+”打开文件等待读取或添加,添加的操作包括新数据写入前EOF标记的移除,以及写入完成后EOF标记的保存,如果文件不存在则先要创建文件。

下面的列表显示了字码组合包括文本的和二进制文件的。通常你可以选择从文本文件中读取或写入文件,但是不要两者同时使用。

就二进制文件而言,你可以选择对相同文件进行读取和写入的操作。列表告诉了我们可以用字码进行哪些操作。

       Mode Type of file Read Write Create Truncate 
r text Read

rb+ binary Read

r+ text Read Write

r+b binary Read Write

rb+ binary Read Write

w text Write Create Truncate

wb binary Write Create Truncate

w+ text Read Write Create Truncate

w+b binary Read Write Create Truncate

wb+ binary Read Write Create Truncate

a text Write Create

ab binary Write Create

a+ text Read Write Create

a+b binary Write Create

ab+ binary Write Create
 

就笔者的经验来看,除非你刚刚创建完文件或读取完文件,否则你通过使用“w+b”只能侥幸成功。

还有一些情况允许其他字母存在。例如微软运行“t”代表文本模式,“c”用于认可,“n”用于非认可,“S”为顺序存取优化缓冲,“R”代表随机存取,“T”代表临时性而“D”用于 删除/临时性保存。

使用二进制文件的主要原因是可以获得灵活性;你可以读取或写入文件的任意部分。文本文件只能让你按照顺序读取或写入。现在随着SQLite或MySQL数据库的普及,在二进制文件中使用随机存取的需要减少了。从某种意义上说,随机存取文件记录有点老土了,但是仍然还是有用的。

笔者在数据库普及之前就使用过基于随机存取文件的多种数据处理方案。例如,在小文件中,笔者使用的是 索引/数据 文件模式。该模式包括两个文件。一个是数据文件,它保存了一些长短不一的记录。另一种文件是索引文件,这样的文件拥有同样的记录作为数据文件。但是在索引文件中每个记录的长度相同而且都由两个适合结构的部分组成。

struct {??fpos_t pos;??int size; } indexrec; 

类型fpos_t是由fsetpos()和fgetpos()定义和使用的执行。这些是fseek和ftell更新的版本且更有助于创建书签。如果你正在计算文件闻之且需要设定文件那么你应该使用fseek(),另外ftell()也可以给你int的当前位置。

在实际操作中,fpos_t可能只是一个int但是你应该使用fpos_t类型。它保存了当前文件指示器的副本。这是随机存取文件的属性,它表明了下一次读取或写入的位置。它的粒度为一,因此你可以将其放在文件的任意位置。

【编辑推荐】

  1. C++、Java与C#的命名规范总结
  2. 在C++中使用Lambda函数提高代码性能
  3. 给C++初学者的50条建议
责任编辑:彭凡 来源: IT专家网
相关推荐

2009-12-16 10:41:37

Ruby随机存取文件

2010-05-05 10:20:43

Java

2010-01-21 09:34:57

C++语法

2010-01-27 16:05:06

C++堆栈

2023-12-04 09:37:00

C++静态变量

2010-01-21 14:07:14

CC++声明

2018-01-29 08:44:14

2010-01-25 10:25:19

C++变量

2010-01-27 17:16:52

C++构造函数

2010-01-20 10:19:55

C++数组

2010-01-28 16:31:54

C++类型

2010-01-21 13:33:44

C++基类

2020-08-21 13:20:36

C++If ElseLinux

2019-01-04 10:41:07

系统内存SRAM

2010-01-26 10:42:26

C++函数

2024-01-25 11:32:21

2015-12-28 11:25:51

C++异常处理机制

2024-05-31 12:50:49

C++编程NaN

2011-04-11 11:09:50

this指针

2010-01-26 14:35:11

C++关键字
点赞
收藏

51CTO技术栈公众号