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,请扫描以下二维码:
这里写图片描述

目录
相关文章
|
19天前
|
Linux 开发工具 Perl
在Linux中,有一个文件,如何删除包含“www“字样的字符?
在Linux中,如果你想删除一个文件中包含特定字样(如“www”)的所有字符或行,你可以使用多种文本处理工具来实现。以下是一些常见的方法:
39 5
|
20天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
37 6
|
20天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
55 6
|
21天前
|
监控 Linux Perl
Linux 命令小技巧:显示文件指定行的内容
在 Linux 系统中,处理文本文件是一项常见任务。本文介绍了如何使用 head、tail、sed 和 awk 等命令快速显示文件中的指定行内容,帮助你高效处理文本文件。通过实际应用场景和案例分析,展示了这些命令在代码审查、日志分析和文本处理中的具体用途。同时,还提供了注意事项和技巧,帮助你更好地掌握这些命令。
34 4
|
26天前
|
网络协议 Linux
linux系统重要文件目录
本文介绍了Linux系统中的重要目录及其历史背景,包括根目录、/usr、/etc、/var/log和/proc等目录的结构和功能。其中,/etc目录下包含了许多关键配置文件,如网卡配置、DNS解析、主机名设置等。文章还详细解释了各目录和文件的作用,帮助读者更好地理解和管理Linux系统。
46 2
|
25天前
|
缓存 监控 Linux
|
28天前
|
Ubuntu Linux Shell
Linux 系统中的代码类型或脚本类型内容
在 Linux 系统中,代码类型多样,包括 Shell 脚本、配置文件、网络配置、命令行工具和 Cron 定时任务。这些代码类型广泛应用于系统管理、自动化操作、网络配置和定期任务,掌握它们能显著提高系统管理和开发的效率。
|
存储 Ubuntu Linux
【Linux】Python代码模块化
在目录下创建py文件,并进行运行任务要点:python的os和sys系统接口,文件接口
111 0
【Linux】Python代码模块化
|
16天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
105 6
|
17天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
58 3