csv4cj:CSV文件读写解析工具,支持中文与格式配置

一个支持csv文件的读写、解析的库

分支6Tags8

csv4cj

简介

一款用于操作 CSV 文件的工具,支持 CSV 文件的读写和解析,并且兼容中文。

特性

  • 🚀 读取、解析及结构化 CSV 文件

  • 🌍 将结构化的 CSV 记录导出为 CSV 文件

  • 💪 提供对 CSV 文件格式的配置支持

  • 🛠️ 支持中文处理

软件架构

源码目录

.
├── doc
│   ├── assets
│   ├── cjcov
│   ├── feature_api.md
│   └── design.md
├── samples
│   ├── csv_perf
│   ├── read_csv_file
│   ├── set_csv_header
│   └── write_csv_file
├── src
│   ├── appendable.cj
│   ├── buffered_reader.cj
│   ├── char_reader.cj
│   ├── constants.cj
│   ├── csv_out_format.cj
│   ├── csv_parse_format.cj
│   ├── csv_parser.cj
│   ├── csv_printer.cj
│   ├── csv_reader.cj
│   ├── csv_record.cj
│   ├── helper.cj
│   ├── lexer.cj
│   └── token.cj
│   └── utf8_reader_stream.cj
└── test
    ├── HLT
    ├── LLT
    └── UT
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README.OpenSource
  • sample 意为使用示例
  • doc 指的是库的设计文档、提案、库的使用文档以及LLT用例的覆盖报告
  • src 是库的源码目录
  • test 包含测试用例,涵盖了HLT用例、LLT用例和UT用例

接口说明

主要涵盖核心类及其成员函数的详细说明,具体请参见 API

编译

提供了两种编译方式

  1. 使用脚本编译

    1. 下载并配置编译脚本
    2. 执行 ciTest build 命令
  2. 使用包管理器编译

    1. 本三方库依赖于stdx,请参照stdx文档设置 CANGJIE_STDX_PATH 路径
    2. 执行 cjpm build 命令

示例

以下是一个示例 test.csv 文件的内容:

id,name,age,remark
30020,biboo,28,middle school student
30021,张飞,45,三国演义男4号𥻗𪧘 
#这是第3行的注释,里面有转义的tab键和引用符号
30022,关\t羽,48,著名的武财神αβγ
30023,刘备,50,"胳膊比较长,善于编草鞋"

以下为示例代码:

import std.fs.*
import std.collection.*
import std.posix.*
import csv4cj.*
import stdx.encoding.json.*

main() {
    let path: String = getcwd()
    println("自定义输出:")
    customPrintingDemo("${path}/test.csv")
    println("\nJson输出:")
    serialPrintingDemo("${path}/test.csv")
    println("\nCSV自定义格式输出:")
    csvPrint()
}

//自定义输出格式
func customPrintingDemo(fileName: String) {
//创建文件流
    let fileStream = File(fileName, OpenMode.Read)

    if (fileStream.canRead()) {
        //创建字符读取的解析流
        let stream = UTF8ReaderStream(fileStream)
        let reader = CSVReader(stream)

        //创建格式化的解析参数
        let format: CSVParseFormat = CSVParseFormat.DEFAULT.setSkipHeaderRecord(true).setFirstLineAsHeader(true)

        //创建解析器
        let csvParser = CSVParser(reader, format)

        //遍历每一行解析记录
        for (csvRecord in csvParser) {
            let rowNo = csvRecord.getRecordNumber()

            //使用表头标题获取对应列的值
            let id = csvRecord.get("id",csvParser.getHeaderDict()) ?? ""
            let name = csvRecord.get("name",csvParser.getHeaderDict()) ?? ""

            //使用列序号获取对应列的值
            let age = csvRecord.get(2)
            let remark = csvRecord.get(3)
            if (let Some(comment) = csvRecord.getComment()) {
                println(
                    "RowNo:${rowNo}|Id:${id}|name:${name}|age:${age}|remark:${remark}|comment:${comment}")
            } else {
                println("RowNo:${rowNo}|Id:${id}|name:${name}|age:${age}|remark:${remark}")
            }
        }
        fileStream.close()
    }
}

//Json格式输出
func serialPrintingDemo(fileName: String) {
    //创建文件流
    let fileStream = File(fileName, OpenMode.Read)

    if (fileStream.canRead()) {
        //创建字符读取的解析流
        let stream = UTF8ReaderStream(fileStream)

        let reader = CSVReader(stream)

        //创建格式化的解析参数
        let format: CSVParseFormat = CSVParseFormat.DEFAULT.setCommentMarker(r'α').setDelimiter("𪧘𪧘")

        //创建解析器
        let csvParser = CSVParser(reader, format)

        let recordList = csvParser.parseRecordsToEnd()

        //遍历每一行解析记录
        for (csvRecord in recordList) {
            println(csvRecord.serialize().toJson())
            CSVRecord.deserialize(csvRecord.serialize())
        }
        fileStream.close()
    }
}

//按照csv格式输出
func csvPrint() {
    let csvContent = 
        ###"# Comment before header
author,title,publishDate
Dan Simmons,Hyperion,"1989"
# Comment Line 1
# Comment Line 2
# Comment Line 3
Douglas Adams,The Hitchhiker's \"Guide\" to the Galaxy,1979
Douglas John,The Hitchhiker's \"Guide\" to the Mars,1979"###

    //创建字符串流
    let readerStream = StringStream(csvContent)

    let reader = CSVReader(readerStream)

    //创建格式化的解析参数
    let format: CSVParseFormat = CSVParseFormat.DEFAULT

    //创建解析器
    let csvParser = CSVParser(reader, format)

    let recordList = csvParser.parseRecordsToEnd()
    let outFormat = CSVOutFormat.DEFAULT
    let sbOut = StringBuilder()
    let csvPrint = CSVPrinter(outFormat)

    var firstLine = true

    //遍历每一行解析记录
    for (csvRecord in recordList) {
        if (firstLine) {
            firstLine = false
            csvPrint.print(csvRecord, sbOut)
        } else {
            csvPrint.printLine(sbOut)
            csvPrint.print(csvRecord, sbOut)
        }
    }
    println(sbOut.toString())
}

执行结果呈现如下:

自定义输出:
RowNo:1|Id:30020|name:biboo|age:28|remark:middle school student
RowNo:2|Id:30021|name:张飞|age:45|remark:三国演义男4号𥻗𪧘
RowNo:3|Id:30022|name:关       羽|age:48|remark:著名的武财神αβγ|comment:这是第3行的注释,里面有转义的tab键和引用符号
RowNo:4|Id:30023|name:刘备|age:50|remark:胳膊比较长,善于编草鞋

 Json输出:
{"recordNumber":1,"values":["id,name,age,remark"],"comment":null}
{"recordNumber":2,"values":["30020,biboo,28,middle school student"],"comment":null}
{"recordNumber":3,"values":["30021,张飞,45,三国演义男4号𥻗𪧘"],"comment":null}
{"recordNumber":4,"values":["#这是第3行的注释,里面有转义的tab键和引用符号"],"comment":null}
{"recordNumber":5,"values":["30022,关\t羽,48,著名的武财神αβγ"],"comment":null}
{"recordNumber":6,"values":["30023,刘备,50,\"胳膊比较长,善于编草鞋\""],"comment":null}

CSV自定义格式输出:
author,title,publishDate
[Dan Simmons,Hyperion,1989
Douglas Adams,"The Hitchhiker's \"Guide\" to the Galaxy",1979
Douglas John,"The Hitchhiker's \"Guide\" to the Mars",1979

性能测试

性能测试示例请见samples目录。

其中csv_perf为解析与写入测试,该测试针对一个包含43538条全国街道信息的CSV文件进行解析,并根据是否使用缓冲区来执行输出,具体结果如下:

:实际使用时间可能会因执行环境的不同而有所变化。

一共解析了43538条记录,使用了313ms334us400ns时间
一共写入了43538条记录,使用了962ms50us500ns时间
一共写入了43538条记录,使用了121ms331us400ns时间

约束与限制

本规范已通过以下版本验证:

仓颉版本:1.0.0

开源协议

本项目采用 Apache License 2.0 协议,欢迎自由使用并参与开源事业。

参与贡献

@张磊

项目介绍

一个支持csv文件的读写、解析的库

定制我的领域

下载使用量

0

项目总下载次数(含Clone、Pull、 zip 包及 release 下载),每日凌晨更新

语言类型

Cangjie100%