简单的csv文件解析

简介: csv文件的结构很简单,最基本的规则,就是用逗号分隔每一个单元格,用换行( 或者 )分隔每一列。其中需要注意的就是双引号为特殊的转义字符。详细的csv文件格式定义,在rfc4180中,主要的定义为: file = [header CRLF] record *(CRLF record) [CRLF]

csv文件的结构很简单,最基本的规则,就是用逗号分隔每一个单元格,用换行( 或者 )分隔每一列。其中需要注意的就是双引号为特殊的转义字符。详细的csv文件格式定义,在rfc4180中,主要的定义为:

file = [header CRLF] record *(CRLF record) [CRLF]
header = name *(COMMA name)
record = field *(COMMA field)
name = field
field = (escaped / non-escaped)
escaped = DQUOTE *(TEXTDATA / COMMA / CR / LF / 2DQUOTE) DQUOTE
non-escaped = *TEXTDATA
COMMA = %x2C
CR = %x0D
DQUOTE = %x22
LF = %x0A
CRLF = CR LF
TEXTDATA = %x20-21 / %x23-2B / %x2D-7E
根据这个定义,用状态机的方法,定义状态的枚举:
enum CSV_STATE
{
CELL_BEGIN, //单元格开始
CELL, //单元格
CELL_END, //单元格结束
QUOTATION_1, //第一个引号
QUOTATION_2, //第二个引号
QUOTATION_3, //第三个引号
ESCAPED_CELL, //转义的单元格
};
之前画的状态迁移图没有保存,直接上代码:
case CELL_BEGIN:
if (*iter == ‘\”‘) //第一个引号开始
{
_state = QUOTATION_1;
}
else if(*iter == ‘,’) //逗号,一个空的单元格
{
row._rowData.push_back(“”);
_cell.clear();
}
else //有中文,其他字符都保存
{
_cell.push_back(*iter);
_state = CELL;
}
break;
case CELL:
if(*iter == ‘\”‘) //rfc4180 未转义的单元格内不能出现引号
{
//简单处理,直接抛出异常
throw std::runtime_error(“error parse csv format arround: ” + line);
}
else if(*iter == ‘,’) //逗号,改单元格结束
{
row._rowData.push_back(_cell); //rfc4180规定单元格能够有空格,后面的空格暂时不管
_cell.clear();
_state = CELL_BEGIN;
}
else //因为有中文,不过滤rfc4180中排除的其他字符
{
_cell.push_back(*iter);
}
break;
case QUOTATION_1:
if(*iter == ‘\”‘) //第二个引号
{
_state = QUOTATION_2;
}
else
{
_cell.push_back(*iter);
_state = ESCAPED_CELL;
}
break;
case QUOTATION_2:
if(*iter == ‘\”‘) //第三个引号,为引号的转义
{
_cell.push_back(*iter);
_state = ESCAPED_CELL;
}
else if(*iter == ‘,’) //没有第三个引号,单元格结束
{
row._rowData.push_back(_cell);
_cell.clear();
_state = CELL_BEGIN;
}
else //其他字符,全部忽略
{
row._rowData.push_back(_cell);
_cell.clear();
_state = CELL_END;
continue;
}
break;
case CELL_END:
if(*iter == ‘,’) //忽略其他字符,遇到逗号,表示新的单元格开始了
{
_state = CELL_BEGIN;
}
break;
case ESCAPED_CELL:
if(*iter == ‘\”‘) //第二个引号
{
_state = QUOTATION_2;
}
else
{
_cell.push_back(*iter);
}
break;
每次读取一行进行解析,开始解析前,初始状态是CELL_BEGIN,解析完成后,有两种可能的状态:
1、处于CELL或者QUOTATION_2状态,因为最后一个单元格是没有逗号的,解析完成后,将最后一个单元格的数据push_back进去
2、处于ESCAPED_CELL状态,因为转义的单元格中间是可以包含换行的,所以需要返回false,继续下一行的读取
3、处于其他状态,应该是不可能的,
if(_state == ESCAPED_CELL) //转义单元格可以包含换行,还在这个单元格,需要继续解析下一行
{
_cell.push_back(‘\n’); //getline把本来应该存在的换行吃了,补回去
return false;
}
else if (_state == CELL || _state == QUOTATION_2) //最后一个单元格,保存下
{
row._rowData.push_back(_cell);
_cell.clear();
_state = CELL_BEGIN;
}
这里做的和rfc文档不同的是,rfc定义的文本字符,是 %x20-21 / %x23-2B / %x2D-7E,但因为csv中可能包含中文,所以除了特殊字符(逗号和双引号)之外,没有再判断字符的有效性。

转载自:https://coolex.info/blog/171.html
目录
相关文章
|
4月前
|
存储 JSON 关系型数据库
Pandas载入txt、csv、Excel、JSON、数据库文件讲解及实战(超详细 附源码)
Pandas载入txt、csv、Excel、JSON、数据库文件讲解及实战(超详细 附源码)
66 0
|
1月前
|
JavaScript
盘点CSV文件在Excel中打开后乱码问题的两种处理方法
盘点CSV文件在Excel中打开后乱码问题的两种处理方法
162 0
|
9月前
ENVI_IDL: 文本文件的读取(主要是txt、csv文件)
ENVI_IDL: 文本文件的读取(主要是txt、csv文件)
271 0
|
11月前
|
数据处理 Python
多线程操作CSV文件并且将CSV文件转成XLSX文件
多线程操作CSV文件并且将CSV文件转成XLSX文件
157 0
|
存储 数据挖掘 数据库
|
关系型数据库 数据库 PostgreSQL
导出CSV文件
导出CSV文件
333 0
|
关系型数据库 MySQL 中间件
Hyperf结合PhpOffice/PhpSpreadsheet实现Excel&CSV文件导出导入
Hyperf结合PhpOffice/PhpSpreadsheet实现Excel&CSV文件导出导入。PhpSpreadsheet是一个用纯PHP编写的库,它提供了一组类,允许您读取和写入各种电子表格文
864 0
Hyperf结合PhpOffice/PhpSpreadsheet实现Excel&CSV文件导出导入
Groovy学习笔记(1)读取CSV文件
  本篇分享讲展示如何在Groovy中读取CSV文件。   我们要读取的CSV文件foo.csv的内容如下:   Groovy代码如下: //import packages import java.
2064 0
|
Python Windows