如何处理多列文本文件以获得另一个多列文本文件?


17

我有一个文本文件:

a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj

我该如何处理并获得2列文件,如下所示:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

或像这样的三列文件:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jj

我更喜欢awk解决方案,但也欢迎其他解决方案。

Answers:


1

您也可以通过单次调用GNU awk来做到这一点:

重塑

# Set awk to split input at whitespace characters and
# use tab as the output field separator 
BEGIN {
  RS="[ \t\n]+"
  OFS="\t"
}

# Print using OFS or ORS based on the element index
{
  printf "%s", $1 (NR%n == 0 ? ORS : OFS)
}

# Append a missing new-line when last row is not full
END { 
  if( NR%n != 0) 
    printf "\n"
}

像这样运行它:

awk -f reshape.awk n=2 infile

还是单线:

awk -v n=2 'BEGIN { RS="[ \t\n]+"; OFS="\t" } { printf "%s", $1 (NR%n == 0 ? ORS : OFS) } END { if( NR%n != 0) printf "\n" }' infile

输出:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

或搭配n=3

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

这不是$1用作格式字符串printf吗?
2016年

@Wildcard:是的,使用起来更安全"%s", ...。更新
2016年

感谢您的确认。:) awk顺便说一句,这同样适用于您对此问题的其他答案中的命令。
2016年

20

将每个字段放在一行上并进行后合并。

每个字段一行

tr

tr -s ' ' '\n' < infile

grep

grep -o '[[:alnum:]]*' infile

sed

sed 's/\s\+/\n/g' infile

或更便携:

sed 's/\s\+/\
/g' infile

awk

awk '$1=$1' OFS='\n' infile

要么

awk -v OFS='\n' '$1=$1' infile

专栏

对于2列:

... | paste - -

对于3列:

... | paste - - -

等等

sed

对于2列:

... | sed 'N; s/\n/\t/g'

对于3列:

... | sed 'N; N; s/\n/\t/g'

等等

xargs

... | xargs -n number-of-desired-columns

xargs用于/bin/echo打印时,请注意看起来像选项的数据echo将被解释为原样。

awk

... | awk '{ printf "%s", $0 (NR%n==0?ORS:OFS) }' n=number-of-desired-columns OFS='\t'

公关

... | pr -at -number-of-desired-columns

要么

... | pr -at -s$'\t' -number-of-desired-columns

列(来自autogen包)

... | columns -c number-of-desired-columns

典型输出:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

2
灌篮高手。+1先生
史蒂文·彭妮

应该不是xargs线路呼叫echoprintf
通配符

1
@Wildcard:默认情况下拨xargs打电话/bin/echo
Thor Thor

1
哇,我不知道!甚至由POSIX指定。谢谢!
2016年

@Wildcard:向其发送数据xargs看起来像/bin/echo引起问题的选项……我添加了警告。
2016年

9
$ sed -E 's/\s+/\n/g' ip.txt | paste - -
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

$ sed -E 's/\s+/\n/g' ip.txt | paste - - -
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj

9

正如Wildcard指出的那样,这只有在文件格式正确的情况下才有效,因为外壳程序不会将任何特殊字符解释为glob,并且您对默认的单词拆分规则感到满意。如果对您的文件是否将“通过”该测试存在任何疑问,请不要使用此方法。

一种可能性是printf

printf '%s\t%s\n' $(cat your_file)

这将对内容进行分词your_file,并将它们配对并在它们之间用制表符进行打印。您可以在中使用更多%s格式字符串printf以增加额外的列。


1
取决于不包含特殊字符的文件。例如,如果它带有任何星号(*),您将得到非常意外的结果。
通配符

4
perl -n0E 'say s/\s+/ ++$n % 4 ?"\t":"\n"/gre' file

(用列数替换4)


4

BSD rs(重塑)实用程序:

$ rs 0 2
a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj
[Ctrl-D][Enter]
a    aa
aaa  b
bb   bbb
c    cc
ccc  d
dd   ddd
e    ee
eee  f
ff   fff
g    gg
ggg  h
hh   hhh
i    ii
iii  j
jj   jjj

0 2。指定0意味着“从列自动计算行”。


3

Python脚本方法。

这里的基本思想是将文本中的所有单词展平到一个列表中,然后在第二个项目之后打印换行符(用于分成两列)。如果要3列,请更改index%2index%3

#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%2 == 0:
       print("\t".join(line))
       line = []

样本输出:

$ python recolumnate.py < input.txt                                            
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

三栏式版本(如上所述,仅index%3 == 0更改)

$ cat recolumnate.py                                                           
#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%3 == 0:
       print("\t".join(line))
       line = []

$ python recolumnate.py < input.txt                                            
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.