简单的csv文件解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 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
目录
相关文章
|
2月前
|
数据处理 开发者 Python
Python 高级编程:深入解析 CSV 文件读取
在Python中,读取CSV文件是数据处理的重要环节。本文介绍了两种高效方法:一是利用pandas库的`read_csv`函数,将CSV文件快速转换为DataFrame对象,便于数据操作;二是通过csv模块的`csv.reader`按行读取CSV内容。此外,还涉及了如何选取特定列、解析日期格式、跳过指定行以及分块读取大文件等高级技巧,帮助开发者更灵活地处理各种CSV文件。参考链接:<https://www.wodianping.com/app/2024-10/48782.html>。
108 6
|
存储 Python
【可定制、转换时间戳】解析nc文件,并保存为csv文件
【可定制、转换时间戳】解析nc文件,并保存为csv文件
520 4
|
7月前
|
分布式计算 Java 大数据
MaxCompute产品使用合集之大数据计算MaxCompute外部表映射了oss中的csv文件,看到"\N"被解析为"N",是什么原因
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
Java 开发工具 git
java解析CSV文件三种方法(openCSV)
java解析CSV文件三种方法(openCSV)
232 0
|
数据采集
【详细步骤解析】爬虫小练习——爬取豆瓣Top250电影,最后以csv文件保存,附源码
【详细步骤解析】爬虫小练习——爬取豆瓣Top250电影,最后以csv文件保存,附源码
318 0
|
Java Maven
CSV 文件解析
<p>介绍</p> <p>    在很多时候,数据是以CSV文件格式存放的。在提取CSV数据时,我们借助javacsv这个开源工具来处理,还是比较方便。</p> <p><br></p> <p>javacsv in pom.xml of Maven<br></p> <p></p> <pre code_snippet_id="387236" snippet_file_name="bl
1985 0
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
76 0

推荐镜像

更多
下一篇
DataWorks