C++实现tar包解析
tar(tape archive)是Unix和类Unix系统上文件打包工具,可以将多个文件合并为一个文件,使用tar工具打出来的包称为tar包。一般打包后的文件名后缀为”.tar”,也可以为其它。tar代表未被压缩的tar文件,已被压缩的tar文件则追加压缩文件的扩展名,如经过gzip压缩后的tar文件,扩展名为”.tar.gz”。在windows系统中用WinRAR也可以解压缩打开tar包。tar文件格式已经成为POSIX标准,最初是POSIX.1-1998,目前是POSIX.1-2001.
tar中的数据都是以512字节为单位。tar由两部分组成即头部+内容,其中头部是512字节的头部结构,内容是存放一个文件内容的地方。
tar文件格式的详细介绍可以参考:https://en.wikipedia.org/wiki/Tar_(computing)#File_header
通过执行以下命令生成测试tar包:
tar -cvf test.tar *
test.tar中包含两个文件blog_info.txt和github_info.txt.
其中blog_info.txt文件内容如下:
name: fengbingchun address: http://blog.csdn.net/fengbingchun?viewmode=contents
github_info.txt文件内容如下:
name: fengbingchun address: https://github.com/fengbingchun
实现代码tar.hpp:
#ifndef FBC_MESSY_TEST_TAR_HPP_
#define FBC_MESSY_TEST_TAR_HPP_
#include <vector>
#include <string>
/* reference:
http://www.gnu.org/software/tar/manual/html_node/Standard.html
http://stackoverflow.com/questions/2505042/how-to-parse-a-tar-file-in-c
http://directory.fsf.org/wiki/Libtar
http://work.freenet59.ru/svn/pkgsrc_haiku/trunk/archivers/libarchive/files/contrib/untar.c
https://codeistry.wordpress.com/2014/08/14/how-to-parse-a-tar-file/
http://stackoverflow.com/questions/17862383/how-to-know-the-files-inside-the-tar-parser
https://en.wikipedia.org/wiki/Tar_(computing)
*/
/* tar Header Block, from POSIX 1003.1-1990. */
/* POSIX header. */
typedef struct posix_header
{ /* byte offset */
char name[100]; /* 0 */
char mode[8]; /* 100 */
char uid[8]; /* 108 */
char gid[8]; /* 116 */
char size[12]; /* 124 */
char mtime[12]; /* 136 */
char chksum[8]; /* 148 */
char typeflag; /* 156 */
char linkname[100]; /* 157 */
char magic[6]; /* 257 */
char version[2]; /* 263 */
char uname[32]; /* 265 */
char gname[32]; /* 297 */
char devmajor[8]; /* 329 */
char devminor[8]; /* 337 */
char prefix[155]; /* 345 */
/* 500 */
} tar_posix_header;
/*
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
*/
#define TMAGIC "ustar" /* ustar and a null */
#define TMAGLEN 6
#define TVERSION "00" /* 00 and no null */
#define TVERSLEN 2
/* Values used in typeflag field. */
#define REGTYPE '0' /* regular file */
#define AREGTYPE '\0' /* regular file */
#define LNKTYPE '1' /* link */
#define SYMTYPE '2' /* reserved */
#define CHRTYPE '3' /* character special */
#define BLKTYPE '4' /* block special */
#define DIRTYPE '5' /* directory */
#define FIFOTYPE '6' /* FIFO special */
#define CONTTYPE '7' /* reserved */
class TarFile {
public:
TarFile(const char* tar_name);
bool IsValidTarFile();
std::vector<std::string> GetFileNames();
bool GetFileContents(const char* file_name, char* contents);
size_t GetFileSize(const char* file_name);
size_t GetTarSize();
~TarFile();
private:
FILE* file;
size_t size;
std::vector<std::string> file_names;
std::vector<size_t> file_sizes;
std::vector<size_t> file_data_start_addrs;
};
int test_tar();
#endif // FBC_MESSY_TEST_TAR_HPP_
tar.cpp:
#include "tar.hpp"
TarFile::TarFile(const char* tar_name)
: file(nullptr), size(0)
{
file_names.clear();
file_sizes.clear();
file_data_start_addrs.clear();
file = fopen(tar_name, "rb");
}
TarFile::~TarFile()
{
if (file) {
fclose(file);
file = nullptr;
}
file_names.clear();
file_sizes.clear();
file_data_start_addrs.clear();
}
bool TarFile::IsValidTarFile()
{
if (!file) return false;
const int block_size{ 512 };
unsigned char buf[block_size];
tar_posix_header* header = (tar_posix_header*)buf;
memset(buf, 0, block_size);
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
if (size % block_size != 0) {
fprintf(stderr, "tar file size should be a multiple of 512 bytes: %d\n", size);
return false;
}
size_t pos{ 0 };
while (1) {
size_t read_size = fread(buf, block_size, 1, file);
if (read_size != 1) break;
if (strncmp(header->magic, TMAGIC, 5)) break;
pos += block_size;
size_t file_size{0};
sscanf(header->size, "%lo", &file_size);
size_t file_block_count = (file_size + block_size - 1) / block_size;
switch (header->typeflag) {
case '0': // intentionally dropping through
case '\0':
// normal file
file_sizes.push_back(file_size);
file_names.push_back(std::string(header->name));
file_data_start_addrs.push_back(pos);
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;
default:
break;
}
pos += file_block_count * block_size;
fseek(file, pos, SEEK_SET);
}
fseek(file, 0, SEEK_SET);
return true;
}
std::vector<std::string> TarFile::GetFileNames()
{
return file_names;
}
bool TarFile::GetFileContents(const char* file_name, char* contents)
{
bool flag = false;
for (int i = 0; i < file_names.size(); i++) {
std::string name_(file_name);
if (file_names[i].compare(name_) == 0) {
int file_size = file_sizes[i];
flag = true;
fseek(file, file_data_start_addrs[i], SEEK_SET);
fread(contents, file_size, 1, file);
fseek(file, 0, SEEK_SET);
break;
}
}
return flag;
}
size_t TarFile::GetFileSize(const char* file_name)
{
size_t file_size{0};
for (int i = 0; i < file_names.size(); i++) {
std::string name_(file_name);
if (file_names[i].compare(name_) == 0) {
file_size = file_sizes[i];
break;
}
}
return file_size;
}
size_t TarFile::GetTarSize()
{
return size;
}
//////////////////////////////////////////////
int test_tar()
{
const std::string tar_file_path{ "E:/GitCode/Messy_Test/testdata/test.tar" };
TarFile tarfile(tar_file_path.c_str());
bool is_valid_tar_file = tarfile.IsValidTarFile();
if (!is_valid_tar_file) {
fprintf(stderr, "it is not a valid tar file: %s\n", tar_file_path.c_str());
return -1;
}
fprintf(stderr, "tar file size: %d byte\n", tarfile.GetTarSize());
std::vector<std::string> file_names = tarfile.GetFileNames();
fprintf(stderr, "tar file count: %d\n", file_names.size());
for (auto name : file_names) {
fprintf(stderr, "=====================================\n");
size_t file_size = tarfile.GetFileSize(name.c_str());
fprintf(stderr, "file name: %s, size: %d byte\n", name.c_str(), file_size);
char* contents = new char[file_size + 1];
tarfile.GetFileContents(name.c_str(), contents);
contents[file_size] = '\0';
fprintf(stderr, "contents:\n%s\n", contents);
delete[] contents;
}
return 0;
}
测试结果如下:
GitHub:https://github.com/fengbingchun/Messy_Test
C++实现tar包解析的更多相关文章
- Caffe实战三(依赖包解析及环境配置)
前面的文章使用的软件环境是开始时通过apt-get命令所安装的,本文将通过编译源码的方式重新配置一个可迁移的软件环境.(参考:<深度学习 21天实战Caffe> 第五天 Caffe依赖包解 ...
- Linux RPM、TAR包管理
一.RPM软件包命令的使用 RPM主要有5种基本操作模式:安装.卸载.刷新.升级及查询.下面分别介绍. 1.安装软件包 命令语法: rpm -ivh [RPM包文件名称] 命令中各参数的含义如下: - ...
- tar 只解压tar包中某个文件
sh-4.1# ls test.tar sh-4.1# tar -tf test.tar ./ecs20161207.png ./ecs.png ./ecs.xml ./rds.png ./Scree ...
- find 查找文件 -exec 然后压缩 查看tar包的内容
[root@cs Downloads]# find ./ -name "banner*" -exec tar -cvf k.tar "{}" \; ./bann ...
- java jar包解析:打包文件,引入文件
java jar包解析:打包文件,引入文件 cmd下: jar命令:package包打包 javac命令:普通类文件打包 Hello.java: package org.lxh.demo; publi ...
- Unix系统解压tar包时出现@LongLink错误
Unix系统上使用tar命令解压tar包后,多了一个@LongLink的文件,并且原来的tar包解压后不完整.网上查了下,原因是AIX系统上tar命令自身的一个缺陷.解决办法:把该tar包上传到lin ...
- Android做法说明(3)---Fragment使用app袋或v4包解析
Android做法说明(3)---Fragment使用app袋或v4包解析 1)问题简述 相信非常多的朋友在调用Fragment都会遇到以下的情况: watermark/2/text/aHR0cDov ...
- linux tar包追加问题【转】
只能已归档的文件才能追加文件. 如果tar.gz文件是如此生成:#tar -zcvf test.tar.gz a.txt即tar.gz是压缩(-z)和归档(-c)文件,则无法给它追加文件:若果tar ...
- Spring (3.2.4) 常用jar 包解析
Spring (3.2.4) 常用jar 包解析 基本jar包 spring-aop-3.2.4.RELEASE.jar spring-aspects-3.2.4.RELEASE.jar spring ...
随机推荐
- mysqlcilent的安装
这软件包是贼的气 首先是windows的安装上你必须要指定版本,linux的安装你就不需要指定版本了 windos上的指定安装必须要 一.下载包的时候需要指定版本, 比如python2的和mysq ...
- 使用Instruments中的CoreAnimation分析动画
使用Instruments中的CoreAnimation分析动画 1. 打开Instruments中的CoreAnimation 2. 运行前的准备工作 要注意勾选以下选项,便于调试 3. 运行与调试 ...
- [翻译] VENCalculatorInputView
VENCalculatorInputView https://github.com/venmo/VENCalculatorInputView VENCalculatorInputView is the ...
- win中使用cmd杀端口
最近在win开发时,总是遇到端口占用的情况...可能是跑的程序太多了吧 当你测试一个demo时遇到这个就很恶心.. 记一下 netstat -ano | findstr 80 //列出进程极其占用的端 ...
- ZT 查找字符串中连续最长的数字串
查找字符串中连续最长的数字串 有俩方法,1)比较好理解一些.2)晦涩 1) /* 功能:在字符串中找出连续最长的数字串,并把这个串的长度返回, 并把这个最长数字串付给其中一个函数参数outputstr ...
- 01-urllib库添加headers的一般方法
2018-08-23 13:07:57 对于请求一些网站,我们需要加上请求头才可以完成网页的抓取,不然会得到一些错误,无法返回抓取的网页.下面,介绍两种添加请求头的方法. 方法一:借助build_op ...
- UI(三)
1. 2.经常用到的loadmap函数 void CTopology::LoadMap() { //m_map.RemoveAllLayers(); AddLayersBasemap(); AddLa ...
- 【C#】#100 调用摄像头
需求:由于项目需要获得用户的头像,所以需要用C#调用摄像头获取头像. 下面写一个调用摄像头的方法 案例:调用摄像头的一个DEMO[效果图] 使用的类库:AForge.dll [Demo下载,Dem ...
- angular 生命周期钩子 ngOnInit() 和 ngAfterViewInit() 的区别
angular 生命周期钩子的详细介绍在 https://angular.cn/guide/lifecycle-hooks 文档中做了介绍. ngOnInit() 在 Angular 第一次显示数据 ...
- iOS: 聊聊 Designated Initializer(指定初始化函数):NS_DESIGNATED_INITIALIZER
总结:指定函数的调用规则: 初始化函数的调用顺序与初始化顺序相反. 上面关于指定初始化的规则讲了那么多,其实可以归纳为两点: 便利初始化函数只能调用自己类中的其他初始化方法 指定初始化函数才有资格调用 ...