CSV是一种古老的数据传输格式,它的全称是Comma-Separated Values(逗号分隔值)。出生在那个标准缺失的蛮荒年代,CSV的标准一直(到2005年)是NULL——世间存在着N种CSV格式,它们自成体系,相互不兼容。比如我们从名字可以认为CSV至少是一种使用逗号分隔的格式,但是实际上,有的CSV格式却是使用分号(;)去做分隔。假如,不存在一种标准,那么这东西最终会因为碎片化而发展缓慢,甚至没落。本文讨论的CSV格式是基于2005年发布的RFC4180规范。我想,在这个规范发布之后,大家应该会更加自觉的遵从这套规范去开发——虽然这套标准依旧存在着一些致命的缺陷。(转载请指明出于breaksoftware的csdn博客)
我们可以从IETF上获得包含了CSV格式定义的文档。当然,如果你觉得看英文文档麻烦,你可以直接看我的下文。
- 在不包含换行符(CRLF即\r\n)的单条信息时,数据要保持在一行,并且使用\r\n结束。
aaa,bbb,ccc,dddCRLF 合法
aaa,b 内容中无换行符,而单条信息被换行,不合法
bb.ccc,dddCRLF
- 最后一条信息可以没有换行符(当然有换行符也是合法的)
aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhh 合法
aaa,bbb,ccc,dddCRLF
eee,fff,ggg,hhhCRLF 合法
- 第一条信息可能是一个头信息。这个头信息和之后信息格式是相同的,并且和之后的信息有相同的模块数(上例中,aaa和bbb和ccc和ddd各被视为一个模块)。(个人认为这是RFC设计这个CSV格式的一个缺陷,因为这个规则将无法让我们从规则的角度去确认第一条信息到底是头信息还是普通信息。当然RFC这么设计肯定有它的原因。)
index,character 合法,从字面意思上我们可以认为这个是头,当然我们也可以认为它不是头
1,aCRLF
2,bCRLF
indexCRLF 非法,模块数不统一
1,aCRLF
- 每条信息都要使用半角逗号(,)分隔出若干模块。每条信息的模块数要相等。每条信息的最后一个模块之后不可以使用半角逗号。空格符被视为一个模块的内容而不可被忽略。(这条规则包含的信息量相对较多)
aaa,bbbCRLF 合法
ccc,ddd,CRLF 非法,一条信息的最后一个模块不可以使用半角逗号
eee;ffffCRLF 非法,要使用半角逗号分隔,而不是分号
ggg, h h h CRLF 合法,注意hhh模块的若干个空格,它属于模块内容而不可以被忽略
iii,jjj,kkkkCRLF 非法,模块数和上面不统一
- 每个模块首尾可以使用双引号扩住(当然也可以不使用)。如果不使用双引号扩住的模块,模块中不可以出现双引号。(言外之意:如果模块中出现双引号,则这个模块要用双引号将首尾扩住)
“aaa”,bbbCRLF 合法
a"aa,bbbCRLF 不合法,因为a"aa中包含了双引号,而这个模块没有被双引号扩住
- 如果模块中包含双引号、半角逗号或换行符,则模块首尾要用双引号扩住。
"a\r\na"a,bbbCRLF 合法,第一个模块包含了换行符,要用双引号包含
"a,aa",bbbCRLF 合法
- 当双引号出现在模块中,要将模块的首尾用双引号扩住,并且将模块中的一个双引号变成一对双引号。
“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格式文件的代码。
这些代码都在工程中。
分享到:
相关推荐
出生在那个标准缺失的蛮荒年代,CSV的标准一直(到2005年)是NULL——世间存在着N种CSV格式,它们自成体系,相互不兼容。比如我们从名字可以认为CSV至少是一种使用逗号分隔的格式,但是实际上,有的CSV格式却是使用...
一种简单的python算法,用于运行Tegrastats,然后从获得的带有时间戳的日志文件中解析数据。 命令行选项: --interval,-i(type = int,默认= 1000,help ='积分器的记录间隔,以毫秒为单位') --log_file,-f...
渗透仪 此GIF展示了这种平衡如何改善德国Saarland... 但是,如果确实存在一个自写的解析器模块,则可以使用此模块支持的每种地图格式(例如,自己的csv的格式),而不必是街道网络。 所有计算将针对单个桌面而不是更昂
Hadoop是一个开源的MapReduce平台,设计运行在大型分布式集群环境中,提供查询和分析服务。尤其适用于大数据系统,Hadoop为苹果、eBay、LinkedIn、雅虎和Facebook等公司提供重要软件环境。它为开发者进行数据存储、...
技术点18 输入和输出格式为CSV 的文件 3.4.2 output committing 的重要性 3.5 本章小结 第3 部分 大数据模式 4 处理大数据的MapReduce 模式 4.1 Join 4.1.1 Repartition Join 技术点19 优化...
fstalign是一种工具,用于在两个令牌序列之间建立对齐(此处称为“参考”和“假设”)。 它具有两个关键功能:计算单词错误率(WER),以及将引用与CTM假设对齐。 由于fstalign使用OpenFST和惰性算法进行基于文本的...
美联储经济数据 (FRED2) 大数据导入实用程序 概述 美联储经济数据库 (FRED2) 包含约 240,000 个离散系列数据 ( )。 每个系列都存储在一个单独的 CSV 文件中,每一行都包含一个日期和值... 为业务分析师提供一种将 FRED
-这不涉及正则表达式(这会占用大量内存并且非常昂贵),但是使用了一种在CSV表达式上执行子字符串的聪明方法。 -子字符串操作的结果(SearchableString中的startsWith,endsWith,findIndices)被缓存,因此后续...
封包 packetq是用于直接在PCAP文件上运行SQL查询的命令行工具,结果可以输出为JSON(默认),格式化/紧凑的CSV和XML。 它还包含一个非常简单的Web服务器,以便远程检查PCAP文件。 PacketQ以前称为DNS2db,但在2011年...
它是我的许多项目的基础库,也是我创建可重用代码的一种方式。 它在 Windows 7(64 位)和 Mac OS X(gcc 4.7 或更高版本)上受支持。 对 linux 系统的支持即将到来。 功能包括: 一个功能相当齐全的矩阵和向量...
还包括用于将数据解析为.csv格式的脚本。 使用两个数据集:正常窦性心律数据库,自发性室性心律失常数据库。 其他文件是使用Pan-Tompkins算法和Matlab代码将ECG信号转换为HRV的实验结果; 在最终实现中不使用 /...
8.2.2. 修改货币格式 8.2.3. Zend_Currency 的信息方法 8.2.4. 设置新缺省值 8.2.5. 加速 Zend_Currency 8.3. 从前面的版本迁移 8.3.1. 从 1.0.2 到 1.0.3 或更新的迁移 9. Zend_Date 9.1. Introduction ...
支持静态链接其它编程语言(如C/C++、汇编等)编译生成的静态库(.LIB或.OBJ),但仅限于COFF格式,支持cdecl和stdcall两种函数调用约定。 使用说明如下:函数声明和调用方法与DLL命令一致;“库文件名”以.lib...