在做*的过程中,遇到了一个
lseek
的函数,纠结于:第一:何为lseek
?第二:是不是标准库,是C
还是C++
的?第三:既然我们需要用C++
,就应该用C++ standard lib
,有无替代品能不让C++
和C
混在一起?
什么是空洞文件?
空洞文件是
UNIX
操作系统的一个概念,就是文件指针的偏移量可以大过这个文件本身,于是乎产生了空洞,目的是为了让多线程同时写这个文件(因为文件中可以制造很多空洞)空洞是否占用空间,取决于上层的文件系统如何写。
lseek是什么?
所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了
O_APPEND
- lseek需要两个头文件,第一个是
unistd.h
,第二个是<sys/types.h>
unistd.h 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。是Unix Standard的缩写。该头文件由 POSIX.1 标准(单一UNIX规范的基础)提出,故所有遵循该标准的操作系统和编译器均应提供该头文件(如 Unix 的所有官方版本,包括 Mac OS X、Linux 等)。对于类 Unix 系统,unistd.h 中所定义的接口通常都是大量针对系统调用的封装(英语:wrapper functions),如 fork、pipe 以及各种 I/O 原语(read、write、close 等等)。
类似于 Cygwin 和 MinGW 的 Unix 兼容层也提供相应版本的 unistd.h。
- 所以我们可以看出:Windows 是没有lseek的,MacOS 和 Linux 有lseek
- 那么
<sys/types.h>
是什么呢???sys/types.h
描述了不同的类型,也是C POSIX的东西 - 综上,Windows没有lseek, lseek也不是C标准库的
由于本人正在翻译别人写的一个代码,他用了lseek,而我不想用,但是我总得知道lseek怎么用吧。。那么lseek如何用呢?
-
函数原型为:
off_t lseek(int filedis, off_t offset, int whence);
成功返回偏移量,失败返回-1 (对socket和pipe也返回-1) -
off_t类型用于指示文件的偏移量,常就是long类型,其默认为一个32位的整数,在gcc编译中会被编译为long int类型,在64位的Linux系统中则会被编译为long long int,这是一个64位的整数,其定义在unistd.h头文件中可以查看。
-
什么是file discriptor?????????????
-
这是一个重要的话题,弄明白这个之前我们首先要搞清楚什么是标准输入输出
-
-
linux中有三种标准输入输出,分别是STDIN,STDOUT,STDERR,对应的数字是0,1,2。STDIN是标准输入,默认从键盘读取信息;STDOUT是标准输出,默认将输出结果输出至终端;STDERR是标准错误,默认将输出结果输出至终端。
-
然后上述废话和file discriptor有什么关系呢?????因为最常用的fd是012
-
注意!这并不代表: fd 只能有012,fd理论上来说可以取值到几千几万,工业甚至还有解放fd数量限制的调优操作,因为有一个说法,fd会被系统限制在内存的百分之十左右,文件类型远不止标准输入输出这么多,还有管道,socket,目录等。
-
另外一个注意的点,我们应该判断lseek是否返回-1来判断对错而不是判断是否<0,因为cfo也有可能是负偏移。
- 如果 whence 是 SEEK_SET,文件偏移量将被设置为 offset。
- 如果 whence 是 SEEK_CUR,文件偏移量将被设置为 cfo 加上 offset,offset 可以为正也可以为负
- 如果 whence 是 SEEK_END,文件偏移量将被设置为文件长度加上 offset,
offset 可以为正也可以为负
说了这么多,我并不想用lseek,那么有漂亮的解决办法么????
-
目前的构想是用C++的seekg和seekp 与 tellg和tellp 搭配着来
what is seekg?
-
seekg这一系列东西是C++的,一图胜千言
// read a file into memory #include <iostream> // std::cout #include <fstream> // std::ifstream int main () { std::ifstream is ("test.txt", std::ifstream::binary); if (is) { // get length of file: is.seekg (0, is.end); int length = is.tellg(); is.seekg (0, is.beg); // allocate memory: char * buffer = new char [length]; // read data as a block: is.read (buffer,length); is.close(); // print content: std::cout.write (buffer,length); delete[] buffer; } return 0; }
-
我们不难发现,seekg有两个参数,第一个参数是offset,第二个参数是“让它以何种形式偏移”。
-
ios::beg:表示输入流的开始位置
-
ios::cur:表示输入流的当前位置
-
ios::end:表示输入流的结束位置
-
假设是在“ios::end”处开始,偏移0位,那么指针很显然就应该在结尾,就是这个道理,所以is.tellg()会返回什么呢??会返回的是:文件头到这个指针的距离。问题来了,为什么是返回一个streampos呢?
-
经过查文档,发现这个返回值就是当成整数去弄,为什么?查不到什么确切答案,但是有迹可循的是,streampos虽然是个模版,但是可以当作是integral type,即:int short long可以直接互相比较,streampos也可以和它们互相比较,但是不能用<=(*上有过深刻的讨论,这个东西是否能完全等效于unsigned long long int,不赘述,感兴趣就去搜)
-
个人认为比C优雅的地方
- 和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置,tellp()是什么也是可以对应出来的。
-
istream &seekg(streamoff offset,seek_dir origin);
-
ostream &seekp(streamoff offset,seek_dir origin);
- 这时候我们发现又一个streamoff,猜也知道这个和streampos一个性质的。
-
总之,这个lseek是可以用C++替换的