使用AWK去除CSV列引号并转换列类型生成TSV的技术求助
解决AWK转换CSV到TSV时的引号与列类型问题
嗨,刚接触Ubuntu和AWK两天就能尝试写脚本转换格式,已经很棒了!咱们一步步来解决你的问题:
首先,修复你的语法错误
你之前写的FS="","";是明显的语法错误——AWK里设置字段分隔符FS只能是一个字符串,正确的做法是用合适的规则匹配带引号的CSV字段,而不是把两个字符串放一起。
核心思路:逐个字段处理类型与格式
你的需求是把特定列转成整数、日期,字符串列保留引号,空值转null,这需要我们遍历每个字段,根据列的类型做针对性处理。下面是一个能满足你需求的完整AWK脚本:
BEGIN { # 用FPAT正确解析带引号的CSV字段(包括字段内含逗号的情况) FPAT = "([^,]+)|(\"[^\"]+\")" OFS = "\t" # 输出用制表符分隔 # 定义需要转为整数的列(列号从1开始) int_cols[5] = 1 # id1 int_cols[6] = 1 # id2 int_cols[7] = 1 # id3 int_cols[4] = 1 # browser_version int_cols[9] = 1 # true or false column # 定义日期列 date_col = 1 # 定义需要保留引号的字符串列 str_cols[2] = 1 # country str_cols[3] = 1 # browser str_cols[8] = 1 # id4 } # 处理表头行 NR == 1 { for (i=1; i<=NF; i++) { # 先去掉字段首尾的引号 gsub(/^"|"$/, "", $i) # 给字符串列和日期列的表头加上引号(匹配你期望的格式) if (str_cols[i] || i == date_col) { $i = "\"" $i "\"" } } print next } # 处理数据行 { for (i=1; i<=NF; i++) { # 去掉当前字段的首尾引号 gsub(/^"|"$/, "", $i) # 处理空值:将空字段转为"null" if ($i == "") { $i = "null" } # 处理整数列:如果是纯数字,直接保留(不带引号) else if (int_cols[i] && $i ~ /^[0-9]+$/) { continue # 无需修改,直接用数字格式 } # 处理日期列:直接保留原始格式(不带引号) else if (i == date_col) { continue } # 处理字符串列:给字段加上引号 else if (str_cols[i]) { $i = "\"" $i "\"" } } # 打印处理后的行 print }
脚本解释
- FPAT的作用:
FPAT = "([^,]+)|(\"[^\"]+\")"是AWK中专门用来解析CSV的规则,它能正确识别两种字段:要么是不含逗号的普通字段,要么是被双引号包裹(即使内部有逗号)的字段,避免了简单用FS=","分割时的错误。 - 列类型定义:通过数组
int_cols、str_cols标记不同列的类型,后续遍历字段时就能针对性处理。 - 空值处理:把空字段统一转为
null,符合你示例中的格式。 - 表头与数据行分开处理:表头需要保留引号格式,数据行则按类型转换。
运行脚本的命令
把上面的脚本保存为csv2tsv.awk,然后运行:
awk -f csv2tsv.awk file.csv > file.tsv
关于你的尝试方向
你的思路是对的:从简单的分隔符替换,进阶到处理字段格式和类型。AWK本身没有像数据库那样的"列类型定义",但通过遍历字段、结合正则表达式和条件判断,完全可以实现类似的效果。
如果后续遇到更复杂的CSV(比如字段内有换行、转义引号),可能需要借助专门的CSV处理工具(比如csvkit),但目前这个脚本完全能满足你的需求~
内容的提问来源于stack exchange,提问作者El_Birdo
相关产品推荐
相关产品推荐

