我需要在一行中包含“THIS CONTAIN NEWLINE”(需要删除第 2 列和第 4 列中的换行符)。
"column1" "column2" "column3" "column4" " column5"
"DATA" "THIS "DATA" "THIS "DATA"
CONTAIN NEWLINE" CONTAIN NEWLINE"
预期输出:
"column1" "column2" "column3" "column4" " column5"
"DATA" "THIS CONTAIN NEWLINE" "DATA" "THIS CONTAIN NEWLINE" "DATA"
答案1
使此文本处理问题变得困难或不寻常的原因是必须单独处理列。
这有点像黑客,但它似乎可以完成工作:
#!/bin/sh
rm -f newfile
for column in 1 2 3 4 5; do
cut -f "$column" file |
perl -ne 'chomp;$nl = ((tr /"/"/ % 2 == 0) ? "\n" : " "); print $_, $nl' |
sed -e 's/[[:blank:]]*$//' -e '/^[[:blank:]]*$/d' |
{ if [ -f newfile ]; then
paste newfile -
else
cat
fi
} >newfile.tmp
mv newfile.tmp newfile
done
该脚本假设调用了输入文件file
,并将创建一个名为 的文件(并使用临时数据的newfile
文件名)。newfile.tmp
它还假设列是正确地用制表符分隔的。
它使用 从原始文件逐一提取制表符分隔的列cut
。每个单独的列都通过一个简短的 Perl 脚本传递:
chomp;
$nl = ( ( tr /"/"/ % 2 == 0 ) ? "\n" : " " );
print $_, $nl;
这会计算每行上双引号的数量,如果该行包含偶数个双引号,则将输出带有附加换行符的行。如果引号的数量是奇数,它将在行末尾附加一个空格字符(从而合并跨行的带引号的字符串)。这是一种很黑客的方式。
它将sed
进行一些清理,从行尾删除尾随空格并删除空行。
然后,我paste
将此新数据作为新的制表符分隔列newfile
(通过首先输出到newfile.tmp
该文件,然后重命名该文件)。cat
仅当第一列newfile
尚不存在时才运行。
对于给定的输入数据,假设列正确地以制表符分隔,这将生成以下制表符分隔的文件:
"column1" "column2" "column3" "column4" " column5"
"DATA" "THIS CONTAIN NEWLINE" "DATA" "THIS CONTAIN NEWLINE" "DATA"
将选项卡替换为管道符号(以显示列的开始和结束位置):
$ tr '\t' '|' <newfile
"column1"|"column2"|"column3"|"column4"|" column5"
"DATA"|"THIS CONTAIN NEWLINE"|"DATA"|"THIS CONTAIN NEWLINE"|"DATA"
答案2
co=$(awk '/^[[:space:]]/{print $0}' filename |perl -pne 's/"/\n/g' |sed -r -e 's/^\s+//g' -e '/^$/d'|awk '{if(!seen[$0]++)print }')
awk -v co="$co" 'NR==1{print }/DATA/{$2=$2" "co"\"";$4=$4" "co"\"";print $0}' filename
输出
"column1" "column2" "column3" "column4" " column5"
"DATA" "THIS CONTAIN NEWLINE" "DATA" "THIS CONTAIN NEWLINE" "DATA"
答案3
使用乐(以前称为 Perl_6)
~$ raku -e 'my @a = [Z] lines.map(*.split: "\t"); \
@a.=map: *.join(" "); @a.=map: *.comb(/\" .+? \"/); \
$_.join("\t").put for [Z] @a;' file
[注意:OP 提供的文件更像是groff
输出的表而不是tsv
文件。如果这是一个tsv
文件,则 row2/column2 中的内部\n
会将 row2 的第 3,4,5 列数据推送到下一行]。
该解决方案假设输入文件已被充分清理。表文本已被缩短("INTERNAL\nNEWLINE"
而不是"THIS\nCONTAIN NEWLINE"
),以便更好地可视化列(但是,代码仍然适用于具有内部空白的字段)。最后,第三行(溢出)——包含第 2 列和第 4 列的“延续”——插入了""
列占位符,如OP。这可以确保每列每行都有一个条目,即使它是空字符串。
简而言之,在每个 的第一个语句中line
,列位于选项卡split
上\t
,然后[Z]
压缩以转置行和列。结果分配给@a
数组。在第二条语句中,每行(以前的列)都被join
组合成一个字符串,从而在第 2 列和第 4 列中重新构成以前“损坏的”双引号数据。
在第三个语句中,每一行(以前的列)都是comb
ed,即损坏的大约双引号文本,每行返回 2 个元素。然后,这个 5 x 2 行/列数组再次[Z]
zip-reduced(即转置)为 2 x 5 行/列数组,列join
通过\t
选项卡编辑,然后 out put
。
示例输入(制表符分隔的列):
"column1" "column2" "column3" "column4" "column5"
"DATA1" "INTERNAL "DATA3" "INTERNAL "DATA5"
"" NEWLINE" "" NEWLINE" ""
示例输出(制表符分隔的列):
"column1" "column2" "column3" "column4" "column5"
"DATA1" "INTERNAL NEWLINE" "DATA3" "INTERNAL NEWLINE" "DATA5"
有两种简单的方法可以更好地可视化上面的输出列:第一种是像 @Kusalananda 那样连接|
管道字符。将最后一个替换join("\t")
为join("|")
会返回以下内容:
"column1"|"column2"|"column3"|"column4"|"column5"
"DATA1"|"INTERNAL NEWLINE"|"DATA3"|"INTERNAL NEWLINE"|"DATA5"
第二种(可能信息更丰富的方法是在输出上调用.raku
or .perl
,它给出类似“数据转储器”的返回(即查看 Raku 数据的内部表示)。因此,最后一个语句包含.raku.put for [Z] @a;
返回以下 (注意转义双引号):
("\"column1\"", "\"column2\"", "\"column3\"", "\"column4\"", "\"column5\"")
("\"DATA1\"", "\"INTERNAL NEWLINE\"", "\"DATA3\"", "\"INTERNAL NEWLINE\"", "\"DATA5\"")