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 ...
随机推荐
- MySQL慢日志简介及Anemometer工具介绍
作者:王航威 - fordba.com 来源:http://fordba.com/box-anemometer-visual-mysql-slow.html,叶师傅对原文内容略有调整 备注:王航威是知 ...
- Python项目生成requirements.txt的多种方式
我相信任何软件程序都会有依赖的类库,尤其现在开源如此的火爆,因为一个项目可能会有无很多的依赖的包 这个时候难道我们都要一个一个的去找到安装吗?即使你找到了依赖的包 但是呢模块的版本又有很多难道你都要装 ...
- DES加密(支持ARC与MRC)
DES加密(支持ARC与MRC) 源文件: YXCrypto.h 与 YXCrypto.m // // YXCrypto.h // 用秘钥给字符串加密或者解密 // // Created by You ...
- 原生 JS 的 Base64 转码
JavaScript 原生提供两个 Base64 相关的方法: btoa():任意值转为 Base64 编码 atob():Base64 编码转为原来的值 注意:这两个方法不适合非 ASCII 码的字 ...
- angular 生命周期钩子 ngOnInit() 和 ngAfterViewInit() 的区别
angular 生命周期钩子的详细介绍在 https://angular.cn/guide/lifecycle-hooks 文档中做了介绍. ngOnInit() 在 Angular 第一次显示数据 ...
- Java多线程和并发基础面试总结
多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题.收藏起来,希望给予即将找 ...
- openssl生成证书
数字证书: 第三方机构使用一种安全的方式把公钥分发出去 证书格式:x509,pkcs家族 x509格式: 公钥和有效期限: 持有者的个人合法身份信息:(主机名,域名) 证书的使用方式 CA的信息 CA ...
- mac 安装npm
npm是什么 NPM的全称是Node Package Manager ,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准. 如何安装 一:如果你安装了Homebrew ...
- 20145203JAVA课程总结
20145203盖泽双 <Java程序设计>课程总结 课程总结 (按顺序)每周读书笔记链接汇总 调查问卷:http://www.cnblogs.com/GZSdeboke/p/524832 ...
- 关于wordpress主题、插件上传和下载问题及其上传图片权限问题解决方案
主题官方下载地址:https://wordpress.org/themes/ 插件官方下载地址: https://wordpress.org/plugins/ 主题的上传下载,无疑是需要ftp服务器的 ...