2010-03-24 36 views
14

Điều tôi muốn làm là tải xuống tệp .tar có nhiều thư mục chứa 2 tệp. Vấn đề là tôi không thể tìm thấy một cách để đọc tập tin tar mà không thực sự giải nén các tập tin (sử dụng tar).Cách phân tích tệp tar trong C++

Các giải pháp hoàn hảo sẽ là một cái gì đó như:

#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 
} 

Tôi có thể sẽ phải viết này bản thân mình, nhưng bất kỳ ý tưởng sẽ được đánh giá ..

+1

'man tar' cho tôi biết' -t Liệt kê nội dung lưu trữ để stdout.' Đó có phải là những gì bạn muốn không? – Potatoswatter

+1

Điều tôi thực sự muốn là ngược lại: đọc một tập tin tar từ stdin. –

Trả lời

29

tôi figured này ra bản thân mình sau một chút công việc. tar file spec thực sự cho bạn biết mọi thứ bạn cần biết. Trước hết, mỗi tập tin bắt đầu với tiêu đề 512 byte, vì vậy bạn có thể biểu diễn nó bằng một ký tự char [512] hoặc char * trỏ vào vị trí nào đó trong mảng char lớn hơn của bạn (nếu bạn có toàn bộ tệp được tải vào một mảng). ví dụ).

Phần header trông như thế này:

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 

Vì vậy, nếu bạn muốn tên tập tin, bạn lấy nó ngay tại đây với string filename(buffer[0], 100);. Tên tập tin là rỗng đệm, vì vậy bạn có thể làm một kiểm tra để đảm bảo có ít nhất một null và sau đó để lại kích thước nếu bạn muốn tiết kiệm không gian.

Bây giờ, chúng tôi muốn biết đó là tệp hay thư mục. Trường "chỉ báo liên kết" có thông tin này, vì vậy:

// 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; 
} 

Tại thời điểm này, chúng tôi đã có tất cả thông tin cần thiết về thư mục nhưng chúng tôi cần thêm một thông tin từ các tệp thông thường.

Độ dài của tệp có thể được lưu trữ theo hai cách khác nhau, dưới dạng chuỗi bát phân kết thúc bằng 0 hoặc không gian đệm hoặc "mã cơ sở 256 được chỉ định bằng cách đặt bit thứ tự cao của byte ngoài cùng bên trái của trường số ".

Giá trị số được mã hóa bằng số bát phân sử dụng các số ASCII, với số 0 đứng đầu. Vì lý do lịch sử, nên sử dụng ký tự NUL hoặc dấu cách cuối cùng. Do đó, mặc dù có 12 byte dành riêng để lưu trữ kích thước tệp, chỉ có thể lưu 11 số bát phân.Điều này cho phép kích thước tệp tối đa là 8 gigabyte trên các tệp được lưu trữ. Để khắc phục hạn chế này, ngôi sao năm 2001 đã giới thiệu mã hóa cơ bản 256 được chỉ định bằng cách thiết lập bit thứ tự cao của byte tận cùng bên trái của một trường số. GNU-tar và BSD-tar theo ý tưởng này. Ngoài ra, các phiên bản của tar từ trước tiêu chuẩn POSIX đầu tiên từ 1988 pad các giá trị có dấu cách thay vì số không.

Đây là cách bạn sẽ đọc các định dạng bát phân, nhưng tôi đã không viết mã cho phiên bản cơ sở-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; 
} 

Ok, vì vậy bây giờ chúng tôi có tất cả mọi thứ ngoại trừ các nội dung tập tin thực tế. Tất cả chúng ta phải làm là lấy size byte tiếp theo của dữ liệu từ tập tin tar và chúng tôi sẽ có những nội dung tập tin của chúng tôi:

// 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) 
+0

Tôi hiện đang sử dụng mã của bạn và đối với tệp tar được tạo bằng Gnome File Roller, "sizeOfFile = octalStringToInt (..., 11)" có vẻ sai "trong một số trường hợp hiếm hoi". Bạn có thể chỉ ra "ma thuật" được bỏ qua trong byte thứ 12 là gì? – rodrigob

+0

@rodrigob Tôi thực sự không biết. Nếu bạn tìm thấy, hãy cho tôi biết. –

+0

Lưu ý, nếu kích thước tệp là _exactly_ 512 byte, thì 'vị trí = vị trí + ((kích thước/512) + 1) * 512' sẽ bỏ lỡ tiêu đề tiếp theo –

11

Bạn đã nhìn libtar?

Từ các thông tin gói Fink:

libtar-1,2-1: file Tar thao tác API libtar là một thư viện C để thao tác với file tar POSIX. Nó xử lý thêm và giải nén tệp vào/từ kho lưu trữ tar. libtar cung cấp các tính năng sau:
* API linh hoạt - bạn có thể thao tác các tệp riêng lẻ hoặc chỉ trích xuất toàn bộ lưu trữ cùng một lúc.
* Cho phép các hàm read() và write() do người dùng chỉ định, chẳng hạn như gzread zlib() và gzwrite().
* Hỗ trợ cả định dạng tệp POSIX 1003.1-1990 và GNU tar.

Không C++ cho mỗi gia nhập, nhưng bạn có thể liên kết đến c khá dễ dàng ...

+3

Các loại tài liệu của hút nhưng tôi kiểm tra nó ra .. –

+0

@BrendanLong Vua hút là một lời nói quá. –

4

libarchive có thể là thư viện mã nguồn mở để phân tích tarball. Libarchive có thể đọc từng tệp từ tệp lưu trữ mà không cần trích xuất và cũng có thể ghi dữ liệu để tạo tệp lưu trữ mới.

Các vấn đề liên quan