CSV是一種古老的數據傳輸格式,它的全稱是Comma-Separated Values(逗號分隔值)。出生在那個標準缺失的蠻荒年代,CSV的標準一直(到2005年)是NULL――世間存在著N種CSV格式,它們自成體系,相互不兼容。比如我們從名字可以認為CSV至少是一種使用逗號分隔的格式,但是實際上,有的CSV格式卻是使用分號(;)去做分隔。假如,不存在一種標準,那么這東西最終會因為碎片化而發展緩慢,甚至沒落。本文討論的CSV格式是基于2005年發布的RFC4180規范。我想,在這個規范發布之后,大家應該會更加自覺的遵從這套規范去開發――雖然這套標準依舊存在著一些致命的缺陷
我們可以從IETF上獲得包含了CSV格式定義的文檔。當然,如果你覺得看英文文檔麻煩,你可以直接看我的下文。
1.在不包含換行符(CRLF即 /r/n)的單條信息時,數據要保持在一行,并且使用/r/n結束。
aaa,bbb,ccc,dddCRLF 合法
aaa,b 內容中無換行符,而單條信息被換行,不合法
bb.ccc,dddCRLF
2.最后一條信息可以沒有換行符(當然有換行符也是合法的)
aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhh 合法
aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhhCRLF 合法
3.第一條信息可能是一個頭信息。這個頭信息和之后信息格式是相同的,并且和之后的信息有相同的模塊數(上例中,aaa和bbb和ccc和ddd各被視為一個模塊)。(個人認為這是RFC設計這個CSV格式的一個缺陷,因為這個規則將無法讓我們從規則的角度去確認第一條信息到底是頭信息還是普通信息。當然RFC這么設計肯定有它的原因。)
index,character 合法,從字面意思上我們可以認為這個是頭,當然我們也可以認為它不是頭
1,aCRLF
2,bCRLF
indexCRLF 非法,模塊數不統一
1,aCRLF
4.每條信息都要使用半角逗號(,)分隔出若干模塊。每條信息的模塊數要相等。每條信息的最后一個模塊之后不可以使用半角逗號。空格符被視為一個模塊的內容而不可被忽略。(這條規則包含的信息量相對較多)
aaa,bbbCRLF 合法
ccc,ddd,CRLF 非法,一條信息的最后一個模塊不可以使用半角逗號
eee;ffffCRLF 非法,要使用半角逗號分隔,而不是分號
ggg, h h h CRLF 合法,注意hhh模塊的若干個空格,它屬于模塊內容而不可以被忽略
iii,jjj,kkkkCRLF 非法,模塊數和上面不統一
5.每個模塊首尾可以使用雙引號擴住(當然也可以不使用)。如果不使用雙引號擴住的模塊,模塊中不可以出現雙引號。(言外之意:如果模塊中出現雙引號,則這個模塊要用雙引號將首尾擴住)
“aaa”,bbbCRLF 合法
a"aa,bbbCRLF 不合法,因為a"aa中包含了雙引號,而這個模塊沒有被雙引號擴住
6.如果模塊中包含雙引號、半角逗號或換行符,則模塊首尾要用雙引號擴住。
"a/r/na"a,bbbCRLF 合法,第一個模塊包含了換行符,要用雙引號包含
"a,aa",bbbCRLF 合法
7.當雙引號出現在模塊中,要將模塊的首尾用雙引號擴住,并且將模塊中的一個雙引號變成一對雙引號。
“a""aa”,bbbCRLF 合法,原始數據為a"aa,bbb
有了以上規則,我們可以編寫出相應的提取算法。以下是我在工作中編寫的一套從CSV文件中提取信息的核心代碼
BOOL CCSV2Json::Parse() { BOOL bSuc = FALSE; do { if ( INVALID_HANDLE_VALUE == m_hFile ) { break; } OVERLAPPED ov; memset(&ov, 0, sizeof(OVERLAPPED)); BYTE lpBuffer[BUFFERSIZE] = {0}; DWORD dwHaveRead = 0; std::string strSingle; BOOL bFirstDoubleQuotes = FALSE; // 第一個字符是否為" BOOL bBeforeIsDoubleQuotes = FALSE; BOOL bBeforeIsX0D = FALSE; ListString Liststr; BOOL bPairDoubleQuotes = FALSE; while ( ReadFile(m_hFile, lpBuffer, sizeof(lpBuffer), &dwHaveRead, &ov ) ) { ov.Offset += dwHaveRead; for ( DWORD dwIndex = 0; dwIndex < dwHaveRead; dwIndex++ ) { BYTE& by = *(lpBuffer + dwIndex); if ( bFirstDoubleQuotes ) { // 有前置" if ( IsDoubleQuotes(by) ) { bBeforeIsX0D = FALSE; if ( bBeforeIsDoubleQuotes ) { strSingle.append(1, (char)(by)); bBeforeIsDoubleQuotes = FALSE; } else { bBeforeIsDoubleQuotes = TRUE; } } else { if ( bBeforeIsDoubleQuotes ) { bFirstDoubleQuotes = FALSE; } bBeforeIsDoubleQuotes = FALSE; if ( IsCRLF( by ) ){ if ( bFirstDoubleQuotes ) { strSingle.append(1, (char)(by)); } else if (FALSE == bBeforeIsX0D) { Liststr.push_back(strSingle); m_Listliststr.push_back(Liststr); Liststr.clear(); strSingle.clear(); bFirstDoubleQuotes = FALSE; } bBeforeIsX0D = IsX0D(by); } else if ( IsSep(by) ) { bBeforeIsX0D = FALSE; if ( bFirstDoubleQuotes ) { strSingle.append(1, (char)(by)); } else { bBeforeIsX0D = FALSE; Liststr.push_back(strSingle); strSingle.clear(); } } else { bBeforeIsX0D = FALSE; strSingle.append(1, (char)(by)); } } } else{ // 如果無前置" if ( IsDoubleQuotes(by) ) { bBeforeIsX0D = FALSE; if ( strSingle.empty() ) { // 空串,第一個是" bFirstDoubleQuotes = TRUE; bBeforeIsDoubleQuotes = FALSE; } else { strSingle.append(1,(char)(by)); continue; } } else { bBeforeIsDoubleQuotes = FALSE; if ( IsCRLF( by ) ){ if (FALSE == bBeforeIsX0D) { Liststr.push_back(strSingle); m_Listliststr.push_back(Liststr); Liststr.clear(); strSingle.clear(); bFirstDoubleQuotes = FALSE; bBeforeIsDoubleQuotes = FALSE; } else { // 連續/r/n不考慮設置為新的行 } bBeforeIsX0D = IsX0D(by); } else if ( IsSep(by) ) { bBeforeIsX0D = FALSE; Liststr.push_back(strSingle); strSingle.clear(); } else { bBeforeIsX0D = FALSE; strSingle.append(1, (char)(by)); } } } } memset(lpBuffer, 0, sizeof(lpBuffer)); } if ( false == strSingle.empty() ) { // while ( IsCRLF(strSingle.at(strSingle.length() - 1) ) && strSingle.length() > 0) { // strSingle = strSingle.substr(0, strSingle.length() - 1 ); // } Liststr.push_back(strSingle); m_Listliststr.push_back(Liststr); Liststr.clear(); strSingle.clear(); } bSuc = TRUE; } while (0); if ( NULL != m_hFile ) { CloseHandle(m_hFile); m_hFile = NULL; } return bSuc; } 這段代碼將CSV文件提取出來一個std::list<std::list<std::string>>結構。如上面名字所示,我這個功能是要將CSV文件轉換為json格式,相應的我也編寫了從json格式轉換為CSV格式文件的代碼。這些代碼都在工程中。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
新聞熱點
疑難解答
圖片精選