การสลับคอลัมน์ไม่ จำกัด จำนวน


12

ฉันมีไฟล์ที่มีคอลัมน์ ดูตัวอย่างด้านล่าง:

a b c ... z  
1 2 3 ... 26

ฉันต้องการสลับคอลัมน์ทั้งหมดที่ 1 กลายเป็นครั้งสุดท้ายที่สองกลายเป็นหนึ่งก่อนที่ผ่านมา ... ฯลฯ

z y x ... a  
26 25 24 ... 1

มีซับ ( awkหรือsed) ที่ทำสิ่งนี้หรือไม่?
ฉันรู้ว่าสามารถใช้งานได้awkเมื่อมีคอลัมน์เพียงไม่กี่คอลัมน์ แต่ฉันต้องการทำสิ่งนี้กับไฟล์ที่มีคอลัมน์หลายพันคอลัมน์

tacทำสิ่งนี้ได้อย่างสมบูรณ์แบบสำหรับบรรทัด
ฉันเดาว่าฉันกำลังมองหาคอลัมน์ที่เทียบเท่า

rev ไม่ได้ผลสำหรับฉันเพราะมันสลับเนื้อหาในคอลัมน์ด้วย


perl -lane 'print join " ", reverse @F'

คำตอบ:


15
awk '{for(i=NF;i>0;i--)printf "%s ",$i;print ""}' file

ฉันทำงานหนักเกินไปสำหรับงานง่ายๆ ง่ายกว่าเสมอดีกว่า +1
Birei

10

คุณสามารถทำได้ด้วยสคริปต์หลามขนาดเล็ก:

#!/usr/bin/env python

# Swaps order of columns in file, writes result to a file.
# usage: program.py input_file output_file

import sys, os

out = []

for line in open(sys.argv[1], 'r'):
    fields = line.split()
    rev = ' '.join(list(reversed(fields)))
    out.append(rev)

f = open(sys.argv[2], 'w')
f.write(os.linesep.join(out))

7

หากคุณไม่สนใจ Python หนึ่งบรรทัดนี้จะกลับคำสั่งของคอลัมน์ที่คั่นด้วยช่องว่างในทุกบรรทัด:

paddy$ cat infile.txt 
a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
a e i o u
paddy$ python3 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))'
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
paddy$ 

การทำงานข้างต้นกับ python2.7 เช่นกัน:

paddy$ python2.7 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))'
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
paddy$ 

วิธีนี้เป็นวิธีที่เร็วที่สุดของผู้ใช้ทั้งหมดที่ฉันได้ทดสอบ
Peter.O

4

awkวิธีการหนึ่งที่ใช้

เนื้อหาของinfile:

a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
a e i o u

เรียกใช้awkคำสั่งต่อไปนี้:

awk '{
    ## Variable 'i' will be incremented from first field, variable 'j'
    ## will be decremented from last field. And their values will be exchanged.
    ## The loop will end when both values cross themselves.
    j = NF; 
    for ( i = 1; i <= NF; i++ ) { 
        if ( j - i < 1 ) { 
            break;
        } 
        temp = $j; 
        $j = $i; 
        $i = temp; 
        j--; 
    }
    print;
}' infile

ด้วยผลลัพธ์ต่อไปนี้:

l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a

3

ช้า แต่มีคุณลักษณะการแลกหนึ่งอย่าง มันจะรักษาความกว้างของตัวคั่นสนามเมื่อพวกเขากว้างกว่าตัวเดียว FWIW: หากคุณเรียกใช้สคริปต์นี้สองครั้งผลลัพธ์จะเหมือนกับต้นฉบับ

นี่คือสคริปต์

awk '{ eix = length($0) 
       for( fn=NF; fn>0; fn--) { dix=eix
            while( substr($0,dix,1) ~ /[ \t]/ ) dix--
            printf "%s%s", substr($0,dix+1,eix-dix), $fn
            dix-=length($fn); eix=dix }
       print substr($0,1,dix)
    }' "$file"

นี่คือการเปรียบเทียบเวลา ไฟล์ทดสอบมี 1 บรรทัด

                      fields           fields     
                      10,0000          10,000,000

user11136 {python} | real  0.029s     real  3.235s
reversible? no     | user  0.032s     user  2.008s
                   | sys   0.000s     sys   1.228s

jmp {python}       | real  0.078s     real  5.045s
reversible? no     | user  0.068s     user  4.268s
                   | sys   0.012s     sys   0.560s

rush {awk}         | real  0.120s     real  10.889s
reversible? no     | user  0.116s     user   8.641s
                   | sys   0.008s     sys    2.252s

petero {awk}       | real  0.319s     real  35.750s
reversible? yes    | user  0.304s     user  33.090s
                   | sys   0.016s     sys    2.660s

3

คุณสามารถใช้tacคุณเพียงแค่ต้องแปลงสัญญาณอินพุตก่อนและหลัง สิ่งนี้สามารถทำได้ด้วยเครื่องคำนวณสเปรดชีตscและเพื่อนสนิทของมันpsc:

< infile psc -S -r | sc -W% - | tac | psc -S -r | sc -W% - > outfile

เท่าที่เห็นนี่

วิธีนี้จะทำงานได้ดีที่สุดเมื่อมีการเติมคอลัมน์ทั้งหมด

INFILE

 a b c d e f g h i  j  k  l
 1 2 3 4 5 6 7 8 9 10 11 12
 A B C D E F G H I  J  K  L

OUTFILE

  l  k  j i h g f e d c b a
 12 11 10 9 8 7 6 5 4 3 2 1
  L  K  J I H G F E D C B A

แก้ไข

ตามที่บันทึกไว้โดยPeterO scมีขีด จำกัด สูงสุด 702 คอลัมน์ดังนั้นขนาดสูงสุดที่วิธีนี้รองรับ


1
มันแปลงตัวเลขเป็นคะแนนลอยตัว (สำหรับฉัน) เช่น ->1 1.00นอกจากนี้ฉันได้รับข้อผิดพลาดสำหรับบรรทัดที่มีมากกว่า 702 ฟิลด์ ดูเหมือนว่าจะเกี่ยวข้องกับขีด จำกัด ตัวเลขที่32768 ... แต่มันค่อนข้างเร็ว
Peter.O

ฉันไม่เห็นการแปลงจุดลอย แต่เพิ่ม-Sกับpscคำสั่งควรเข้าใจทุกอย่างเป็นสตริง สำหรับขีด จำกัด ของคอลัมน์ 702 นั้นเป็นขีด จำกัด ที่ยากเนื่องจากสนับสนุนเฉพาะคอลัมน์ A ถึง ZZ (26 + 26 * 26) ฉันจะเพิ่มความคิดเห็นเกี่ยวกับเรื่องนั้น
Thor

1
ที่จริงแล้วประเด็นจุดลอยตัวก็โอเคฉันมองไปไกลกว่านั้นและฉันค้นพบว่าฉันไม่ควรตรวจสอบผลลัพธ์ขณะที่ฉันกำลังรีบออกไปที่ประตู .. จุดลอยตัวเกิดขึ้นหลังจากที่มันถึงขีด จำกัด702 เท่านั้น ... เร็วกว่าคำตอบของหลามสำหรับฟิลด์1บรรทัดของ 702 แต่สำหรับ 100 บรรทัดมันจะกลายเป็นวิธีที่ช้าที่สุดของวิธีการที่กำหนดทั้งหมด :( .. มันต้องมีเวลาเริ่มต้นที่สั้นกว่างูหลาม
Peter.O

3

ไปป์ไลน์นี้เร็วกว่าคำตอบอื่น ๆ ที่เร็วที่สุดโดยปัจจัยสำคัญ (ดูผลลัพธ์) มันใช้และtr tacจำเป็นต้องใช้ 2 ASCII ไบต์ (\ x00- \ x7F) ซึ่งไม่มีอยู่ในข้อมูลของคุณ

\x00โดยทั่วไปแล้วเป็นตัวเลือกที่ดีตามที่เป็นอยู่\x01แต่คุณสามารถใช้ ASCII ไบต์ใดก็ได้ที่ไม่ได้อยู่ในข้อมูล

ในตัวอย่างนี้ SPACE และ TAB เป็นอักขระตัวคั่น ตัวคั่นสามารถเป็นแบบหลายไบต์หรือเดี่ยว ตัวคั่นเอาต์พุตเป็นพื้นที่เดียว

นี่คือคำสั่ง ชื่อไฟล์จะแสดงnumberof fields_xnumber of lines

 <"$file" tr ' \t\n' '\0\0\1' |tr -s '\0' '\n' |tac |tr '\n' ' ' |tr '\1' '\n'

หากคุณต้องการ / จำเป็นต้องตรวจสอบไบต์ที่ไม่ได้ใช้งานคุณสามารถตรวจสอบล่วงหน้าด้วยawkสคริปต์ตัวเลือกนี้ เวลาโดยรวมแม้เมื่อเรียกใช้สคริปต์ตัวเลือกนี้ยังคงเร็วกว่า metod อื่น ๆ (จนถึงตอนนี้ :) .. นี่คือสคริปต์การประมวลผลล่วงหน้า

o=($(<"$file" char-ascii-not-in-stream)); x="${o[0]}"; y="${o[1]}"
<"$file" tr ' \t\n' "$x$x$y" |tr -s "$x" '\n' |tac |tr '\n' ' ' | tr '$y' '\n' >"$file".$user

นี่คือสคริปต์ awk: char-ascii-not-in-stream

#!/usr/bin/awk -f
{c[$0]} END{for(i=0;i<=127;i++) {if(sprintf("%c", i) in c);else {printf "\\%03o ",i}}}

สำหรับชุดที่สองของเวลานี้รวมถึงchar-ascii-not-in-streamเวลาของสคริปต์ด้วย

Peter.O {tr,tac,tr} ==== file_10_x10000
real    0m0.013s    0m0.015s
user    0m0.020s    0m0.020s
sys     0m0.008s    0m0.012s   

user11136 {python} ===== file_10_x10000
real    0m0.057s
user    0m0.048s
sys     0m0.008s

jmp {python} =========== file_10_x10000
real    0m0.160s
user    0m0.160s
sys     0m0.000s

rush {awk} ============= file_10_x10000
real    0m0.121s
user    0m0.120s
sys     0m0.000s

##############################################

Peter.O {tr,tac,tr} ==== file_1000_x1000
real    0m0.048s    0m0.059s
user    0m0.040s    0m0.040s
sys     0m0.040s    0m0.048s

user11136 {python} ===== file_1000_x1000
real    0m0.158s
user    0m0.136s
sys     0m0.028s

jmp {python} =========== file_1000_x1000
real    0m0.327s
user    0m0.320s
sys     0m0.008s

rush {awk} ============= file_1000_x1000
real    0m0.832s
user    0m0.820s
sys     0m0s012s

##############################################

Peter.O {tr,tac,tr} ==== file_1000000_x50
real    0m5.221s    0m6.458s
user    0m4.208s    0m5.248s
sys     0m2.624s    0m2.396s

user11136 {python} ===== file_1000000_x50
real    0m16.286s
user    0m10.041s
sys     0m5.148s

jmp {python} =========== file_1000000_x50
real    0m22.845s
user    0m20.705s
sys     0m1.140s

rush {awk} ============= file_1000000_x50
real    0m44.793s
user    0m43.583s
sys     0m0.848s

##############################################

0

คุณสามารถทำได้โดยไม่ต้องพิมพ์f :

awk 'BEGIN{ORS=""} {for(k=NF;k>0;--k) {print $k; if (k==1) print "\n"; else print " "}} ' file
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.