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 ...
随机推荐
- Spring MVC 常用Jar包
spring:http://maven.springframework.org/release/org/springframework/spring/ jackson:http://repo1.mav ...
- 【Oracle】存储过程写法小例子
1.存储过程的基本语法: CREATE OR REPLACE PROCEDURE 存储过程名(param1 in type,param2 out type) IS 变量1 类型(值范围); 变量2 类 ...
- SQL Server FOR XML PATH 和 STUFF函数的用法
FOR XML PATH ,其实它就是将查询结果集以XML形式展现,将多行的结果,展示在同一行. 下面我们来写一个例子: 假设我们有个工作流程表: CREATE TABLE [dbo].[Workfl ...
- phpcas 整合 ecms的问题
如果有ecms整合phpCAS经验的小伙伴可以给我留言,有偿付费 需求是:ecms登录自动跳转到CAS认证(已完成),认证后直接跳转到帝国cms系统中,并保持帝国cms系统权限等都和以前一样 正常使用 ...
- 用windows自带的fsutil修改稀疏文件大小成功,但文件内容似乎丢失
fsutil sparse setflag. fsutil sparse setrange 10M对应字节,1G对应字节.. 看文件属性,实际尺寸是小了,但内容似乎也丢了..因为自己把该文件做成虚拟盘 ...
- 【理解】 Error 10053和 Error 10054
1. 10053 这个错误码的意思是: A established connection was aborted by the software in your host machine, 一个已建 ...
- [转] pom.xml 配置详解
From:http://blog.csdn.net/ithomer/article/details/9332071 Maven是一个项目管理工具,通过pom.xml来实现. pom.xml 配置文件 ...
- 【洛谷】【动态规划/背包】P1833 樱花
[题目描述:] 爱与愁大神后院里种了n棵樱花树,每棵都有美学值Ci.爱与愁大神在每天上学前都会来赏花.爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Ai遍,一种樱花树 ...
- virtualbox迁移已建虚机存储磁盘方法
1. 先关闭虚拟机 2. 将虚拟机的磁盘拷贝或移动到想要存储的位置,virtualbox一般为.vdi文件(虚拟磁盘文件) 3. vboxmanage internalcommands sethduu ...
- mysql 去除特殊字符和前后空白字符
mysql 去除特殊字符 update table set field = replace(replace(replace(field,char(9),''),char(10),''),char(13 ...