我需要使用C的不同偏移中的文件同时阅读。 dup
不正确地创建了一个文件描述符,该文件描述符与原件共享偏移和标志。
是否有像dup
这样的函数不共享偏移和标志?
编辑我只能访问文件指针FILE* fp;
我没有文件路径
编辑此程序除了Mac和Linux的许多口味外,还为Windows编译了此程序
解决方案我们可以在POSIX系统上使用PREAD,我为Windows编写了一个PREAD功能,该功能解决了此问题https://github.com/storj/libstorj/blob/master/src/src/utils.c#l227
在Linux上,您可以从/proc/self/fd/N
恢复文件名,其中N
是文件描述符的积分值:
sprintf( linkname, "/proc/self/fd/%d", fd );
然后在生成的链接名称上使用readlink()
。
如果文件已重命名或删除,您可能会不幸。
但是为什么您需要另一个文件描述符?您可以在原始文件描述符上使用pread()
和/或pwrite()
在不影响当前偏移的情况下从/写入/写入/写入/写入。(警告:在Linux上,pwrite()
到以附加模式打开的文件是错误的-POSIX指出,在附加模式下打开的pwrite()
将写入pwrite()
调用中指定的偏移量,但Linux pwrite()
实现已损坏,并且会忽略该文件。偏移并将数据附加到文件的末尾 - 请参阅Linux Man页面的错误部分)
no,c和posix(因为您提到的dup()
)都具有基于现有文件句柄打开新的独立文件句柄的功能。如您所观察到的,您可以dup()
文件描述符,但结果指的是基础的开放文件描述。
要获得独立的手柄,您需要open()
或fopen()
相同的路径(仅当FILE
涉及通过文件系统访问的对象时,才有可能。如果您不知道那是什么路径,或者首先没有任何路径,那么您将需要其他方法。
一些考虑的选择:
- 缓冲器中的某些或全部文件内容,并根据需要从缓冲区读取以满足您对独立文件偏移的需求;
- 构建
tee
命令的内部等效;这可能需要第二个线程,并且您可能无法读取一个文件超过另一个文件,或者在任何一个文件中读取; - 将文件内容复制到带有已知名称的临时文件,并根据需要打开多次;
- 如果文件对应于常规文件,请将其映射到内存中并在那里访问其内容。POSIX函数
fmemopen()
在这种情况下可能很有用,可以使内存映射适应您现有的基于流的用法。
在Windows上(假设VisualStudio),您可以从STDIO文件句柄中访问OS文件句柄。从那里重新打开并将其转换回新的文件句柄。
这仅是Windows,但我认为Andrews的答案也适用于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;
}
我能够在POSIX系统上使用PREAD和PWRITE,并且我将Windows Systems上的ReadFile/WriteFile包装到PREAD和PWRITE功能
#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 : %dn", 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 :%dn", GetLastError());
return -1;
}
return written_bytes;
}
#endif