去除源文件中的重复行的程序流程及其C代码实现

(本文正在参加2015博客之星评选,诚邀你来投票,谢谢:http://vote.blog.csdn.net/blogstar2015/candidate?username=zhouzxi)

一、需求描述

要求对一个包含若干行记录且某几条记录相同的文件(源文件)实现去重操作,并将去重之后的记录写入到另外一个文件(目的文件)中。也即最后生成的文件中没有内容相同的两行记录。如果源文件中两条记录之间有空行,则在目的文件中一并将其去掉。

两条记录相同的标准是:

1) 字符个数及内容完全相同。

2) 去除空格及回车换行符之后的内容完全相同。

示例:

源文件样例:

ABCD
EFGH
abcd
AB CD
abcd

12345

对应的目的文件样例:

ABCD
EFGH
abcd
12345

二、程序总体流程

为了实现去重操作,我们考虑使用链表数据结构。先将源文件中的记录内容逐条读取,与链表中已经存在的记录内容相比较,如果没有与之相同的,则将该条记录加入链表。当源文件中的所有记录内容都读取完成之后,再将链表中的记录内容写入到目的文件中。

程序的总体流程如图1所示。

图1 程序的总体流程

三、重要程序流程及数据结构介绍

1.存放记录内容的链表

本程序使用链表来存放从源文件中读取到的每条记录,该链表的结构如下:

typedef struct T_FileInfoStruct
{
    UINT8  szContentLine[256];
    struct T_FileInfoStruct  *pNext;
} T_FileInfo;

2.判断某条记录是否已存在于链表的函数

每当读取到一条记录,就要判断该条记录是否已经存在于链表中了(第一条记录除外)。判断操作非常的简单,只需要遍历整个链表就可以了。该操作由IsInList函数实现,其具体代码如下:

UINT32 IsInList(T_FileInfo *ptContentListHead, UINT8 *pszContentLine)
{
    T_FileInfo  *pTmpInfo = ptContentListHead;

    if (ptContentListHead == NULL || pszContentLine == NULL)
    {
        printf("IsInList: input parameter(s) is NULL!\n");
        return 0;
    }

    while (pTmpInfo != NULL)
    {
        if (strncmp(pszContentLine, pTmpInfo->szContentLine, strlen(pszContentLine)) == 0)   // 存在于链表中
        {
            return 1;
        }
        pTmpInfo = pTmpInfo->pNext;
    }

    if (pTmpInfo == NULL)    // 不存在于链表中
    {
        return 0;
    }
}

3.去除记录的回车换行符、去除记录中的空格的函数

RmNewLine函数用于去除记录的回车换行符,RemoveSpaceInStr函数用于去除记录中的空格。这两个函数的程序代码请见附录。

四、程序编译及执行结果

将RemoveRepeatLine.c(详细的代码请见附录)程序上传到Linux机器上,使用gcc -g -o RemoveRepeatLine RemoveRepeatLine.c命令对其编译,生成RemoveRepeatLine。

在本程序中,我们使用的源文件为TestFile.txt,位于当前用户的zhouzhaoxiong/zzx/RemoveRepeatLine/TestFile目录下;生成的目的文件为ResultFile.txt,与源文件位于相同的目录之下。

本人使用的源文件TestFile.txt的内容为:

ABCD
EFGH
AB CD
1243565
dfbfdj
dgbsjn

1234 n
dsaghhn
 zvb
 vc

awsswgh
EFGH

zvb

1234 n
fedhh
 EFG H

运行RemoveRepeatLine命令之后,生成结果文件ResultFile.txt,其内容为:

ABCD
EFGH
1243565
dfbfdj
dgbsjn
1234n
dsaghhn
zvb
vc
awsswgh
fedhh

可见,程序满足了需求,去除了重复的行,同时将多余的空行也去掉了。

五、总结

关于本程序,有如下几点说明:

第一,链表这种数据结构在实际的软件开发项目中应用得非常的广泛,大家一定要熟练掌握其用法,特别是如何向链表中插入数据、如何遍历整个链表、如何清空链表等。

第二,写文件函数WriteToFile中,在“strncpy(szContentBuf, ptContentListHead->szContentLine, strlen(ptContentListHead->szContentLine));”语句之前,一定要加上“memset(szContentBuf, 0x00, sizeof(szContentBuf));”语句,否则写入文件中的内容可能不是我们想要的。大家也可以在自己的机器上试试,看加与不加“memset(szContentBuf, 0x00, sizeof(szContentBuf));”语句,生成的结果文件有何区别。

第三,在有关话单生成及处理的程序中,对源话单文件进行去重操作是大家必须考虑的问题。本文中的程序为相关项目的开发人员提供了参考。

附:完整的程序代码

/**********************************************************************
* 版权所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名称:RemoveRepeatLine.c
* 文件标识:无
* 内容摘要:去除源文件中的重复行
* 其它说明:无
* 当前版本:V1.0
* 作    者:Zhou Zhaoxiong
* 完成日期:20151209
*
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

// 存放文件每行内容的链表
typedef struct T_FileInfoStruct
{
    UINT8  szContentLine[256];
    struct T_FileInfoStruct  *pNext;
} T_FileInfo;

// 函数声明
void RemRepLineAndWriResFile(UINT8 *pszTestFile);
void RmNewLine(UINT8 *pInStr);
UINT32 IsInList(T_FileInfo *ptContentListHead, UINT8 *pszContentLine);
void WriteToFile(T_FileInfo *ptContentListHead);
void ClearList(T_FileInfo *ptContentListHead);
void RemoveSpaceInStr(UINT8 *pszStr, UINT32 iStrLen);

/**********************************************************************
* 功能描述:主函数
* 输入参数:无
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期       版本号        修改人            修改内容
* ---------------------------------------------------------------
* 20151209        V1.0     Zhou Zhaoxiong          创建
***********************************************************************/
INT32 main()
{
    UINT8  szTestFile[128] = {0};

    // 拼装配置文件路径
    snprintf(szTestFile, sizeof(szTestFile)-1, "%s/zhouzhaoxiong/zzx/RemoveRepeatLine/TestFile/TestFile.txt", getenv("HOME"));

    RemRepLineAndWriResFile(szTestFile);   // 调用函数完成去重及写文件的操作

    return 0;
}

/**********************************************************************
* 功能描述:去除源文件中的重复行, 并将去重之后的内容写入结果文件中
* 输入参数:pszTestFile-带路径的测试文件名
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期       版本号        修改人            修改内容
* ---------------------------------------------------------------
* 20151209        V1.0     Zhou Zhaoxiong          创建
***********************************************************************/
void RemRepLineAndWriResFile(UINT8 *pszTestFile)
{
    UINT8      szContentLine[256] = {0};
    UINT32     iLineLen           = 0;
    UINT32     iRetVal            = 0;

    FILE       *fp                = NULL;
    T_FileInfo *ptContentListHead = NULL;
    T_FileInfo *ptContentListTail = NULL;
    T_FileInfo *ptCurrentContent  = NULL; 

    if (pszTestFile == NULL)
    {
        printf("RemRepLineAndWriResFile: pszTestFile is NULL!\n");
        return;
    }

    printf("RemRepLineAndWriResFile: now, begin to process file %s\n", pszTestFile);

    if ((fp = fopen(pszTestFile, "r")) == NULL)
    {
        printf("RemRepLineAndWriResFile: open file %s failed!\n", pszTestFile);
        return;
    }
    else
    {
        ptContentListHead = NULL;
        ptContentListTail = NULL; 

        while (feof(fp) == 0 && ferror(fp) == 0)
        {
            memset(szContentLine, 0x00, sizeof(szContentLine));
            if (fgets(szContentLine, sizeof(szContentLine), fp) == NULL)    // 从源文件中读取一行内容
            {
                printf("RemRepLineAndWriResFile: get line null, break.\n");
                break;
            }
            else
            {
                printf("RemRepLineAndWriResFile: get content line: %s\n", szContentLine);
            }

            RmNewLine(szContentLine);      // 去掉字符串后面的回车换行符
            RemoveSpaceInStr(szContentLine, strlen(szContentLine));  // 去掉字符串中的空格

            iLineLen = strlen(szContentLine);
            if (iLineLen == 0)        // 如果该行的有效长度为0, 则继续读取下一行
            {
                printf("RemRepLineAndWriResFile: the length of line is 0, continue to read the next content line.\n");
                continue;
            }

            if (ptContentListHead != NULL)    // 判断当前行是否已经存在了
            {
                iRetVal = IsInList(ptContentListHead, szContentLine);
                if (iRetVal == 1)   // 已经存在
                {
                    printf("RemRepLineAndWriResFile: this content line has already existed.\n");
                    continue;
                }
            }

            // 将当前行加入链表中
            ptCurrentContent = (T_FileInfo *)malloc(sizeof(T_FileInfo));
            if (ptCurrentContent == NULL)
            {
                printf("RemRepLineAndWriResFile: exec malloc failed, memory may be not enough.\n");
                return;
            }

            memcpy(ptCurrentContent->szContentLine, szContentLine, strlen(szContentLine));

            if (ptContentListHead == NULL)   // 链表为空时作为链表头
            {
                ptContentListHead = ptCurrentContent;
                ptContentListTail = ptCurrentContent;
            }
            else
            {
                if (ptContentListTail != NULL)  // 插入链表尾部
                {

                    ptContentListTail->pNext = ptCurrentContent;
                    ptContentListTail        = ptCurrentContent;
                }
            }
        }
        // 源文件使用完毕
        fclose(fp);
        fp = NULL;
    }

    // 将去重之后的结果写入文件中
    WriteToFile(ptContentListHead);

    // 清空链表
    ClearList(ptContentListHead);
    ptContentListHead = NULL;
}

/**********************************************************************
* 功能描述:去掉字符串后面的回车换行符
* 输入参数:pInStr-输入字符串
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期       版本号        修改人            修改内容
* ---------------------------------------------------------------
* 20151209        V1.0     Zhou Zhaoxiong          创建
***********************************************************************/
void RmNewLine(UINT8 *pInStr)
{
    UINT32  iStrLen = 0;

    if (pInStr == NULL)
    {
        printf("RmNewLine: pInStr is NULL!\n");
        return;
    }

    iStrLen = strlen(pInStr);
    while (iStrLen > 0)
    {
        if (pInStr[iStrLen-1] == ‘\n‘ || pInStr[iStrLen-1] == ‘\r‘)
        {
            pInStr[iStrLen-1] = ‘\0‘;
        }
        else
        {
            break;
        }

        iStrLen --;
    }
}

/**********************************************************************
* 功能描述:判断某行内容是否已经存在于链表中了
* 输入参数:pInStr-输入字符串
* 输出参数:无
* 返 回 值:1-存在  0-不存在
* 其它说明:无
* 修改日期       版本号        修改人            修改内容
* ---------------------------------------------------------------
* 20151209        V1.0     Zhou Zhaoxiong          创建
***********************************************************************/
UINT32 IsInList(T_FileInfo *ptContentListHead, UINT8 *pszContentLine)
{
    T_FileInfo  *pTmpInfo = ptContentListHead;

    if (ptContentListHead == NULL || pszContentLine == NULL)
    {
        printf("IsInList: input parameter(s) is NULL!\n");
        return 0;
    }

    while (pTmpInfo != NULL)
    {
        if (strncmp(pszContentLine, pTmpInfo->szContentLine, strlen(pszContentLine)) == 0)   // 存在于链表中
        {
            return 1;
        }
        pTmpInfo = pTmpInfo->pNext;
    }

    if (pTmpInfo == NULL)    // 不存在于链表中
    {
        return 0;
    }
}

/**********************************************************************
 * 功能描述: 把内容写到文件中
 * 输入参数: ptContentListHead-文件记录链表
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 * 修改日期            版本号            修改人           修改内容
 * ----------------------------------------------------------------------
 * 20151209             V1.0          Zhou Zhaoxiong        创建
 ************************************************************************/
void WriteToFile(T_FileInfo *ptContentListHead)
{
    FILE   *fp                  = NULL;
    UINT8   szLocalFile[500]    = {0};
    UINT8   szContentBuf[256]   = {0};

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

    snprintf(szLocalFile, sizeof(szLocalFile)-1, "%s/zhouzhaoxiong/zzx/RemoveRepeatLine/TestFile/ResultFile.txt", getenv("HOME"));
    fp = fopen(szLocalFile, "a+");
    if (fp == NULL)
    {
         printf("WriteToFile: open local file failed, file=%s\n", szLocalFile);
         return;
    }

    while (ptContentListHead != NULL)
    {
        memset(szContentBuf, 0x00, sizeof(szContentBuf));
        strncpy(szContentBuf, ptContentListHead->szContentLine, strlen(ptContentListHead->szContentLine));
        printf("WriteToFile: LocalFile=%s, ContentBuf=%s\n", szLocalFile, szContentBuf);

        fputs(szContentBuf, fp);
        fputs("\r\n", fp);     // 加上回车换行符
        fflush(fp);

        ptContentListHead = ptContentListHead->pNext;
    }

    fclose(fp);
    fp = NULL;

    return;
}

/**********************************************************************
* 功能描述: 清除链表
* 输入参数: ptContentListHead-链表指针
* 输出参数: 无
* 返 回 值: 无
* 其它说明: 无
* 修改日期        版本号        修改人          修改内容
* ------------------------------------------------------
* 20151209        V1.0     Zhou Zhaoxiong        创建
***********************************************************************/
void ClearList(T_FileInfo *ptContentListHead)
{
    T_FileInfo *ptContentList = NULL;
    T_FileInfo *pTmpData      = NULL;

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

    ptContentList = ptContentListHead;
    while (ptContentList != NULL)
    {
        pTmpData = ptContentList;
        ptContentList  = ptContentList->pNext;
        free(pTmpData);
    }
}

/**********************************************************************
* 功能描述: 清除字符串中的空格
* 输入参数: pszStr-输入的字符串
             iStrLen-最大长度
* 输出参数: 无
* 返 回 值: 无
* 其它说明: 无
* 修改日期        版本号     修改人            修改内容
* ------------------------------------------------------
* 20151209        V1.0     Zhou Zhaoxiong        创建
***********************************************************************/
void RemoveSpaceInStr(UINT8 *pszStr, UINT32 iStrLen)
{
    UINT8  szResult[256] = {0};
    UINT8  szBuf[256]    = {0};
    UINT32 iLoopFlagSrc  = 0;
    UINT32 iLoopFlagDst  = 0;       

    if (pszStr == NULL)
    {
        return;
    }

    memcpy(szBuf, pszStr, iStrLen);

    for (iLoopFlagSrc = 0; iLoopFlagSrc < strlen(szBuf); iLoopFlagSrc ++)
    {
        if (szBuf[iLoopFlagSrc] != ‘ ‘)
        {
            szResult[iLoopFlagDst] = szBuf[iLoopFlagSrc];
            iLoopFlagDst ++;
        }
    }

    szResult[iLoopFlagDst+1] = 0;

    memcpy(pszStr, szResult, iStrLen);

    return;
}

(本文中的程序已提交到了GitHub上,欢迎下载阅读:https://github.com/zhouzxi/RemoveRepeatLine)

时间: 2024-10-11 17:41:32

去除源文件中的重复行的程序流程及其C代码实现的相关文章

sort排序命令 uniq 去除排序过的文件中的重复行 cut提取命令 wc 统计命令

sort 命令对 File 参数指定的文件中的行排序,并将结果写到标准输出.如果 File 参数指定多个文件,那么 sort 命令将这些文件连接起来,并当作一个文件进行排序. sort语法 [[email protected] ~]# sort [-fbMnrtuk] [file or stdin] 选项与参数: -f :忽略大小写的差异,例如 A 与 a 视为编码相同: -b :忽略最前面的空格符部分: -M :以月份的名字来排序,例如 JAN, DEC 等等的排序方法: -n :使用『纯数字

Java如何取源文件中文件名和行号

package com.github.jdk; /**  * Java如何取源文件中文件名和行号  *   * {@code StackTraceElement}的定义详见文档  *   * @author doctor  *  * @time 2015年3月13日 上午9:16:16  */ public class StackTraceElementPractice { public static void main(String[] args) { StackTraceElement[] 

js去除数组中的重复值

hasOwnProperty(property) 方法 JavaScript中hasOwnProperty函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性. 使用方法: object.hasOwnProperty(proName) 其中参数object是必选项.一个对象的实例.proName是必选项.一个属性名称的字符串值. 如果 object 具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回 true:反之则返回 false.此方法无法检查该对

去除字符串中连续重复的字符

目的:把字符串中连续重复的字符赐除掉. 输入:序列:kkkhan888shioobo66 正确的返回结果应该是:hanshibo 思路解析 1 使用JAVA正则表达式,匹配出连续相同的字符或数字. 2 查找出匹配出来的序列,并取出来放到list里面 3 对list进行排序.把重复的序列排在前面.(该步可省略) 4找出连续重复的子序列,并把这些连续重复的子序列用空(字字符串)替换. 5 返回输出. code public class Test { public static void main(S

一些常用的方法(1)--去除DataTable中的重复数据

private DataTable Display(DataTable dtSource)        {            DataTable dtTemp = dtSource.Copy();            foreach (DataRow dr in dtSource.Rows)            {                DataRow[] drRepeat = dtTemp.Select("AA='" + dr["AA"].ToS

去除数组中的重复数据

1 public static String[] array_unique(String[] a) { 2 Set<String> set = new HashSet<String>(); 3 set.addAll(Arrays.asList(a)); 4 return set.toArray(new String[0]); 5 } 去除数组中的重复数据

计算 num 的 n 次幂、n 的阶乘、斐波那契数列、字符串的字节长度、去除字符串中的重复字符

1 //计算 num 的 n 次幂 2 function numPow(num, n) { 3 if (n == 1) { 4 return num; 5 } 6 return num * numPow(num, n - 1); 7 } 8 9 //计算 n 的阶乘 10 function nFactorial(n) { 11 if (n == 1) { 12 return 1; 13 } 14 return n * nFactorial(n - 1); 15 } 16 17 //斐波那契数列,

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

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

sed tr 去除PATH中的重复项

最近发现由于自己不良的安装软件的习惯,shell的PATH路径包含了很多冗余的项.这里使用shell命令去除PATH的冗余项. export PATH=$(echo $PATH | sed 's/:/\n/g' | sort | uniq | tr -s '\n' ':' | sed 's/:$//g') 上面的代码可以去除linux环境变量中的重复项. 最近查看环境变量时,发现PATH中包含了很多重复项,而在~/.bashrc中又没有看到什么重复的指令,只好手动去重了. 起先在网上看到有人使用