使用竖线分隔的文件中存在空条目;将其转换为制表符分隔的文件,在空条目之间添加“<empty>”。

4

问题

我收到了一个以管道符分隔的文本文件,其中包含每个文件的文件名和一些索引信息。我的目标是将其变成以制表符分隔的文件。 然而,我想知道空条目在哪里。例如,使用lorem||dolor将变为lorem'\t'<empty>'\t'dolor

让我再举几个例子,说明我收到了什么以及需要什么:

多行示例:(注意,每行上的条目数量相同。)

给定:

||dolor|sit
amet,||adipiscing|
sed|do|eiusmod|tempor

希望实现的目标:

<empty> '\t' <empty> '\t' dolor '\t' sit '\n'
amet, '\t' <empty> '\t' adipiscing '\t' <empty> '\n'
sed '\t' do '\t' eiusmod '\t' tempor '\n'

开头和结尾的空条目。

给定:

|ut|labore||dolore||

希望实现的目标:

<empty> '\t' ut '\t' labore '/t' <empty> '\t' dolore '\t' <empty> '\t' <empty>

问题在于连续的空条目。我收到的文件可以有从1到36个连续的管道符号(0到37个连续的空条目)。

澄清

解决方案不必是sedawkgreptr等。这些只是我查看的解决方案。 perlpython脚本(或任何我没有想到的其他想法)也将受到欢迎。

我的尝试和研究

对于我在研究期间尝试的内容,包括命令及其输出作为图像1和文本文件2,以避免问题过于混乱。

我的尝试图像

我的尝试文本

我查找的链接 -- 使用sed查找连续的管道符号(并替换任何这样的管道符号系列):参考此处;计算空字段的数量(可能有用,以了解需要多少个<empty>):参考此处;最长序列:参考此处

系统信息

$ uname -a
CYGWIN_NT-10.0 A-1052207 2.5.2(0.297/5/3) 2016-06-23 14:29 x86_64 Cygwin
$ bash --version
GNU bash, version 4.3.42(4)-release (x86_64-unknown-cygwin) ...
$

我正在Windows 10上运行此版本的Cygwin(因为工作需要)。

编辑1

我不太清楚具体需要什么。

以下是一个简短的示例,显示了我想要的管道开头和结尾:

(如果您输入第一行,按回车键,输入第二行,按回车键等,则会看到并需要键入此内容。无法复制/粘贴,因为>只会在您按下前一行的回车键后出现。)

$ cat > myfile.txt<<EOF
> ||foo|||bar||
> EOF

$ <**command-to-be-used**> myfile.txt | cat -A
<empty>^I<empty>^Ifoo^I<empty>^I<empty>^Ibar^I<empty>^I<empty>$

^I 表示的是我的 bash 版本中的 '\t'。从使用我提供的示例文本得到的答案中,我意识到我希望在 labore 后面加上一个 <empty>(请参见下面的命令)。请注意,收到的答案(感谢 @Neil_McGuigan 和 @Ed_Morton)确实在 labore 之后给出了一个 '\t',只是没有 <empty>。这是我的错误,因为我在最初的描述中没有表述清楚。我很抱歉。

通过对 @Neil_McGuigan 的命令进行微调,我成功地实现了我的目标。请注意,如果您想按照显示的方式 "逐行" 输入此命令,则需要在每行末尾包括一个空格和一个 \

$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | 
  awk '
       {
         $1=$1; n_empty=0; 
         for(i=1; i<=NF; i++) 
         { 
           if($i=="") {$i="<empty>"; n_empty++;}
         }; 
         print
       }
       END {print n_empty" entries are empty" | "cat 1>&2";}
      ' FS='|' OFS=$'\t'
   | cat -A

给出结果:

<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I<empty>$
9 entries are empty

再次说明,对于那些不想滚动的人,此输出如下:

<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I<empty>$ 有9个条目为空

(请注意,将空条目的计数写入stderr并不是必要的,但很好。)

对于我所想要的内容没有表述清楚,非常抱歉。


我成功使用的方法

感谢@Neil_McGuigan和@Ed_Morton,我能够得到我所寻找的解决方案。我的最终命令如下:

$ awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) {if($i=="") {$i="<empty>"; n_empty++;}}; print;} END {print n_empty" entries are empty" | "cat 1>&2";}' FS='|' OFS=$'\t' file_pipe-delim.txt > file_tab-delim.txt

$

如果您不想滚动,请查看相同的命令:

$ awk '{$1=$1; for(i=1; i<NF; i++){ if($(i)=="")$(i)="<empty>" }; print}'
  FS='|' OFS=$'\t' file_pipe-delim.txt | sed 's/\t$/\t<empty>/g' > 
  file_tab-delim.txt

$

以下是一个制作、转换和保存文件的示例:

(如果您输入第一行,按Enter键,输入第二行,按Enter键等,则会看到并需要输入以下内容。它不能被复制/粘贴,因为>符号只会在您在前一行按Enter键后出现。)

$ cat > file_pipe-delim.txt<<EOF
> ||dolor|sit
> amet,||adipiscing|
> sed|do|eiusmod|tempor
> |||
> |aliqua.|Ut|
> EOF

$ awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) 
{if($i=="") {$i="<empty>"; n_empty++;}}; print;} END 
{print n_empty" entries are empty" | "cat 1>&2";}' 
FS='|' OFS=$'\t' file_pipe-delim.txt > file_tab-delim.txt


$ cat -A file_tab-delim.txt
<empty>^I<empty>^Idolor^Isit$
amet,^I<empty>^Iadipiscing^I<empty>$
sed^Ido^Ieiusmod^Itempor$
<empty>^I<empty>^I<empty>^I<empty>$
<empty>^Ialiqua.^IUt^I<empty>$

$

最后,让我们返回那个让我头疼的字符串。我们可以按以下方式获得所需的输出:
$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) {if($i=="") {$i="<empty>"; n_empty++;}}; print;} END {print n_empty" entries are empty" | "cat 1>&2";}' FS='|' OFS=$'\t' | cat -A
<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I<empty>$
9 entries are empty

现在,我们执行同样的命令,但是不使用管道符号到 cat -A,这意味着我们将不会看到每个 '\t'^I;我们只会看到文本中的制表符。

$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | \ 
awk '{$1=$1; n_empty=0; for(i=1; i<=NF; i++) \
{if($i=="") {$i="<empty>"; n_empty++;}}; print;} END \
{print n_empty" entries are empty" | "cat 1>&2";}' \
FS='|' OFS=$'\t'

<empty> <empty> lorem   ipsum   <empty> sit     amet,   <empty> <empty> <empty>eiusmod  tempor  <empty> <empty> labore  <empty>
9 entries are empty

修复 a|||b 中的两个空字段的问题在于 s/||/|<empty>|/g' 或类似的正则表达式会使用前两个管道符号进行第一次匹配,因此当扫描继续时,第三个管道符号没有成对。您可以通过重复原始匹配来克服这个问题:sed -e 's/||/|<empty>|/g' -e 's/||/|<empty>|/g'。但是,当您也要更改分隔符时,就需要更加努力地工作,这就是问题所在。 - Jonathan Leffler
是的,我考虑过那个问题,这就是为什么我没有尝试那条路线的原因。看起来@Ed_Morton已经想出来了。 - bballdave025
2个回答

2
awk '
     {
       $1=$1; 
       for(i=1; i<NF; i++) { 
         if($i=="") { $i="<empty>"; empty++ }
       }; 
       print
     }
     END { print empty" empty" | "cat 1>&2"; }
' FS='|' OFS=$'\t'

这应该能解决问题。$1=$1告诉awk“重构”输入字段,以便它们可以与新的OutputFieldSeparator(OFS)一起使用。

print empty" empty" | "cat 1>&2"会将“n empty”打印到stderr。如果您愿意,可以省略它。


谢谢!它完美地解决了问题。它还解决了逗号的问题。我也很感激你添加的解释。我现在还不能给答案点赞(声望点数不够),但我已经打了勾。如果我能因为使用awk而给它+1,我会非常乐意这样做。$ echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | awk '{$1=$1; for(i=1; i<NF; i++){ if($(i)=="")$(i)="<empty>" }; print}' FS='|' OFS=$'\t' | cat -A<empty>^I<empty>^Ilorem^Iipsum^I<empty>^Isit^Iamet,^I<empty>^I<empty>^I<empty>^Ieiusmod^Itempor^I<empty>^I<empty>^Ilabore^I$ - bballdave025
我刚意识到有些事情我没有澄清。这个答案受到了它的影响。我不确定是否应该编辑我的问题,还是只在相关帖子下评论。基本问题是:我想在labore^I之后添加一个<empty>。对于我的表述不够清晰,我深感抱歉;我已经更新了我的问题。实际上,这与我可能遇到的数据情况有关;数据是在Windows机器上生成的。这意味着文件末尾不一定有换行符('\n')或任何其他字符。请参见Edit1。 - bballdave025

1
你只需要全局地进行两次 || 替换,无论该模式出现多少次,只要进行这样的替换即可:|<empty>|
$ sed 's/||/|<empty>|/g; s/||/|<empty>|/g; s/|/\t/g' file
lorem   ipsum   <empty> sit     amet,   <empty> <empty> <empty> eiusmod tempor <empty>  <empty> labore

或者如果您更喜欢awk:
$ awk '{while(gsub(/\|\|/,"|<empty>|")); gsub(/\|/,"\t")} 1' file
lorem   ipsum   <empty> sit     amet,   <empty> <empty> <empty> eiusmod tempor <empty>  <empty> labore

对于某些sed命令,您可能需要使用'$'\t''而不是只用\t


1
我喜欢这种方法。它有助于在标准的UNIX类型安装中包含这些带有命令的想法,例如 sedawk。您还回答了我自己关于在 || 上运行多个替换的问题。 - bballdave025
我无法理解Edit1的含义。有多个命令和输入,还有很多模糊的文本,例如在示例中从“<empty>”到“E”等等。我无法区分期望输出和实际输出等等。请花一点时间想出一个演示您遇到问题的样本输入文件,以及您得到的实际输出和所需的期望输出,然后编辑您的问题,以展示我们这个明确、简洁的问题示例。到目前为止,您一直在使用“<empty>”和“\t”,所以请继续使用它们。 - Ed Morton
抱歉,作为 Stack Overflow 的新手,我不确定如何分离输入和输出。输入在 outfile.txt 结束。 - bballdave025
1
感谢您对第一次在SO上发布帖子的耐心和建议。我很感激了解到这里通常发布事物的格式。至于outfile.txt,那是我犯的一个愚蠢的错误 - 使用以前关于如何使用cat创建文件的笔记中的内容。您完美地回答了我提出的问题,我非常感激。我也感谢您提供的帮助,让我能够更清晰地发布内容。 - bballdave025
1
我确实没有清楚地理解 |$ 变成了 |<empty> 这个操作,但这是期望的行为。非常感谢您指出来。我确实希望 ^| 变成 ^<empty> '\t'(我在2016年8月10日22:37:18Z的文章中错误陈述了我的担忧)。 - bballdave025
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接