【问题标题】:Pipe-delimited file with empty entries; convert to tab-delimited with '<empty>' between带有空条目的管道分隔文件;转换为以“<empty>”分隔的制表符
【发布时间】:2016-12-17 05:27:18
【问题描述】:

问题

我收到了一个以竖线分隔的文本文件,其中包含每个文件的文件名和一些索引信息。我的目标是使它成为一个制表符分隔的文件。 但是,我想知道空条目在哪里。这将完成,例如lorem||dolor 变成 lorem '\t' &lt;empty&gt; '\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 以便不要让问题过于混乱。

My Attempts image

My Attempts text

指向我查找的内容的链接 -- 使用 sed 查找连续管道(并替换任何此类管道系列):参考。 here ;计算空字段的数量(可能有助于了解需要多少 &lt;empty&gt;):参考。 here ;最长序列:ref here ;

系统信息

$ 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

我不清楚到底想要什么。

这是一个简短的示例,展示了我希望在开头和结尾使用管道的效果:

(如果您输入第一行,按 Enter,输入第二行,按 Enter 等,您将看到并需要输入。它不能被复制/粘贴,因为 @ 987654347@ 仅在您在上一行按回车后显示。)

$ 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' 的方式。从使用我给出的一些示例文本给出的答案中,我意识到我希望在最后一个 &lt;empty&gt; 之后,在 labore 之后(参见下面的命令)。请注意,收到的答案(感谢@Neil_McGuigan 和@Ed_Morton)在labore 之后给出'\t',而不是&lt;empty&gt;。这是我的错,因为我在最初的描述中不够清楚。我很抱歉。

我能够通过对@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 entries are empty

(请注意,写入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 等,您将看到并需要输入。它不能被复制/粘贴,因为 @ 987654364@ 仅在您在上一行按回车后显示。)

$ 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 的管道,这意味着我们不会看到每个^I'\t';我们只会看到“标签”的文本。

$ 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/||/|&lt;empty&gt;|/g' or something similar is that the first match uses both the first two pipes, so when the scan continues, the third pipe is not paired. You can overcome that by repeating the original match: sed -e 's/||/||/g' -e 's/||/| |/g'` .但是,当您也更改分隔符时,您必须更加努力,但这就是出现问题的原因。
  • 是的,我考虑过这个问题,这就是我没有尝试过那条路线的原因。看来@Ed_Morton 已经想通了。

标签: regex bash awk sed csv


【解决方案1】:
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&gt;&amp;2" 将“n empty”打印到标准错误。喜欢的可以省略

【讨论】:

  • 谢谢!它就像一个魅力。它还解决了逗号问题。我也很欣赏您添加的内容的解释。我还不能对答案进行投票(没有足够的声望点),但我给了它复选标记。如果有 +1 我可以给它使用awk,我很乐意这样做。 $echo "||lorem|ipsum||sit|amet,||||eiusmod|tempor|||labore|" | awk '{$1=$1; for(i=1; i&lt;NF; i++){ if($(i)=="")$(i)="&lt;empty&gt;" }; print}' FS='|' OFS=$'\t' | cat -A&lt;empty&gt;^I&lt;empty&gt;^Ilorem^Iipsum^I&lt;empty&gt;^Isit^Iamet,^I&lt;empty&gt;^I&lt;empty&gt;^I&lt;empty&gt;^Ieiusmod^Itempor^I&lt;empty&gt;^I&lt;empty&gt;^Ilabore^I$
  • 我刚刚意识到有些事情我没有澄清。这个答案受它的影响。我不确定是否应该编辑我的问题,或者只是评论相关帖子。基本问题是这样的:我想要一个&lt;empty&gt;,在labore^I 之后。我为我的表述不够清楚而道歉;我已经更新了我的问题。这实际上与我可能遇到的数据有关。数据是在 Windows 机器上生成的。这意味着文件末尾不一定有换行符 ('\n') 字符或任何其他字符。见编辑1。
【解决方案2】:

只要您每次全局执行,无论该模式出现多少次,您只需执行两次|| -> |&lt;empty&gt;| 替换:

$ 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

【讨论】:

  • 我喜欢这种方法。使用包含在标准 UNIX 类型安装中的命令(如 sedawk)来获得这些想法会有所帮助。您还回答了我问自己关于在 || 上运行多个替换的问题。谢谢
  • 我无法制作 Edit1 的头部或尾部。有多个命令和输入带有很多模棱两可的文本,我们在示例中从&lt;empty&gt;E 等,我无法从实际输出等中分辨出所需的输出等。请稍等一下使用 1 个示例输入文件演示您遇到的问题以及您获得的实际输出以及您想要获得的所需输出,然后编辑您的问题,以便向我们展示 1 个清晰、简洁的问题示例。到目前为止,您一直在使用 &lt;empty&gt;\t,所以请坚持下去。
  • 感谢您告诉我。我会尝试清理它。我想我已经看这个太久了。
  • 感谢您的建议和对首次发布 SO 海报的耐心。我很高兴了解这里通常发布的内容的格式。至于outfile.txt,这对我来说是一个愚蠢的错误——使用之前关于如何使用cat 创建文件的注释中的内容。您完美地回答了我提出的问题,对此我表示感谢。我也感谢您提供的帮助,以便我可以更清楚地发布内容。
  • 我绝对不清楚|$ 变成|&lt;empty&gt;。这是期望的行为。我很感谢你指出这一点。我确实希望 ^| 变成 ^&lt;empty&gt; '\t'(我在 2016-8-10 22:37:18Z 的帖子中错误地表达了我的担忧。)
猜你喜欢
  • 2016-07-19
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多