一、需求描述
要求對一個包含若干行記錄且某幾條記錄相同的文件(源文件)實現去重操作,並將去重之後的記錄寫入到另外一個文件(目的文件)中。也即最後生成的文件中沒有內容相同的兩行記錄。如果源文件中兩條記錄之間有空行,則在目的文件中一並將其去掉。
兩條記錄相同的標准是:
1) 字符個數及內容完全相同。
2) 去除空格及回車換行符之後的內容完全相同。
示例:
源文件樣例:
ABCD
EFGH
abcd
AB CD
abcd
12345
對應的目的文件樣例:
ABCD
EFGH
abcd
12345
二、程序總體流程
為了實現去重操作,我們考慮使用鏈表數據結構。先將源文件中的記錄內容逐條讀取,與鏈表中已經存在的記錄內容相比較,如果沒有與之相同的,則將該條記錄加入鏈表。當源文件中的所有記錄內容都讀取完成之後,再將鏈表中的記錄內容寫入到目的文件中。
程序的總體流程如圖1所示。
圖1 程序的總體流程<喎?/kf/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;
}