15

我想要做的是下载一个包含多个目录的 .tar 文件,每个目录有 2 个文件。问题是我找不到在不实际提取文件的情况下读取 tar 文件的方法(使用tar)。

完美的解决方案是这样的:

#include <easytar>

Tarfile tar("somefile.tar");
std::string currentFile, currentFileName;
for(int i=0; i<tar.size(); i++){
  file = tar.getFileText(i);
  currentFileName = tar.getFileName(i);
  // do stuff with it
}

我可能不得不自己写这个,但任何想法都会受到赞赏..

4

3 回答 3

34

经过一番工作,我自己弄清楚了这一点。tar 文件规范实际上会告诉您您需要知道的一切。

首先,每个文件都以 512 字节的标题开头,因此您可以使用 char[512] 或 char* 来表示它,指向较大的 char 数组中的某个位置(例如,如果您将整个文件加载到一个数组中)。

标题如下所示:

location  size  field
0         100   File name
100       8     File mode
108       8     Owner's numeric user ID
116       8     Group's numeric user ID
124       12    File size in bytes
136       12    Last modification time in numeric Unix time format
148       8     Checksum for header block
156       1     Link indicator (file type)
157       100   Name of linked file

所以如果你想要文件名,你可以在这里用string filename(buffer[0], 100);. 文件名填充了空值,因此您可以进行检查以确保至少有一个空值,然后如果您想节省空间则不要使用该大小。

现在我们想知道它是文件还是文件夹。“链接指示器”字段包含此信息,因此:

// Note that we're comparing to ascii numbers, not ints
switch(buffer[156]){
    case '0': // intentionally dropping through
    case '\0':
        // normal file
        break;
    case '1':
        // hard link
        break;
    case '2':
        // symbolic link
        break;
    case '3':
        // device file/special file
        break;
    case '4':
        // block device
        break;
    case '5':
        // directory
        break;
    case '6':
        // named pipe
        break;
}

至此,我们已经拥有了有关目录所需的所有信息,但我们还需要普通文件中的另一件事:实际的文件内容。

文件的长度可以以两种不同的方式存储,或者作为 0 或空格填充的以空结尾的八进制字符串,或者“通过设置最左边字节的高位来指示的 base-256 编码的数字字段”。

数值使用 ASCII 数字以八进制数编码,前导零。由于历史原因,应使用最终的 NUL 或空格字符。因此,尽管为存储文件大小保留了 12 个字节,但只能存储 11 个八进制数字。这使归档文件的最大文件大小为 8 GB。为了克服这个限制,star 在 2001 年引入了 base-256 编码,通过设置数字字段最左边字节的高位来表示。GNU-tar 和 BSD-tar 遵循了这个想法。此外,1988 年第一个 POSIX 标准之前的 tar 版本用空格而不是零填充值。

以下是您阅读八进制格式的方法,但我还没有为 base-256 版本编写代码:

// in one function
int size_of_file = octal_string_to_int(&buffer[124], 11);

// elsewhere
int octal_string_to_int(char *current_char, unsigned int size){
    unsigned int output = 0;
    while(size > 0){
        output = output * 8 + *current_char - '0';
        current_char++;
        size--;
    }
    return output;
}

好的,现在我们有了除了实际文件内容之外的所有内容。我们所要做的就是size从 tar 文件中获取下一个字节的数据,我们将拥有我们的文件内容:

// Get to the next block after the header ends
location += 512;
file_contents = new char[size];
memcpy(file_contents, &buffer[location], size);
// Go to the next block by rounding up to 512
// This isn't necessarily the most efficient way to do this,
// but it's the most obvious.
location += (int)ceil(size / 512.0)
于 2010-03-24T21:25:48.923 回答
12

你看过libtar吗?

从 fink 包信息:

libtar-1.2-1:Tar 文件操作 API libtar 是一个用于操作 POSIX tar 文件的 C 库。它处理向/从 tar 存档添加和提取文件。libtar 提供以下功能:
* 灵活的 API - 您可以操作单个文件或一次提取整个存档。
* 允许用户指定的 read() 和 write() 函数,例如 zlib 的 gzread() 和 gzwrite()。
* 支持 POSIX 1003.1-1990 和 GNU tar 文件格式。

不是 c++本身,但你可以很容易地链接到 c ......

于 2010-03-24T02:55:38.830 回答
4

libarchive 可以是解析 tarball 的开源库。Libarchive 可以从一个归档文件中读取每个文件而无需提取,也可以写入数据以形成一个新的归档文件。

于 2016-01-13T15:29:49.923 回答