用 0 填充空格/制表符分隔的空列

Posted

技术标签:

【中文标题】用 0 填充空格/制表符分隔的空列【英文标题】:Filling space/tab separated, empty columns with 0 【发布时间】:2011-06-07 02:07:05 【问题描述】:

我有一个巨大的文件,作为输出,一些列没有值,我需要用 0 填充这些列以进行进一步分析。我可以用空格或制表符分隔列,现在可以看到下面用制表符分隔。

【问题讨论】:

只是一个问题,您检查过编辑器中的最后一个空白列吗?列数据(不是分隔符)是实际的 \t 吗? 【参考方案1】:

这确实是 CSV 解析器的工作,但如果它必须是正则表达式,并且您在引用的 CSV 条目中从来没有标签,您可以搜索

(^|\t)(?=\t|$)

替换为

$10

所以,在 Perl 中:

(ResultString = $subject) =~ 
s/(    # Match either...
   ^   # the start of the line (preferably)
   |   # or
   \t  # a tab character
  )    # remember the match in backreference no. 1
  (?=  # Then assert that the next character is either
   \t  # a(nother) tab character
   |   # or
   $   # the end of the line
  )    # End of lookahead assertion
/$10/xg;

这将改变

1   2       4           7   8
    2   3       5   6   7   

进入

1   2   0   4   0   0   7   8   
0   2   3   0   5   6   7   0

【讨论】:

“...如果它必须是正则表达式...” - 原始海报从未提到使用正则表达式? @mfontani:哎呀。当然... - @plusplus:是的,他做到了,这是他使用的第一个标签。 @Tim Pietzcker, perl -p -e "s/(^|\t)(?=\t|$)/$10/xg" 文件,似乎不起作用, 任何想法?零只是放在列成员旁边 对不起,我不懂 Perl(我从 RegexBuddy 得到了代码 sn-p,它把我的正则表达式翻译成 Perl 代码),所以我不知道哪里出了问题。它确实希望文件是制表符分隔的。您可以在问题中发布文件的摘录(而不是屏幕截图)吗?一两行就够了。 @Tim Pietzcker, perl -p -e "s/(^|\t)(?=\t|$)/$1\t0/g" 工作正常。【参考方案2】:

对于制表符分隔的文件,这个 AWK sn-p 可以解决问题:

BEGIN  FS = "\t"; OFS="\t" 

    for(i = 1; i <= NF; i++) 
         if(!$i)  $i = 0 
    
    print $0

【讨论】:

如果空列位于行首或行尾,这是否也有效,即。 e.不在两个制表符之间? 是的。我已经在一个小的(3 列)文件上对其进行了测试,该文件包含一个完全填充的行,一个在中间缺少值,一个在开头缺少值,一个在末尾缺少值,它处理所有四行正确。 $0 是多余的:print 就足够了。 确实如此。但是当详细程度的差异如此之小时,我更喜欢明确。【参考方案3】:

这是sed 解决方案。请注意,sed 的某些版本不喜欢\t

sed 's/^\t/0\t/;:a;s/\t\t/\t0\t/g;ta;s/\t$/\t0/' inputfile

sed -e 's/^\t/0\t/' -e ':a' -e 's/\t\t/\t0\t/g' -e 'ta' -e 's/\t$/\t0/' inputfile

解释:

s/^\t/0\t/    # insert a zero before a tab that begins a line
:a            # top of the loop
    s/\t\t/\t0\t/g    # insert a zero between a pair of tabs
ta            # if a substitution was made, branch to the top of the loop
s/\t$/\t0/    # insert a zero after a tab that ends a line

【讨论】:

澄清一下,如果您有类似a\t\t\tb 的情况,则需要一个循环。正则表达式匹配永远不会重叠。因此,如果循环不可用,a\t\t\tb 将更改为a\t0\t\tb 而不是a\t0\t0\tb【参考方案4】:

重新阅读原帖后删除我的答案。没有标签作为数据,只有分隔符。如果没有数据,将出现一个双分隔符来对齐列。 不可能是其他方式。因此,如果存在单个分隔符,它将分隔两个空字段。 "" = 1 个空字段,"\t" = 2 个空字段。我现在明白了。

Tim Pietzcker 一直都有正确的答案。为他 +1。 也可以写成s/ (?:^|(?&lt;=\t)) (?=\t|$) /0/xg;,但都是一样的。

【讨论】:

输出由工具给出,有制表符和逗号选项。这里的-i.txt有什么作用?我的文件列号也可以稍后根据输出更改 @berkay - c:\> perl -h 会给你所有的开关。 -i[extension] 是就地编辑,其中 [extension] 添加到您的输入文件名(此处为“d.txt”)作为输入文件(正在修改)的“备份”的文件名。您始终可以将其作为 Perl 程序运行并传入文件名和编号。在这种情况下,正则表达式将是s!(?:^|(?&lt;=\t))\t!$number\t!g。或者您可以将 1 个衬垫作为批处理参数。不知道你在做什么。 谢谢,但我遇到了正则表达式的问题。 s后面应该跟'/,s///这是windows风格吗? @berkay 在 unix 上你应该使用单引号。这是新的正则表达式,如果对您不起作用:perl -pe 's/(?:^|(?&lt;=\t))(?:(?=\t$)\t|(\t))/0$1/g' d.txt 现在它可以工作但也检查接受的答案,它更容易理解。谢谢sln。 +1【参考方案5】:

当且仅当您的数据仅包含数字并且您有明确定义的字段分隔符FS,您可以使用以下技巧:

awk 'BEGINFS=OFS="\t"for(i=1;i<=NF;++i) $i+=01' file

通过添加零,我们将字符串转换为数字。空字符串将被转换为数字零。您可以将字段分隔符定义为您喜欢的任何内容。

但是,这可能会有点慢,因为每次重新分配字段 $i 时,它都会重新解析 $0 并将其拆分为字段。

更快的方法是Dennis Williamson的解决方案

【讨论】:

以上是关于用 0 填充空格/制表符分隔的空列的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式的空值该如何写?

如何在不折叠空格的情况下在 bash 脚本中拆分制表符分隔的字符串?

awk指令

从制表符分隔的 xls 文件中读取数据

Python3编码规范

使用制表符和空格读入文件