CSV 格式说明和应用

问题

我们常常将多个字符串item使用逗号拼接成一个字符串,用来表示数组,使用时再用逗号切割成为数组。比如安卓机型列表:

ALN-AL10,ALN-AL10,BRA-AL00,ALN-AL00/ALN-AL80

直到有一天,苹果设备也要用到这个机型列表,而它的每个机型都带着逗号,那我们使用逗号切割就得到了错误的数据。

iPhone15: iPhone15,4
iPhone15Plus: iPhone15,5
iPhone15Pro: iPhone16,1
iPhone15Pro_Max: iPhone16,2

为了解决这个问题,首先想到了换一个分隔符,比如 | ,再比如用一些不可见字符 : 0x01。
但我们不能保证这些字符串 item 一定不包含这些特殊字符,也许还有更好的方法。

既然是逗号分隔,首先想到的就是 CSV格式,毕竟 CSV 的全称就是Comma-Separated Values逗号分隔值。它是如何解决这个问题的?

CSV格式

CSV 的RFC说明文档:https://datatracker.ietf.org/doc/html/rfc4180

  1. 基本字段
  2. 包含逗号的字段,则使用双引号括起来;
  3. 包含双引号的字段,则在双引号前面必须加上另一个双引号进行转义。;
  4. 包含换行符的字段,则使用双引号括起来;
  5. 包含特殊字符的组合的字段,也是使用双引号括起来;
姓名,年龄,城市,备注
张三,30,北京,无备注
李四,25,上海,"喜欢, 打篮球"
王五,28,"广州, 广东",""
"李, 六",35,"""特别"" 市","这是一段
跨行的备注"
"陈, 七","40","深圳",

"包含""双引号""和,逗号"

使用 csv 的工具包是可以非常方便的处理这种数据。类似的后台表格导出 csv 文件也应当使用该csv工具包。

example:

Go 语言可以使用 https://pkg.go.dev/encoding/csv

func main() {
    in := `姓名,年龄,城市,备注
张三,30,北京,无备注
李四,25,上海,"喜欢, 打篮球"
王五,28,"广州, 广东",""
"李, 六",35,"""特别"" 市","这是一段
跨行的备注"
"陈, 七","40","深圳","包含""双引号""和,逗号"
`
    r := csv.NewReader(strings.NewReader(in))
    for {
        record, err := r.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(record)
    }
}

输出:

[姓名 年龄 城市 备注]
[张三 30 北京 无备注]
[李四 25 上海 喜欢, 打篮球]
[王五 28 广州, 广东 ]
[李, 六 35 "特别" 市 这是一段
跨行的备注]
[陈, 七 40 深圳 包含"双引号"和,逗号]
package utils

import (
	`bytes`
	`encoding/csv`
	`strings`
)

// SliceToCsvString 将字符串切片转换为CSV字符串
func SliceToCsvString(slice []string) (string, error) {
	var buf bytes.Buffer
	writer := csv.NewWriter(&buf)
	// 写入单行数据
	err := writer.Write(slice)
	if err != nil {
		return "", err
	}
	// 确保所有数据都被写入
	writer.Flush()
	// 检查是否有任何错误
	if err := writer.Error(); err != nil {
		return "", err
	}

	return buf.String(), nil
}

// CsvStringToSlice 将CSV字符串转换为字符串切片
func CsvStringToSlice(csvString string) ([]string, error) {
	reader := csv.NewReader(strings.NewReader(csvString))
	reader.ReuseRecord = true
	// 读取所有记录
	records, err := reader.Read()
	if err != nil {
		return nil, err
	}

	return records, nil
}

Read more

golang proto api 校验国际化 protovalidate

golang proto api 校验国际化 protovalidate

众所周知,protobuf 原型文件扩展很多功能,比如生成 http 接口层代码,顺势就有了生成接口参数校验代码的需求。 早期可以使用https://github.com/bufbuild/protoc-gen-validate 来实现,通过生成特定的 go 代码的方式来实现校验。 github 中也提到目前趋于稳定,不会有更多新特性的支持,推荐大家使用新的版本 protovalidate,https://github.com/bufbuild/protovalidate 。该版本是protoc-gen-validate 的“精神继承者”。它不需要任何代码生成并支持自定义约束。 现在我们尝试新版本,并且增加国际化支持。 go get github.com/bufbuild/protovalidate-go import "github.com/bufbuild/protovalidate-go" syntax = "proto3"; package

By brian
git clone 复制一个整个仓库并推送到新地址

git clone 复制一个整个仓库并推送到新地址

要使用 git clone --bare 复制一个新的仓库并推送到远程仓库,可以按照以下步骤操作: 1. 克隆一个裸仓库 首先,使用 git clone --bare 命令克隆源仓库。假设源仓库的 URL 是 https://github.com/user/source-repo.git,你可以执行以下命令: bash复制 git clone --bare https://github.com/user/source-repo.git 这将创建一个新的裸仓库(没有工作区),通常会创建一个名为 source-repo.git 的目录。 2. 进入裸仓库目录 进入刚刚克隆的裸仓库目录: bash复制 cd source-repo.git 3. 添加新的远程仓库 接下来,

By brian
搜索引擎技巧不用多,学会 3 个加速 100% 找到目标

搜索引擎技巧不用多,学会 3 个加速 100% 找到目标

在搜索时使用英文关键词,提高结果质量。尽量使用 google.com 以下搜索引擎技巧在 google.com 进行测试,效果都很好,前三个非常常用且强烈推荐。 使用精确搜索 * 建议: 使用双引号 "" 搜索完全匹配的短语,避免无关结果。 * 示例: * "Java NullPointerException" fix * 场景: 找到错误信息的精确解决方案。 利用站内搜索 * 建议: 使用 site: 限制搜索范围到特定网站。 * 示例: * site:stackoverflow.com "TypeError: undefined is not a function" * 场景: 搜索 Stack Overflow、官方文档或技术博客的特定内容。 一些关键词 * 建议:

By brian
沪ICP备2022013452号-1