lseek的问题:Fri, Dec 18th, 2020

在做*的过程中,遇到了一个 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也有可能是负偏移。

  1. 如果 whence 是 SEEK_SET,文件偏移量将被设置为 offset。
  2. 如果 whence 是 SEEK_CUR,文件偏移量将被设置为 cfo 加上 offset,offset 可以为正也可以为负
  3. 如果 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()是什么也是可以对应出来的。
  1. istream &seekg(streamoff offset,seek_dir origin);

  2. ostream &seekp(streamoff offset,seek_dir origin);

  • 这时候我们发现又一个streamoff,猜也知道这个和streampos一个性质的。
  • 总之,这个lseek是可以用C++替换的

来源:https://blog.csdn.net/mafuli007/article/details/7314917

上一篇:结构体嵌套


下一篇:使用Consul实现服务发现:instance-id自定义(3种方式)