標(biāo)準(zhǔn)CSV格式的介紹和分析以及解析算法實(shí)例詳解
CSV是一種古老的數(shù)據(jù)傳輸格式,它的全稱是Comma-Separated Values(逗號(hào)分隔值)。出生在那個(gè)標(biāo)準(zhǔn)缺失的蠻荒年代,CSV的標(biāo)準(zhǔn)一直(到2005年)是NULL——世間存在著N種CSV格式,它們自成體系,相互不兼容。比如我們從名字可以認(rèn)為CSV至少是一種使用逗號(hào)分隔的格式,但是實(shí)際上,有的CSV格式卻是使用分號(hào)(;)去做分隔。假如,不存在一種標(biāo)準(zhǔn),那么這東西最終會(huì)因?yàn)樗槠l(fā)展緩慢,甚至沒落。本文討論的CSV格式是基于2005年發(fā)布的RFC4180規(guī)范。我想,在這個(gè)規(guī)范發(fā)布之后,大家應(yīng)該會(huì)更加自覺的遵從這套規(guī)范去開發(fā)——雖然這套標(biāo)準(zhǔn)依舊存在著一些致命的缺陷
我們可以從IETF上獲得包含了CSV格式定義的文檔。當(dāng)然,如果你覺得看英文文檔麻煩,你可以直接看我的下文。
1.在不包含換行符(CRLF即 \r\n)的單條信息時(shí),數(shù)據(jù)要保持在一行,并且使用\r\n結(jié)束。
aaa,bbb,ccc,dddCRLF 合法
aaa,b 內(nèi)容中無(wú)換行符,而單條信息被換行,不合法
bb.ccc,dddCRLF
2.最后一條信息可以沒有換行符(當(dāng)然有換行符也是合法的)
aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhh 合法
aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhhCRLF 合法
3.第一條信息可能是一個(gè)頭信息。這個(gè)頭信息和之后信息格式是相同的,并且和之后的信息有相同的模塊數(shù)(上例中,aaa和bbb和ccc和ddd各被視為一個(gè)模塊)。(個(gè)人認(rèn)為這是RFC設(shè)計(jì)這個(gè)CSV格式的一個(gè)缺陷,因?yàn)檫@個(gè)規(guī)則將無(wú)法讓我們從規(guī)則的角度去確認(rèn)第一條信息到底是頭信息還是普通信息。當(dāng)然RFC這么設(shè)計(jì)肯定有它的原因。)
index,character 合法,從字面意思上我們可以認(rèn)為這個(gè)是頭,當(dāng)然我們也可以認(rèn)為它不是頭
1,aCRLF
2,bCRLF
indexCRLF 非法,模塊數(shù)不統(tǒng)一
1,aCRLF
4.每條信息都要使用半角逗號(hào)(,)分隔出若干模塊。每條信息的模塊數(shù)要相等。每條信息的最后一個(gè)模塊之后不可以使用半角逗號(hào)??崭穹灰暈橐粋€(gè)模塊的內(nèi)容而不可被忽略。(這條規(guī)則包含的信息量相對(duì)較多)
aaa,bbbCRLF 合法
ccc,ddd,CRLF 非法,一條信息的最后一個(gè)模塊不可以使用半角逗號(hào)
eee;ffffCRLF 非法,要使用半角逗號(hào)分隔,而不是分號(hào)
ggg, h h h CRLF 合法,注意hhh模塊的若干個(gè)空格,它屬于模塊內(nèi)容而不可以被忽略
iii,jjj,kkkkCRLF 非法,模塊數(shù)和上面不統(tǒng)一
5.每個(gè)模塊首尾可以使用雙引號(hào)擴(kuò)?。ó?dāng)然也可以不使用)。如果不使用雙引號(hào)擴(kuò)住的模塊,模塊中不可以出現(xiàn)雙引號(hào)。(言外之意:如果模塊中出現(xiàn)雙引號(hào),則這個(gè)模塊要用雙引號(hào)將首尾擴(kuò)?。?br />
“aaa”,bbbCRLF 合法
a"aa,bbbCRLF 不合法,因?yàn)閍"aa中包含了雙引號(hào),而這個(gè)模塊沒有被雙引號(hào)擴(kuò)住
6.如果模塊中包含雙引號(hào)、半角逗號(hào)或換行符,則模塊首尾要用雙引號(hào)擴(kuò)住。
"a\r\na"a,bbbCRLF 合法,第一個(gè)模塊包含了換行符,要用雙引號(hào)包含
"a,aa",bbbCRLF 合法
7.當(dāng)雙引號(hào)出現(xiàn)在模塊中,要將模塊的首尾用雙引號(hào)擴(kuò)住,并且將模塊中的一個(gè)雙引號(hào)變成一對(duì)雙引號(hào)。
“a""aa”,bbbCRLF 合法,原始數(shù)據(jù)為a"aa,bbb
有了以上規(guī)則,我們可以編寫出相應(yīng)的提取算法。以下是我在工作中編寫的一套從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; // 第一個(gè)字符是否為" 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{ // 如果無(wú)前置" if ( IsDoubleQuotes(by) ) { bBeforeIsX0D = FALSE; if ( strSingle.empty() ) { // 空串,第一個(gè)是" 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 { // 連續(xù)\r\n不考慮設(shè)置為新的行 } 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文件提取出來(lái)一個(gè)std::list<std::list<std::string>>結(jié)構(gòu)。如上面名字所示,我這個(gè)功能是要將CSV文件轉(zhuǎn)換為json格式,相應(yīng)的我也編寫了從json格式轉(zhuǎn)換為CSV格式文件的代碼。這些代碼都在工程中。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
上一篇:C++時(shí)間戳轉(zhuǎn)換成日期時(shí)間的步驟和示例代碼
欄 目:C語(yǔ)言
下一篇:淺談socket TCP編程中connect的一些坑
本文標(biāo)題:標(biāo)準(zhǔn)CSV格式的介紹和分析以及解析算法實(shí)例詳解
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/1943.html
您可能感興趣的文章
- 01-10如何用C語(yǔ)言生成簡(jiǎn)單格式的xml
- 01-10解析C++ 浮點(diǎn)數(shù)的格式化顯示
- 01-10C++中用兩個(gè)標(biāo)準(zhǔn)容器stack,實(shí)現(xiàn)一個(gè)隊(duì)列的方法詳解
- 01-10解析C++ 浮點(diǎn)數(shù)的格式化輸出
- 01-10淺析C語(yǔ)言字中的符串格式化顯示
- 01-10C++的sstream標(biāo)準(zhǔn)庫(kù)詳細(xì)介紹
- 01-10C++標(biāo)準(zhǔn)庫(kù)中sstream與strstream的區(qū)別詳細(xì)解析
- 01-10c語(yǔ)言常見圖片格式判斷實(shí)例
- 01-10C++標(biāo)準(zhǔn)模板庫(kù)函數(shù)sort的那些事兒
- 01-10c++中的string常用函數(shù)用法總結(jié)


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實(shí)例總結(jié)
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?