如何删除 tsv 文件中双引号列内的换行符?

如何删除 tsv 文件中双引号列内的换行符?

我需要在一行中包含“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 列中重新构成以前“损坏的”双引号数据。

在第三个语句中,每一行(以前的列)都是combed,即损坏的大约双引号文本,每行返回 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"

第二种(可能信息更丰富的方法是在输出上调用.rakuor .perl,它给出类似“数据转储器”的返回(即查看 Raku 数据的内部表示)。因此,最后一个语句包含.raku.put for [Z] @a;返回以下 (注意转义双引号):

("\"column1\"", "\"column2\"", "\"column3\"", "\"column4\"", "\"column5\"")
("\"DATA1\"", "\"INTERNAL NEWLINE\"", "\"DATA3\"", "\"INTERNAL NEWLINE\"", "\"DATA5\"")

https://raku.org

相关内容