文件合并程序
程序的功能
将一个目录中的指定的文件进行合并或者拆分。
处理规则是:将指定目录中的指定的文件形成一个文件列表,对该列表中的每一个文件进行读取,一条一条地将记录写入目的文件,当目的文件达到指定的数量的时候,关闭旧的目的文件,打开新的目的文件,继续处理原始的记录。最终的后果就是,如果原始文件的记录较少,该程序实现了文件合并,如果原始文件的记录数量较多,该程序实现了文件的拆分。
使用到的系统函数有:
获取文件目录中文件列表的办法;
取得当前程序进程号;
获取时间的办法;
C++中读写文件的办法;
测试命令
程序的功能
将一个目录中的指定的文件进行合并或者拆分。
处理规则是:将指定目录中的指定的文件形成一个文件列表,对该列表中的每一个文件进行读取,一条一条地将记录写入目的文件,当目的文件达到指定的数量的时候,关闭旧的目的文件,打开新的目的文件,继续处理原始的记录。最终的后果就是,如果原始文件的记录较少,该程序实现了文件合并,如果原始文件的记录数量较多,该程序实现了文件的拆分。
使用到的系统函数有:
获取文件目录中文件列表的办法;
取得当前程序进程号;
获取时间的办法;
C++中读写文件的办法;
一些宏的使用;
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <vector>
#include <string>
#include <fstream>
using namespace std;
/*
编译方法
AIX:xlC_r -g -w -q64 -qwarn64 -brtl -bhalt:5 -o mergeCdr mergeCdr.cpp
发布的时候,去掉“-g”
*/
/*
版本号定义规则
1 主版本号,用于较大的程序改动
0 次版本号,用于较小的需求改动
0 每次的修改的bug 更改次数
1 总的发布次数
第一次版本的发布信息为: Ver 1.0.0.1
*/
/*
版本修改历史:
Ver 1.0.0.1 功能点:
支持程序将指定目录中的指定的文件,复制并备份到指定的目录
Ver 1.1.0.2 功能点:
支持将目的文件设定最大行数
*/
#define __VER__ "Ver 1.1.0.2"
const int FILE_NAME_LEN = 256; //文件名的最大长度
const int BUFFER_LEN = 4096; //清单中的一条记录的最大长度
vector<string> vec_file; //用于存放需要处理的文件名称
int nFileRows = 0; //合并后的文件的当前记录数量
int nMaxFileRows = 0; //合并后的文件的最大记录数量
char chFileMask[50] = { 0 }; //命令行参数中传进来的输出文件名称的前缀
int GetFile(char * src_dir, char *src_file_name);
void usage(char *process_name);
int DealOneFile(char *srcFileNameWithPath, char*bakFileNameWithPath,char * destFilePath, char * destFileName);
int GetMMHHSS(char * mmhhss);
int GetYYYYMMDD(char * yymmdd);
int GetMMDD(char * mmdd);
/*
程序的参数:
process_name sleep_time src_dir src_file_mask src_bak_dir dest_dir desc_file_mask file_rows
process_name 程序名字本身
sleep_time 程序休眠的时间
src_dir 原来的目录路径
src_file_mask 原始文件的掩码
src_bak_dir 备份文件的路径
dest_dir 新的目录路径
desc_file_mask 新的文件名称掩码
file_rows 合并后的文件达到file_rows的记录后,重新建立一个文件
启动方式举例:
./mergeCdr 60 /hnibm/henan/mergeCdr A /hnibm/henan/mergeCdr/bak /hnibm/henan/mergeCdr A 3
*/
int main(int argc, char ** argv)
{
printf("==============process [%s] begin...==============\n", argv[0]);
//参数校验
if( argc == 2 &&
( strcmp(argv[1],"-v") == 0 || strcmp(argv[1],"-V") == 0 ))
{
printf("current version is [%s]\nbulid time is [%s %s]\n",__VER__, __DATE__,__TIME__);
return 0;
}
else if( argc != 8)
{
printf("process need 7 parameters!\n");
usage(argv[0]);
return 2;
}
sprintf(chFileMask, "%s", argv[6]);
nMaxFileRows = atol(argv[7]);
//主要业务的处理
while(true)
{
//取得当前的日期和时间,用于生成目标文件名称
char szyyyymmdd[15] = {0};
GetYYYYMMDD(szyyyymmdd);
char szhhmiss[15] = {0};
GetMMHHSS(szhhmiss);
char szmmdd[10] = {0};
GetMMDD(szmmdd);
printf("current date and time is [%s %s]!\n",szyyyymmdd,szhhmiss);
//取得需要处理的文件列表
int ret = GetFile(argv[2],argv[3]);
if(ret != 0)
{
return ret;
}
if(vec_file.size() == 0)
{
printf("no files to be need\n");
}
else
{
printf("there are [%d] files need to deal\n",vec_file.size());
//规整输入的文件名称
char szSrcFilePath[FILE_NAME_LEN] = {0};
if(argv[2][strlen(argv[2])-1] != '/')
{
sprintf(szSrcFilePath,"%s%s",argv[2],"/");
}
printf("src file path is [%s]\n",szSrcFilePath);
//规整备份文件的路径名称
char szBakFilePath[FILE_NAME_LEN] = {0};
if(argv[4][strlen(argv[4])-1] != '/')
{
sprintf(szBakFilePath,"%s%s",argv[4],"/" );
}
printf("backup file path is [%s]\n",szBakFilePath);
//规整输出的路径名称
char szDestFilePath[FILE_NAME_LEN] = { 0 };
if(argv[5][strlen(argv[5])-1] != '/')
{
sprintf(szDestFilePath,"%s%s",argv[5],"/" );
}
char szDestFileNameTemp[FILE_NAME_LEN / 2] = { 0 };
sprintf(szDestFileNameTemp, "%s_0%s%s_%s.r.XswapX", chFileMask, szmmdd, szhhmiss, szyyyymmdd);
char szSrcFileNameWithPath[FILE_NAME_LEN] = {0};
char szBakFileNameWithPath[FILE_NAME_LEN] = {0};
for(vector<string>::iterator itr = vec_file.begin(); itr != vec_file.end(); itr ++)
{
memset(szSrcFileNameWithPath,0x00,FILE_NAME_LEN);
memset(szBakFileNameWithPath,0x00,FILE_NAME_LEN);
sprintf(szSrcFileNameWithPath,"%s%s",szSrcFilePath,itr->c_str());
sprintf(szBakFileNameWithPath,"%s%s",szBakFilePath,itr->c_str());
printf("begin to deal file [%s]\n",szSrcFileNameWithPath);
//处理一个文件
//特别需要注意参数 szDescFileNameTemp,当一个合并文件写满时,它的变量值在处理过程中会发生改变。
ret = DealOneFile(szSrcFileNameWithPath, szBakFileNameWithPath, szDestFilePath, szDestFileNameTemp);
if(ret)
{
//return ret;
continue;
}
}
//将目标文件的名称进行调整
char szCmd[1024] = {0};
string strDestFileNameTemp(szDestFileNameTemp);
sprintf(szCmd, "mv %s%s %s%s ", szDestFilePath, szDestFileNameTemp, szDestFilePath, strDestFileNameTemp.substr(0, strDestFileNameTemp.rfind(".")).c_str());
printf("final dest file: [%s]\n", szCmd);
system(szCmd);
}
printf("==============sleep(%d)==============\n", atoi(argv[1]));
sleep(atoi(argv[1]));
}
return 0;
}
void usage(char *process_name)
{
printf("USAGE:\n");
printf("\t[%s -v]\n",process_name);
printf("\t[%s -V]\n",process_name);
printf("\t[%s sleep_time src_dir src_file_name_mask src_bak_dir dest_dir dest_file_name_mask file_rows]\n",process_name);
}
int GetFile(char * src_dir, char *src_file_name)
{
printf("begin to get files in dir [%s], file name like [%s]\n", src_dir, src_file_name );
DIR *dp;
struct dirent *dirp;
if ((dp = opendir(src_dir)) == NULL)
{
printf("can't open [%s]\n", src_dir);
return 3;
}
vec_file.clear();
int iSrcFileNameLen = strlen(src_file_name);
while ((dirp = readdir(dp)) != NULL)
{
if(strcmp(dirp->d_name,".") == 0 || (strcmp(dirp->d_name,"..") == 0))
{
continue;
}
if(strncmp(dirp->d_name,src_file_name,iSrcFileNameLen) != 0)
{
printf("file name is [%s], skip!\n", dirp->d_name);
continue;
}
int nFileNameLen = strlen(dirp->d_name);
if (! (dirp->d_name[nFileNameLen - 2] == '.' && dirp->d_name[nFileNameLen - 1] == 'r') )
{
printf("file name is [%s], skip!\n", dirp->d_name);
continue;
}
vec_file.push_back(dirp->d_name);
}
closedir(dp);
return 0;
}
int DealOneFile(char *srcFileNameWithPath, char*bakFileNameWithPath, char * destFilePath, char * destFileName)
{
ifstream src_file(srcFileNameWithPath);
char szDestFileNameWithPath[FILE_NAME_LEN] = { 0 };
sprintf(szDestFileNameWithPath, "%s%s", destFilePath, destFileName);
printf("dest file name with path is [%s]\n", szDestFileNameWithPath);
ofstream dest_file(szDestFileNameWithPath, ofstream::app);
if(!src_file.is_open())
{
printf("open src file [%s] Error\n",srcFileNameWithPath);
return 4;
}
if(!dest_file.is_open())
{
printf("open dest file [%s] Error\n", szDestFileNameWithPath);
return 5;
}
char szBuffer[BUFFER_LEN] = {0};
//复制文件
while (! src_file.eof() )
{
memset(szBuffer,0x00,BUFFER_LEN);
/*
//read 函数是读取一块数据,会有多行,不能使用此函数
src_file.read(szBuffer,BUFFER_LEN);
dest_file << szBuffer;
*/
src_file.getline(szBuffer,BUFFER_LEN);
if(strlen(szBuffer) == 0)
{
printf("empty line, skip\n" );
continue;
}
dest_file << szBuffer <<endl;
nFileRows ++;
//如果一个文件“被写满”,写下一个文件
if( nFileRows == nMaxFileRows)
{
printf("file is full, use next file\n");
nFileRows = 0;
dest_file.close();
//将目标文件的名称进行调整
string strDestFileName(destFileName);
char szCmd[1024] = { 0 };
sprintf(szCmd, "mv %s %s%s", szDestFileNameWithPath, destFilePath, strDestFileName.substr(0, strDestFileName.rfind(".")).c_str());
printf("final dest file: [%s]\n", szCmd);
system(szCmd);
//为了防止新的目标文件名与上一次产生的文件名重复
sleep(1);
char szyyyymmdd[15] = { 0 };
GetYYYYMMDD(szyyyymmdd);
char szhhmiss[15] = { 0 };
GetMMHHSS(szhhmiss);
char szmmdd[10] = { 0 };
GetMMDD(szmmdd);
//此处需要修改函数的入参,好让此文件名供后续的其他需要处理的源文件使用。
sprintf(destFileName, "%s_0%s%s_%s.r.XswapX", chFileMask, szmmdd, szhhmiss, szyyyymmdd);
sprintf(szDestFileNameWithPath, "%s%s", destFilePath, destFileName);
printf("new dest file name with path is [%s]\n", szDestFileNameWithPath);
dest_file.open(szDestFileNameWithPath, ofstream::app);
}
}
//dest_file<<endl;
dest_file.close();
src_file.close();
//将原始文件进行备份
char szCmd[1024] = {0};
sprintf(szCmd,"mv %s %s",srcFileNameWithPath,bakFileNameWithPath );
printf("backup file: [%s]\n", szCmd);
system(szCmd);
return 0;
}
int GetMMHHSS(char * mmhhss)
{
time_t current_time = time(0);
struct tm *mStLocalTime = localtime(¤t_time);
sprintf(mmhhss,"%02d%02d%02d",mStLocalTime->tm_hour,mStLocalTime->tm_min,mStLocalTime->tm_sec);
return 0;
}
int GetYYYYMMDD(char * yymmdd)
{
time_t current_time = time(0);
struct tm *mStLocalTime = localtime(¤t_time);
sprintf(yymmdd,"%04d%02d%02d",mStLocalTime->tm_year+1900,mStLocalTime->tm_mon + 1,mStLocalTime->tm_mday);
return 0;
}
int GetMMDD(char * mmdd)
{
time_t current_time = time(0);
struct tm *mStLocalTime = localtime(¤t_time);
sprintf(mmdd,"%02d%02d",mStLocalTime->tm_mon + 1,mStLocalTime->tm_mday);
return 0;
}
测试命令
测试命令:./mergeCdr 60 /hnibm/henan/mergeCdr A /hnibm/henan/mergeCdr/bak /hnibm/henan/mergeCdr A 1000
命令说明
./mergeCdr 程序本身的名称;
60 程序循环变量指定的原始文件目录时,所休眠的时间,以秒为单位;
/hnibm/henan/mergeCdr 程序需要处理的原始文件目录;
A 程序需要处理的原始文件目录中的以字符'A' 开头的文件名称;
/hnibm/henan/mergeCdr/bak 程序需要处理的原始文件在处理后的备份的路径;
/hnibm/henan/mergeCdr 程序需要存放的合并后的文件的路径;
A 合并后的文件的名称前缀,最终的文件名为“A_0mmddhhmiss_yyyymmdd.r”;
1000 合并后的文件的记录数量的最大值,当文件的记录数量达到此值后,系统会创建一个新的记录文件;
备注:
我测试的时候,原始文件的路径和合并后的文件存放的路径是一致的,原始文件的名称匹配字符和合并后的文件的名称前缀也是一致的,这种源和目的都一样的情况,会导致数据文件在下一个循环中进行重复处理,虽然不会导致话单错误,但是也是多此一举。所以,在实际生产中,不建议用这种方式进行配置。