去除源文件中的重复行的程序流程及其C代码实现
一、需求描述
要求对一个包含若干行记录且某几条记录相同的文件(源文件)实现去重操作,并将去重之后的记录写入到另外一个文件(目的文件)中。也即最后生成的文件中没有内容相同的两行记录。如果源文件中两条记录之间有空行,则在目的文件中一并将其去掉。
两条记录相同的标准是:
1) 字符个数及内容完全相同。
2) 去除空格及回车换行符之后的内容完全相同。
示例:
源文件样例:
dos="">ABCD EFGH abcd AB CD abcd 12345
对应的目的文件样例:
ABCD EFGH abcd 12345
二、程序总体流程
为了实现去重操作,我们考虑使用链表数据结构。先将源文件中的记录内容逐条读取,与链表中已经存在的记录内容相比较,如果没有与之相同的,则将该条记录加入链表。当源文件中的所有记录内容都读取完成之后,再将链表中的记录内容写入到目的文件中。
程序的总体流程如图1所示。
图1 程序的总体流程喎? f/ware/vc/"="" target="_blank" class="keylink">vcD4NCjxwPjxzdHJvbmc+yP2hotbY0qqzzNDywfezzLywyv2+3b3hubm96cncPC9zdHJvbmc+PGJyIC8+DQo8c3Ryb25nPjEutOa3xbzHwrzE2sjdtcTBtLHtPC9zdHJvbmc+PGJyIC8+DQqxvrPM0PLKudPDwbSx7cC0tOa3xbTT1LTOxLz+1tC2wcihtb21xMO/zPW8x8K8o6y4w8G0se21xL3hubnI58/Co7o8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;">
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! ); 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 #include #include // 数据类型重定义 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! ); return; } printf(RemRepLineAndWriResFile: now, begin to process file %s , pszTestFile); if ((fp = fopen(pszTestFile, r)) == NULL) { printf(RemRepLineAndWriResFile: open file %s failed! , 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. ); break; } else { printf(RemRepLineAndWriResFile: get content line: %s , 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. ); continue; } if (ptContentListHead != NULL) // 判断当前行是否已经存在了 { iRetVal = IsInList(ptContentListHead, szContentLine); if (iRetVal == 1) // 已经存在 { printf(RemRepLineAndWriResFile: this content line has already existed. ); continue; } } // 将当前行加入链表中 ptCurrentContent = (T_FileInfo *)malloc(sizeof(T_FileInfo)); if (ptCurrentContent == NULL) { printf(RemRepLineAndWriResFile: exec malloc failed, memory may be not enough. ); 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! ); return; } iStrLen = strlen(pInStr); while (iStrLen > 0) { if (pInStr[iStrLen-1] == ' ' || pInStr[iStrLen-1] == ' ') { pInStr[iStrLen-1] = ''; } 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! ); 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! ); 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 , szLocalFile); return; } while (ptContentListHead != NULL) { memset(szContentBuf, 0x00, sizeof(szContentBuf)); strncpy(szContentBuf, ptContentListHead->szContentLine, strlen(ptContentListHead->szContentLine)); printf(WriteToFile: LocalFile=%s, ContentBuf=%s , szLocalFile, szContentBuf); fputs(szContentBuf, fp); fputs( , 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! ); 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; }
喎?>