2017-02-23 84 views
2

我需要使用C同时读取不同偏移量的文件。 dup不幸的是,创建一个与原始文件共享偏移量和标志的文件描述符。C文件描述符重复没有共享偏移量或标志

是否有像dup这样的函数不共享偏移量和标志?

编辑我只能访问到文件指针FILE* fp;我没有文件路径

编辑这个程序被编译为除了Mac和Linux的许多风味窗口

解决方案 我写了windows的pread函数来解决这个问题 https://github.com/Storj/libstorj/blob/master/src/utils.c#L179

+1

'的open()'文件两次? – EOF

+0

我只能访问文件指针。 –

回答

0

我在下面的线程的代码片段使用一个文件指针在POSIX或Windows读取文件:

#ifdef _WIN32 
ssize_t pread(int fd, void *buf, size_t count, uint64_t offset) 
{ 
    long unsigned int read_bytes = 0; 

    OVERLAPPED overlapped; 
    memset(&overlapped, 0, sizeof(OVERLAPPED)); 

    overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32); 
    overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL); 

    HANDLE file = (HANDLE)_get_osfhandle(fd); 
    SetLastError(0); 
    bool RF = ReadFile(file, buf, count, &read_bytes, &overlapped); 

    // For some reason it errors when it hits end of file so we don't want to check that 
    if ((RF == 0) && GetLastError() != ERROR_HANDLE_EOF) { 
     errno = GetLastError(); 
     // printf ("Error reading file : %d\n", GetLastError()); 
     return -1; 
    } 

    return read_bytes; 
} 

ssize_t pwrite(int fd, const void *buf, size_t count, uint64_t offset) 
{ 
    long unsigned int written_bytes = 0; 

    OVERLAPPED overlapped; 
    memset(&overlapped, 0, sizeof(OVERLAPPED)); 

    overlapped.OffsetHigh = (uint32_t)((offset & 0xFFFFFFFF00000000LL) >> 32); 
    overlapped.Offset = (uint32_t)(offset & 0xFFFFFFFFLL); 

    HANDLE file = (HANDLE)_get_osfhandle(fd); 
    SetLastError(0); 
    bool RF = WriteFile(file, buf, count, &written_bytes, &overlapped); 
    if ((RF == 0)) { 
     errno = GetLastError(); 
     // printf ("Error reading file :%d\n", GetLastError()); 
     return -1; 
    } 

    return written_bytes; 
} 
#endif 
1

不,既不是C也不是POSIX(因为您提到了dup())具有基于现有文件句柄打开新的独立文件句柄的功能。正如你所观察到的,你可以使用dup()一个文件描述符,但结果指的是相同的底层打开文件描述。

要得到一个独立的句柄,你需要open()fopen()相同的路径(这是可能的,只有在FILE指物体通过文件系统访问)。如果你不知道是什么路径,或者如果没有任何的路径,那么你需要一种不同的方法。

一些替代品来考虑:

  • 缓冲部分或全部在内存中的文件内容,并读取缓冲区根据需要提供给您的独立文件偏移量的需求;
  • 构建内部等效命令tee;这可能需要第二个线程,并且您可能无法读取一个文件超过另一个文件,或者找不到任何一个;
  • 将文件内容复制到一个具有已知名称的临时文件,并根据需要打开多次;
  • 如果FILE对应一个普通文件,将其映射到内存中并访问其中的内容。在这种情况下,POSIX函数fmemopen()可能有助于将内存映射调整为现有的基于流的使用。
+0

一个临时的Linux唯一的解决方案似乎也像 'sprintf(path,“/ proc/self/fd /%d”,fileno(state-> original_file));'我这样做的主要原因是因为我停止将任何文件写入磁盘这些文件的数据量可达数千兆字节,因此我必须查看内存映射文件。 –

2

在Linux上,你可以从/proc/self/fd/N恢复的文件名,其中N是文件描述符的积分值:

sprintf(linkname, "/prod/self/fd/%d", fd); 

然后使用“的readlink()”,对得到的链接名称。

如果该文件已被重命名或删除,您可能会运气不好。

但是为什么你需要另一个文件描述符吗?您可以使用原始文件描述符上的pread()和/或pwrite()来从文件读取/写入文件,而不影响当前偏移量。(注意:在Linux上,pwrite()以append模式打开的文件是越野车 - POSIX指出pwrite()以append模式打开的文件会写入pwrite()调用中指定的偏移量,但Linux pwrite()实现中断并且将忽略偏移并将数据追加到文件末尾 - see the BUGS section of the Linux man page

+0

有没有与此相当的窗户?我编译了许多不同操作系统的程序 –

+0

@AlexanderLeitner这可能是相关的(我没有足够深入的Windows编程经验可以肯定):[GetFinalPathNameByHandle](https://msdn.microsoft.com/en-us /library/windows/desktop/aa364962%28v=vs.85%29.aspx) –

1

在windows(假设为VisualStudio)上,您可以从stdio FILE句柄访问OS文件句柄。 从那里重新打开并转换回新的FILE句柄。

这只是Windows,但我认为安德鲁斯的答案会适用于Linux,也可能适用于Mac - 不幸的是,没有便携的方法可以在所有系统上使用它。

#include <Windows.h> 
#include <fcntl.h> 
#include <io.h> 
#include <stdio.h> 

FILE *jreopen(FILE* f) 
{ 
    int n = _fileno(f); 
    HANDLE h = (HANDLE)_get_osfhandle(n); 
    HANDLE h2 = ReOpenFile(h, GENERIC_READ, FILE_SHARE_READ, 0); 
    int n2 = _open_osfhandle((intptr_t)h2, _O_RDONLY); 
    FILE* g = _fdopen(n2, "r"); 

    return g; 
}