一、前言:

在日常的工作中,数据处理工作中,Excel 文件无疑是最常见的载体之一。无论是业务报表、数据统计还是配置清单,我们都可能面临在大量 Excel 数据中进行查找和替换的需求。面对庞杂的数据,手动操作不仅效率低下,还极易出错。本文将深入探讨如何通过仓颉开发编程的方式读取Excel数据能实现数据导入、批量处理、数据比对和更新等任务的自动化,不仅可以提高工作效率还能减少手动处理的错误风险。

在常规的公司业务中,大多数的数据管理与交互都是使用excel来进行维护,如何在仓颉开发中快速的 实现读取的Excel数据可以与其他系统进行交互或集成,实现数据的无缝传输和共享,满足特定项目的需求,今天给大家来介绍一下,借助仓颉的第三方库csv4cj的强大的功能,一款用于操作 CSV 文件的工具,支持 CSV 文件的读写和解析,并且兼容中文,实现高效、精准的 Excel 数据查找与替换,解决开发者和企业在数据处理上的痛点。通过本文,你将学会如何在仓颉语言中傻瓜式处理 Excel 文件的各项操作任务。


二、仓颉第三方库csv4cj介绍:

请添加图片描述

仓颉第三方库csv4cj一款用于操作 CSV 文件的工具,支持 CSV 文件的读写和解析,并且兼容中文。

1. 特性:

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

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

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

  • 🛠️ 支持中文处理


2. 软件架构源码目录:

├── 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用例

三、如何在项目中集成仓颉第三方库csv4cj:

由于目前没有中央仓库的概念,没有ohpm、npm、pip这些好用的包管理工具,我们只能通过依赖的模式,这里还是非常期待官方能够推出包管理器来维护第三方组件包与库。

1. git submodule引入csv4cj三方库依赖:

首先可以将 csv4cj 作为三方库依赖引入,目标工程把 csv4cj 依赖库作为 git submodule 引入进来:

> cd $工程根目录
> mkdir csv-cangjie
> cd csv-cangjie
> git submodule add "https://gitcode.com/Cangjie-TPC/csv4cj"

会在当前工程根目录下生成一个 .gitmodules 文件,内容如下:

[submodule "csv4cj"]
	path = csv4cj
	url = https://gitcode.com/Cangjie-TPC/csv4cj

请添加图片描述

接下来修改自身应用 csv-cangjie 目录下的 cjpm.toml 文件,添加依赖:

[dependencies]
  [dependencies.csv4cj]
  path = "./csv4cj"

请添加图片描述

在项目中使用 csv4cj 组件:

import csv4cj.*

2. 引入并设置仓颉编程语言 stdx:

仓颉编程语言提供了 stdx 模块,该模块提供了网络、安全等领域的通用能力。

当前 stdx 提供了如下包列表:

包名 功能
aspectCJ aspectCJ 包提供 Cangjie 中面向切面编程(Aspect Oriented Programming, AOP)相关的能力。
compress.zlib 提供压缩和解压缩功能。
crypto.crypto 提供安全加密能力。
crypto.digest 提供常用的消息摘要算法。
crypto.keys 提供非对称加密和签名算法。
crypto.x509 提供处理数字证书功能。
encoding.base64 提供字符串的 Base64 编码及解码。
encoding.hex 提供字符串的 Hex 编码及解码。
encoding.json 用于对 JSON 数据的处理,实现 String、JsonValue、DataModel 之间的相互转换。
encoding.json.stream 用于仓颉对象和 JSON 数据流之间的互相转换。
encoding.url 提供 URL 相关的能力,包括解析 URL 的各个组件,对 URL 进行编解码,合并 URL 或路径等。
fuzz.fuzz 提供基于覆盖率反馈的仓颉 fuzz 引擎及对应的接口,开发者可以编写代码对 API 进行测试。
log 提供了日志记录相关的能力。
logger 提供文本格式和 JSON 格式日志打印功能。
net.http 提供 HTTP/1.1、HTTP/2、WebSocket 协议的 Server 端和 Client 端的实现。
net.tls 用于进行安全加密的网络通信,提供创建 TLS 服务器、基于协议进行 TLS 握手、收发加密数据、恢复 TLS 会话等能力。
serialization.serialization 提供序列化和反序列化能力。
unittest.data 提供单元测试扩展能力。

接下来使用包管理器编译,这里会使用到本三方库依赖于stdx,可以进行设置 CANGJIE_STDX_PATH 路径,stdx 提供静态和动态两种二进制 ,两者独立使用,开发者可根据实际情况引用。

下载stdx包,stdx 软件包名称由操作系统、系统架构、stdx 版本号组成。其中,x.x.x.x 为实际 stdx 的版本号,其中前 3 位为 cjc 版本号。不同环境的软件包如下:

- cangjie-stdx-linux-aarch64-x.x.x.x.zip
- cangjie-stdx-linux-x64-x.x.x.x.zip
- cangjie-stdx-windows-x64-x.x.x.x.zip
- cangjie-stdx-mac-aarch64-x.x.x.x.zip
- cangjie-stdx-mac-x64-x.x.x.x.zip
- cangjie-stdx-ohos-aarch64-x.x.x.x.zip
- cangjie-stdx-ohos-x64-x.x.x.x.zip

请添加图片描述

这里很遗憾我的Mac电脑是intel芯片的,暂不支持仓颉的开发,只能使用windows的电脑来进行安装,打开stdx 项目主页,找到windows对应的发行版处找到对应版本的 stdx 二进制软件包,cangjie-stdx-windows-x64-1.0.1.1.zip。

下载解压后放到一个固定的目录下,这里建议不要使用中文命名文件名,最好是在C盘、D盘根目录下面,本人直接放到C盘下面,这里 stdx有两种模式:

  • 动态二进制在 “C:\windows_x86_64_llvm\stdx\dynamic\stdx”
  • 静态二进制在 “C:\windows_x86_64_llvm\stdx\static\stdx”

在代码工程的 cjpm.toml 文件中增加如下类似配置:

当前WIndows环境下cjpm.toml配置中,使用“path-option”配置文件路径时不支持带有空格和"."的路径,如“C:\Program Files (x86)”“D:\tools\.sdk”等。

[target.系统架构]                                                                # 系统架构和 OS 信息
  [target.系统架构-dependencies]
    path-option = ["C:\\windows_x86_64_llvm\\stdx\\dynamic\\stdx"]              # stdx 路径根据实际情况配置

系统架构表示代码编译所在机器的操作系统架构信息。该信息可以通过执行 cjc -v 获得。开发者请根据实际情况配置。例如执行 cjc -v 的回显信息如下,则该配置为 x86_64-w64-mingw32

Cangjie Compiler: 0.60.5 (cjnative)
Target: x86_64-w64-mingw32

以下为完整的配置示例:

[target.x86_64-w64-mingw32]                                                     # 系统架构和 OS 信息
  [target.x86_64-w64-mingw32.bin-dependencies]
    path-option = ["C:\\windows_x86_64_llvm\\stdx\\dynamic\\stdx"]              # stdx 路径根据实际情况配置
  • x86_64-w64-mingw32.bin-dependencies:该配置中的 x86_64-w64-mingw32 请替换为实际的操作系统信息。
  • path-option:stdx 二进制所在路径,请根据实际路径和使用动态还是静态二进制修改。
    请添加图片描述

配置示例:假设开发环境为 Windows(架构为 x86_64),导入 stdx 的动态二进制,则 cjpm.toml 配置示例如下。

[package]
  cjc-version = "1.0.3"
  name = "cangjie"
  description = "nothing here"
  version = "1.0.0"
  target-dir = ""
  src-dir = ""
  output-type = "executable"
  compile-option = ""
  override-compile-option = ""
  link-option = ""
  package-configuration = {}

[dependencies]
  [dependencies.csv4cj]
  path = "./csv4cj"

[target.x86_64-w64-mingw32]
  [target.x86_64-w64-mingw32.bin-dependencies]
    path-option = ["C:\\windows_x86_64_llvm\\dynamic"]

四、项目中如何使用csv4cj:

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())
}

在执行 cjpm run命令时,发现windows会报错:

PS D:\SDK\cangjie> cjc -v
cjc : 无法将“cjc”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 1
+ cjc -v
+ ~~~
    + CategoryInfo          : ObjectNotFound: (cjc:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 
PS D:\SDK\cangjie>
 *  History restored 

Cangjie Compiler: 1.0.3 (cjnative)
Target: x86_64-w64-mingw32

请添加图片描述

这里的原因是没有设置环境变量的问题,需要设置CANGJIE的环境变量即可解决这个问题。

请添加图片描述

接下来再执行cjpm run命令,发现还是会报错,那我们就需要通过管理员的身份来执行这个命令,如下图:

请添加图片描述

PS D:\SDK\cangjie> cjpm run
Error: can not find path '/windows_x86_64_llvm/dynamic/stdx' which is listed in 'target.x86_64-w64-mingw32.bin-dependencies' field at 
D:\SDK\cangjie\csv4cj\cjpm.toml
Error: cjpm run failed
PS D:\SDK\cangjie> cjpm run
Error: can not find path '/windows_x86_64_llvm/dynamic/stdx' which is listed in 'target.x86_64-w64-mingw32.bin-dependencies' field at 
D:\SDK\cangjie\csv4cj\cjpm.toml
Error: cjpm run failed
PS D:\SDK\cangjie>

最后,我们再执行cjpm run命令,就发现问题解决了,输出相关的信息如下:

请添加图片描述


约束与限制:

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

- **仓颉语言版本**: 1.0.3+

- **使用场景**: 鸿蒙应用开发

- windows;运行测试成功

在这里插入图片描述

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐