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格式文件的代码。这些代码都在工程中。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
本文向大家介绍JAVA中Context的详细介绍和实例分析,包括了JAVA中Context的详细介绍和实例分析的使用技巧和注意事项,需要的朋友参考一下 最熟悉的陌生人——Context 刚刚学android或者js等,都会看见这个频繁的字眼——Context。 意为”上下文“。 本文主要记述,Context到底是什么、如何理解Context、一个APP可以有几个Context、Context能干啥
本文向大家介绍Java List 用法详解及实例分析,包括了Java List 用法详解及实例分析的使用技巧和注意事项,需要的朋友参考一下 Java List 用法详解及实例分析 Java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对Java List用法做了详解。 List:元素是有序的(怎么存的就怎么取出来,顺序不会乱),元素可以重复(角标1上有个3,角标2上也可以有个
主要内容:1 Stack的概述,2 Stack的源码解析,2.1 构造器,2.2 API方法,3 总结基于JDK1.8对Java中的Stack集合的源码进行了深度解析,包括各种方法的底层实现,并且给出了Stack的使用建议。 1 Stack的概述 public class Stack< E > extends Vector< E > Stack,来自于JDK1.0 的古老集合类,底层是数组结构,元素可重复,有序(存放顺序),支持下标索引访问,允许null元素。 Stack类继承了Vector,所
主要内容:1 Vector的概述,2 Vector的源码解析,2.1 主要类属性,2.2 构造器与初始化容量,2.3 add方法与扩容机制,2.4 addAll方法,2.5 remove方法,2.6 get方法,2.7 set方法,2.8 clone方法,2.9 序列化,3 迭代器,3.1 Enumeration迭代器的概述,3.2 Enumeration迭代器的实现,3.3 分析Enumeration迭代器的死循环,,基于JDK1.8对Java中的Vector集合的源码进行了深度解析,包括各种
本文向大家介绍mysql(5.6及以下)解析json的方法实例详解,包括了mysql(5.6及以下)解析json的方法实例详解的使用技巧和注意事项,需要的朋友参考一下 mysql(5.6及以下)解析json 自测 改进,再找不到key,返回''值,之前的是在找不到的情况下,查找到第一的值。 总结 以上所述是小编给大家介绍的mysql(5.6及以下)解析json的方法实例详解,希望对大家有所帮助,
TLV格式数据解析。 什么是TLV数据?传送门:http://blog.csdn.net/chexlong/article/details/6974201 在标准的TLV数据解析过程中会遇到很多问题。在目前的开源的C/C 的项目中代码十分的庞大,整合起来十分不方便并没有OC的代码,故作者封装了这个类库供需要使用TLV格式数据的同学们学习使用。 [Code4App.com]