Linux下合并前缀相同的文件的程序流程及其C代码实现

一、概述

在实际的软件开发项目中,会出现对多个前缀(或后缀)相同的文件进行合并的需求。也就是说,将这些前缀(或后缀)相同的文件中的内容合并到一个文件中。这些文件的来源可能是前一流程中程序生成的文件,也可能是其他模块生成的文件。

例如,我们要将前缀相同(以“Test_”作为前缀)的Test_1.txt和Test_2.txt文件中的内容合并到ResultFile.txt文件中,如果Test_1.txt文件中的内容为:

AAAAA

Test_2.txt文件中的内容为:

BBBBB

那么ResultFile.txt文件中的内容就应该为:

AAAAA
BBBBB

本文详细介绍了相同前缀文件合并的程序流程,并给出了C程序实现。

二、总体程序流程

相同前缀文件合并的整体程序流程如图1所示。

图1 文件合并的总体程序流程

三、示例程序流程说明

为了便于说明,我们假定要进行合并的文件前缀为“Test_”,每个文件中的内容以空行结尾,存放在TestFile目录下。结果文件为ResultFile.txt,存放在ResultFile目录下。

重要程序流程说明如下:

1.扫描文件

本示例程序使用scandir函数来扫描TestFile目录下的文件,并将扫描出来的文件存放到一个内存链表中。

该步骤的流程如图2所示。

图2 扫描文件程序流程

2.合并文件

在实际的软件开发项目中,为了确保最终生成的文件中的内容完全正确,通常的做法是先将每个待合并的文件中的内容写入到一个临时文件中,等合并完成之后,再将临时文件转换为正式文件。本示例程序采用了这种做法,并使用了rename函数来获得最终文件。

该步骤的流程如图3所示。

图3 合并文件程序流程

四、程序编译、运行和测试

我想这些还是留给读者朋友们来做吧。 :)

五、程序扩展

本文只是实现了基本的文件扫描和合并的流程,大家可以从以下方面对本程序进行扩展:

第一,统计扫描到的文件的个数、从文件中读取到的数据记录的条数和写入到临时文件中的数据记录的条数。

第二,将处理完成之后的文件删掉,或者是备份到另外的目录中。

第三,将生成的正式文件上传到FTP服务器上。

第四,对读取到的每条数据记录进行特殊处理(如添加字段、删除字段、截取字段等)之后,再写入临时文件中。

附录:完整的程序代码

/**********************************************************************
* 版权所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名称:FileMerge.c
* 文件标识:无
* 内容摘要:演示相同前缀文件内容的合并
* 其它说明:无
* 当前版本:V1.0
* 作    者:Zhou Zhaoxiong
* 完成日期:20150707
*
**********************************************************************/
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <ftw.h>

// 重定义数据类型
typedef signed   int        INT32;
typedef unsigned int        UINT32;
typedef unsigned char       UINT8;
typedef unsigned short int  UINT16;

// 结构体定义
// 扫描到的文件名链表
typedef struct _List
{
    void         *pData;
    struct _List *pNext;
}FileNameList_T;

// 函数声明
INT32 SelectFlies(const struct dirent *pDir);
FileNameList_T *InsertList(FileNameList_T* pCurNode, void *pData, UINT16 iDataLen);
void ClearList(FileNameList_T *pFileNameList);
INT32 ScanFlieAndMerge(UINT8 *pszScanDir);
INT32 MergeFile(UINT8 *pszMergeFileName, FileNameList_T *ptFileNameList);
INT32 main(void);

/****************************************************************
* 功能描述: 主函数
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 0-执行成功
           -1-执行失败
* 其他说明: 无
* 修改日期       版本号        修改人        修改内容
* -------------------------------------------------------------
* 20150707        V1.0     Zhou Zhaoxiong     创建
****************************************************************/
INT32 main(void)
{
    UINT8   szScanDir[256]  = {0};
    INT32   iRetValue       = 0;

    // 获取扫描路径名
    snprintf(szScanDir, sizeof(szScanDir)-1, "%s/zhouzx/test/FileMerge/TestFile", getenv("HOME"));

    // 调用函数执行文件的扫描与合并
    iRetValue = ScanFlieAndMerge(szScanDir);
    if (iRetValue == 0)   // 扫描与合并成功
    {
        printf("Exec ScanFlieAndMerge successfully!\n");
        return 0;
    }
    else
    {
        printf("Exec ScanFlieAndMerge failed!\n");
        return -1;
    }
}

/**********************************************************************
* 功能描述:根据前缀选择文件
* 输入参数:dir-目录
* 输出参数:无
* 返 回 值:0-失败   1-成功
* 其它说明:无
* 修改日期         版本号      修改人          修改内容
* --------------------------------------------------------------------
* 20150707         V1.0    ZhouZhaoxiong        创建
***********************************************************************/
INT32 SelectFlies(const struct dirent *pDir)
{
    INT32 iPrefixLen  = 0;
    INT32 iLoopFlag   = 0;
    INT32 iSelectResult = 0;

    if (pDir == NULL)
    {
        printf("SelectFlies:input parameter is NULL!\n");
        return 0;
    }

    // 匹配文件前缀
    iPrefixLen = strlen("Test_");       // 前缀为"Test_"
    iSelectResult = (0 == strncmp(pDir->d_name, "Test_", iPrefixLen));
    if (iSelectResult == 1)            // 找到了匹配前缀的文件
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**********************************************************************
* 功能描述:将数据插入到内存链表中
* 输入参数:pCurNode-当前节点
            pData-待插入数据
            iDataLen-数据长度
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期         版本号      修改人          修改内容
* --------------------------------------------------------------------
* 20150707         V1.0    ZhouZhaoxiong        创建
***********************************************************************/
FileNameList_T *InsertList(FileNameList_T *pCurNode, void *pData, UINT16 iDataLen)
{
    FileNameList_T *pNewNode = NULL;

    if (NULL == pCurNode || NULL == pData)
    {
        printf("InsertList:input parameter(s) is NULL!\n");
        return NULL;
    }

    if (NULL != (pNewNode = (FileNameList_T *)malloc(sizeof(*pNewNode) + iDataLen)))
    {
        pNewNode->pData = pNewNode + 1;
        memset(pNewNode->pData, 0x00, iDataLen);
        memcpy(pNewNode->pData, pData, iDataLen);

        // 加入到当前节点的后面
        pNewNode->pNext = pCurNode->pNext;
        pCurNode->pNext = pNewNode;
    }

    return pNewNode;
}

/**********************************************************************
* 功能描述:清空内存链表
* 输入参数:pFileNameList-链表数据
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期         版本号      修改人          修改内容
* --------------------------------------------------------------------
* 20150707         V1.0    ZhouZhaoxiong        创建
***********************************************************************/
void ClearList(FileNameList_T *pFileNameList)
{
    FileNameList_T *pCur  = NULL;

    if (pFileNameList == NULL)
    {
        printf("InsertList:input parameter is NULL!\n");
        return;
    }

    for (pCur = pFileNameList->pNext; NULL != pCur; pCur = pFileNameList->pNext)
    {
        pFileNameList->pNext = pCur->pNext;

        free(pCur);
        pCur = NULL;
    }
}

/**********************************************************************
* 功能描述:扫描文件并进行合并
* 输入参数:pszScanDir-扫描目录
* 输出参数:无
* 返 回 值:0-成功   其它-失败
* 其它说明:无
* 修改日期         版本号      修改人          修改内容
* --------------------------------------------------------------------
* 20150707         V1.0    ZhouZhaoxiong        创建
***********************************************************************/
INT32 ScanFlieAndMerge(UINT8 *pszScanDir)
{
    INT32  iScanDirRet           = 0;
    UINT32 iIdxNum               = 0;
    INT32  iRetVal               = 0;
    UINT8  szScanFileName[512]   = {0};
    UINT8  szResultFileName[512] = {0};

    FileNameList_T  tFileName  = {0};
    FileNameList_T *ptFileName = {0};

    struct dirent **ppDirEnt = NULL;

    if (pszScanDir == NULL)
    {
        printf("ScanFlieAndMerge:input parameter is NULL!\n");
        return -1;
    }

    // 扫描目录, 获取文件
    iScanDirRet = scandir(pszScanDir, &ppDirEnt, SelectFlies, alphasort);
    if (iScanDirRet < 0)
    {
        printf("ScanFlieAndMerge:exec scandir failed, path=%s\n", pszScanDir);
        return -2;
    }

    if (iScanDirRet == 0)
    {
        printf("ScanFlieAndMerge: no file in directory %s\n", pszScanDir);
        return -3;
    }

    ptFileName = &tFileName;

    // 将扫描到的文件插入内存链表中
    for (iIdxNum = 0; iIdxNum < iScanDirRet; iIdxNum ++)
    {
        snprintf(szScanFileName, sizeof(szScanFileName) - 1, "%s/%s", pszScanDir, ppDirEnt[iIdxNum]->d_name);
        if (NULL == (ptFileName = InsertList(ptFileName, szScanFileName, strlen(szScanFileName) + 1)))
        {
            printf("ScanFlieAndMerge: exec InsertList failed, FileName=%s\n", szScanFileName);
            break;
        }
    }

    // 释放链表空间
    for (iIdxNum = 0; iIdxNum < iScanDirRet; iIdxNum ++)
    {
        free(ppDirEnt[iIdxNum]);
        ppDirEnt[iIdxNum] = NULL;
    }

    free(ppDirEnt);
    ppDirEnt = NULL;

    // 获取带路径的结果文件名
    snprintf(szResultFileName, sizeof(szResultFileName) - 1, "%s/zhouzx/test/FileMerge/ResultFile/ResultFile.txt", getenv("HOME"));

    iRetVal = MergeFile(szResultFileName, &tFileName);
    if (iRetVal == 0)    // 合并成功
    {
        printf("ScanFlieAndMerge: exec MergeFile successfully, ResultFileName=%s\n", szResultFileName);

        // 清空内存链表
        ClearList(&tFileName);
        return 0;
    }
    else
    {
        printf("ScanFlieAndMerge: exec MergeFile failed, ResultFileName=%s\n", szResultFileName);

        // 清空内存链表
        ClearList(&tFileName);
        return -4;
    }
}

/**********************************************************************
* 功能描述: 将多个文件合并为一个
* 输入参数: pszMergeFileName-合并之后的文件名
             ptFileNameList-被合并的文件名链表
* 输出参数: 无
* 返 回 值: 0-成功   其它-失败
* 其它说明: 无
* 修改日期        版本号            修改人         修改内容
* --------------------------------------------------------------
* 20150707        V1.0          ZhouZhaoxiong        创建
***********************************************************************/
INT32 MergeFile(UINT8 *pszMergeFileName, FileNameList_T *ptFileNameList)
{
    FILE   *pFTmp   = NULL;
    FILE   *pFSrc   = NULL;
    INT32   iRetVal = 0;
    UINT8   szContentBuf[256]  = {0};
    UINT8   szTmpFileName[512] = {0};

    FileNameList_T *ptScanFileName = NULL;

    if (pszMergeFileName == NULL || ptFileNameList == NULL)
    {
        printf("MergeFile:input parameter(s) is NULL!\n");
        return -1;
    }

    // 先将内容写入临时文件中,完成之后再写入正式文件中
    // 临时文件名为"正式文件名.tmp"
    snprintf(szTmpFileName, sizeof(szTmpFileName) - 1, "%s%s", pszMergeFileName, ".tmp");
    if (NULL == (pFTmp = fopen(szTmpFileName, "wt")))
    {
        printf("MergeFile:open %s failed!\n", szTmpFileName);
        return -2;
    }

    // 将内容写入临时文件中
    for (ptScanFileName = ptFileNameList->pNext; ptScanFileName != NULL; ptScanFileName = ptScanFileName->pNext)
    {
        if (NULL == (pFSrc = fopen(ptScanFileName->pData, "r")))
        {
            printf("MergeFile:open %s failed!\n", ptScanFileName->pData);
            break;
        }

        while (NULL != fgets(szContentBuf, sizeof(szContentBuf) - 1, pFSrc))   // 将文件内容取出来
        {
            if (fputs(szContentBuf, pFTmp) <= 0)   // 将文件内容放入临时文件中
            {
                printf("MergeFile:exec fputs failed, ScanFileName=%s, TmpFileName=%s\n", ptFileNameList->pData, szTmpFileName);
                break;
            }
        }

        if (0 == feof(pFSrc))   // 文件内容未完全从扫描文件中读出, 异常退出
        {
            printf("MergeFile:do not reach the end of file %s\n", ptFileNameList->pData);
            fclose(pFSrc);
            pFSrc = NULL;
            break;
        }
        else
        {
            fclose(pFSrc);
            pFSrc = NULL;
        }
    }

    // 使用完文件之后要将其关闭并将文件指针置为空
    fclose(pFTmp);
    pFTmp = NULL;

    // 将临时文件更名为正式文件
    iRetVal = rename(szTmpFileName, pszMergeFileName);
    if (iRetVal == 0)
    {
        printf("MergeFile:rename %s to %s successfully!\n", szTmpFileName, pszMergeFileName);
        return 0;
    }
    else
    {
        printf("MergeFile:rename %s to %s failed!\n", szTmpFileName, pszMergeFileName);
        return -3;
    }
}


本人微信公众号:zhouzxi,请扫描以下二维码:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-27 18:05:30

Linux下合并前缀相同的文件的程序流程及其C代码实现的相关文章

Linux下按照时间和大小生成新文件的程序流程及其C代码实现

一.概述 在实际的软件开发项目中,会出现按照时间和大小生成新文件的需求.例如,某软件需求的描述如下: 按照如下两个条件之一生成新的文件: 第一,新的一天到来. 第二,文件的大小超过阈值. 本文详细介绍了根据时间和大小生成新文件的程序流程,并给出了C程序实现. 二.算法设计 对于这个按照不同的条件生成新文件的需求,在编写代码之前,我们要认真考虑以下问题: 1.如何知道当前写文件的时间与上次时间相比,是新的一天? 对于这个问题,最简单的做法是将上次写完文件之后的时间保存在内存中,等下次写文件之前读取

linux 下查找大于100M的文件

命令行如下 find . -type f -size +1000000k Linux系统下查找大文件或目录的技巧 当硬盘空间不够时,我们就很关心哪些目录或文件比较大,看看能否干掉一些了,怎么才能知道呢? #已易读的格式显示指定目录或文件的大小,-s选项指定对于目录不详细显示每个子目录或文件的大小 du -sh [dirname|filename] 如: 当前目录的大小: du -sh . 当前目录下个文件或目录的大小: du -sh * [[email protected] var]# du -

linux 下C语言编程库文件处理与Makefile编写

做开发快3年了,在linux下编译安装软件算是家常便饭了.就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题.看来还是像vs.codeblocks这样的ide把人弄蠢了.便下定决心一定要好好学习下如何在linux下纯手工gcc编译c项目.今天学了2点,一个是库文件处理,另一个是makefile编写. 学习的系统是centos6.6,编译升级的gcc4.8.2,明天写个博客总结下这回gcc安装的过程,每次都能学到些东西. gcc的编译过程 首先需要清楚gcc编译做了些什么 源文件

Linux下音频编程-输出音频文件

程序实现了在Linux下播放Ok.wav的功能.程序首先调用fstat函数获得文件相关信息(主要是文件大小信息).通过malloc函数分配指定的内存空间,并将online.wav读入内存:然后,打开声卡设备文件,设置声卡参数:再调用write函数完成文件的播放. 简要的实例,代码如下: #include<unistd.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #includ

Linux下用inotify-tool实时监控服务器文件

说明: 服务器系统:CentOS 文件目录:/home/web/os 实现目的: 当/home/web/os下面除过cache目录之外,任何文件发生变化时,记录日志并保存. 具体操作: 一.安装Inotify-tools工具 1.查看服务器内核是否支持inotify ll /proc/sys/fs/inotify #列出文件目录,出现下面的内容,说明服务器内核支持inotify -rw-r--r-- 1 root root 0 Mar 7 02:17 max_queued_events -rw-

Linux下gcc编译生成动态链接库*.so文件并调用它 是转载的

动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助.1.动态库的编译 下面通过一个例子来介绍如何生成一个动态库.这里有一个头文件:so_test.h,三个.c文件:test_a.c.test_b.c.test_c.c,我们将这几个文件编译成一个动态库:libtest.so. //so_test.h:#include "stdio.h"

Linux下自动清除MySQL日志文件

MySQL运行过程中会生成大量的日志文件,占用不少空间,修改my.cnf文件配置bin-log过期时间,在Linux下自动清除MySQL日志文件 [mysqld] expire-logs-days=7 max-binlog-size=268435456

关于在linux下用gcc编译头文件的问题。

关于在linux下用gcc编译头文件的问题. 2011-01-21 18:5215052135380 | 分类:其他编程语言 | 浏览8139次 有node.h prepare.h list.h file.h 这四个头文件, prepare.h中用到了node.h, list.h中用到了preapre.h , file.h中用到了list.h ,怎么用gcc对这四个头文件进行编译. 分享到: 2011-01-22 02:23 天天爱答题,抽奖送惊喜~ 提问者采纳 我用一个例子来告诉你怎么样在 C

linux下使用math.h头文件-l与-L参数

遇到一个问题就是,c语言用到sqrt时,明明已包含math.h文件,却仍提示未定义,所以上网招答案的: gcc -lm 以下转自http://bbs.csdn.net/topics/330105678 ·-l参数和-L参数 -l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了 好了现在我们知道怎么得到库名,当我们自已要用到一个第三方提供