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