我有一个制表符分隔的文本文件,其中带有字符串的第 23 列中有 \n ,这导致它断到下一行。
我在 vi 中打开文本文件并启用空白字符,我可以看到 DESCR2 字段中的值有破坏字符串的空行。
该字符串包含在制表符分隔的字符内,我试图删除 \n 并将字符串连接到 ABC 123,同时仍在 1 field 中。
我已经尝试过tr -d '\n' < file.txt
,但这会使所有行变成 1 行。我只想从该列中删除 \n\n 。
我也尝试过sed 's/\n\n//' file.txt
但是没有效果。我可以在 vi 中搜索并替换 \n\n 但无法使用 sed 获得相同的结果。
例子:
\t"ABC\n
\n
123"\t
期望的输出:
\t"ABC 123"\t
答案1
您似乎有一个格式正确的 CSV 文件,该文件使用制表符作为字段分隔符。只要正确引用这些字段,就可以在字段中嵌入换行符,您显示的示例就是这样。任何 CSV 解析器在读取您的数据时都不会出现问题。
如果您想删除这些换行符,您可以使用 CSV 解析器,例如csvkit
。
我将处理一个如下所示的示例文件:
$ cat -t file.csv
col1^Icol2^Icol3
col1^I"ABC
123"^Icol3
col1^Icol2^Icol3
每个^I
都是制表符。第二行的第二个字段包含两个连续的换行符,我们希望安全地将它们替换为单个空格字符。
我正在使用csvjson
from csvkit
,它将 CSV 数据转换为 JSON 文档。这使得使用 修改数据变得稍微容易一些jq
,它也可以用于将数据转回 CSV 格式:
$ csvjson -t -H file.csv | jq -r '.[] | [ .[] | values |= gsub("\n\n";" ") ] | @csv'
"col1","col2","col3"
"col1","ABC 123","col3"
"col1","col2","col3"
此处使用的命令csvjson
将 CSV 文件的每一行转换为 JSON 对象。该-t
选项告诉工具输入使用制表符作为分隔符,并且-H
我们告诉它没有列标题。
JSON 对象被放入一个数组中并通过读取来jq
提取值(数据将分配给a
、b
、c
等键,因为原始 CSV 文件没有标题,或者至少没有问题中提到的标题)并应用简单的替换,使用gsub()
空格替换每对连续的换行符。
gsub()
显然,您可以更改上面使用的正则表达式,\n+
使其用单个空格字符替换任何连续的换行符。
然后,操作员@csv
接收一组数组形式的数据,这些数据被格式化为 CSV 输出。
csvformat
您是否想要将默认字段分隔符从逗号更改回制表符,使用其-T
(对于制表符分隔输出)和-H
(CSV 输入中没有标题)选项通过管道传输结果:
$ csvjson -t -H file.csv | jq -r '.[] | [ .[] | values |= gsub("\n\n";" ") ] | @csv' | csvformat -T -H
col1 col2 col3
col1 ABC 123 col3
col1 col2 col3
csvformat
会自动引用需要引用的字段。
该csvformat
工具也是csvkit
.
作为参考,由 创建的中间 JSON 文档csvjson
如下所示(由 进行美化jq
):
[
{
"a": "col1",
"b": "col2",
"c": "col3"
},
{
"a": "col1",
"b": "ABC\n\n123",
"c": "col3"
},
{
"a": "col1",
"b": "col2",
"c": "col3"
}
]
答案2
GoCSV 可以做到这一点。
将 TSV 转换为 CSV 并替换换行符
我从看起来像这样的 TSV 文件开始,尝试模拟您的数据:
+--------+--------+--------+--------+--------+
| Col21 | Col22 | DESCR2 | Col24 | Col25 |
+--------+--------+--------+--------+--------+
| data21 | data22 | ABC | data24 | data25 |
| | | | | |
| | | 123 | | |
+--------+--------+--------+--------+--------+
第一步是将 TSV 转换为 CSV,这是所有 GoCSV 命令所使用的格式。我还在末尾添加了一个新列,其中包含 DESC2 的值并替换了换行符。 -n是个姓名新专栏的-t是 SPRIG模板具有replace
我们需要的函数(.DESCR2 | replace
读起来像“将 DESCR2 列放入替换函数中”):
gocsv delim \
-i "\t" \
-o "," \
input.tsv |
gocsv add \
-n DESCR2_replaced \
-t '{{ .DESCR2 | replace "\n" " " }}' \
> replaced.csv
替换.csv
+--------+--------+--------+--------+--------+-----------------+
| Col21 | Col22 | DESCR2 | Col24 | Col25 | DESCR2_replaced |
+--------+--------+--------+--------+--------+-----------------+
| data21 | data22 | ABC | data24 | data25 | ABC 123 |
| | | | | | |
| | | 123 | | | |
+--------+--------+--------+--------+--------+-----------------+
换入新列并重命名为旧列
在 _replaced 列中对数据进行标准化后,我将“选择出”旧的 DESCR2 并“选择”新的 DESCR2_replaced 代替它;然后改名DESCR2_替换回 DESCR2。在我的示例中,我只有 6 列,因此-C 柱子索引与您的 23+ 列文件中的索引不同:
gocsv select \
-c 1-2,6,4-5 \
replaced.csv |
gocsv rename \
-c 3 \
-names DESCR2 \
> final.csv
最终.csv
+--------+--------+----------+--------+--------+
| Col21 | Col22 | DESCR2 | Col24 | Col25 |
+--------+--------+----------+--------+--------+
| data21 | data22 | ABC 123 | data24 | data25 |
+--------+--------+----------+--------+--------+
转换回 TSV
gocsv delim \
-i "," \
-o "\t" \
final.csv \
> final.tsv
一条大管道
gocsv delim \
-i "\t" \
-o "," \
input.tsv \
| gocsv add \
-n DESCR2_replaced \
-t '{{ .DESCR2 | replace "\n" " " }}' \
| gocsv select \
-c 1-2,6,4-5 \
| gocsv rename \
-c 3 \
-names DESCR2 \
| gocsv delim \
-i "," \
-o "\t" \
> final.tsv
答案3
您是否尝试过使用:sed ':a;N;$!ba;s/\\n\n/ /g' file.txt
?
我发现这答案详细说明了如何使用 sed 删除换行符,并在\\n
其前面添加了一个附加反斜杠来转义特殊字符。
答案4
一次简单的 sed 运行在其模式空间中的任何一次都只包含一行,这就是您的 sed 语法不起作用的原因。一个(类似,如果不重复)问题有一个答案,解释如何使用 sed 处理多行编辑这里。 TLDR 是它很糟糕并且语法很痛苦
同样, tr 也会失败,因为它在任何时候都只查看一行。
在我看来,处理多行的最简单方法是使用 perl:
perl -0777 -pe 's/\n\n/ /igs' file.txt
其中 -0777 告诉 perl 匹配整个文件,而 -pe 只是查找和替换
此版本将使用 -i 进行内联编辑
perl -0777 -pe 's/\n\n/ /igs' -i file.txt
编辑:如果您打算用显示的特殊字符进行替换,您可能需要在正则表达式中正确转义 \n