การจัดการข้อความด้วย sed


12

ขณะนี้ฉันมีไฟล์ข้อความหลายไฟล์ที่มีเนื้อหาที่มีลักษณะเช่นนี้ (มีหลายบรรทัด):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

ฉันต้องการเปลี่ยนแต่ละบรรทัดเพื่อให้มีรูปแบบต่อไปนี้:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

มีวิธีการทำข้างต้นโดยใช้ sed หรือไม่ หรือฉันต้องหันไปใช้ Python?

คำตอบ:


22

คุณสามารถทำได้ด้วย sed, ใช่ แต่เครื่องมืออื่น ๆ นั้นง่ายกว่า ตัวอย่างเช่น:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

คำอธิบาย

awk จะแบ่งแต่ละบรรทัดของข้อมูลเกี่ยวกับช่องว่าง (ค่าเริ่มต้น) ประหยัดแต่ละสาขาเป็น$1, ,$2 $Nดังนั้น:

  • printf "%s ", $2; จะพิมพ์ฟิลด์ที่ 2 และพื้นที่ต่อท้าย
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: จะย้ำกว่าเขต 3 ถึงสนามสุดท้าย ( NFคือจำนวนของเขตข้อมูล) และสำหรับแต่ละของพวกเขาจะพิมพ์เขต 1 เป็นแล้วเขตข้อมูลปัจจุบันและ::1
  • print "" : แค่พิมพ์บรรทัดใหม่สุดท้ายขึ้นมา

หรือ Perl:

$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

คำอธิบาย

-aทำให้perlทำตัวเหมือนawkและแยกการป้อนข้อมูลของตนในช่องว่าง ที่นี่ฟิลด์จะถูกเก็บไว้ในอาร์เรย์@Fซึ่งหมายความว่าฟิลด์ที่ 1 จะเป็นฟิลด์$F[0]ที่ 2 $F[1]เป็นต้นดังนั้น:

  • print "$F[1] " : พิมพ์ฟิลด์ที่ 2
  • print "$F[0]:$_:1 " for @F[2..$#F];: วนซ้ำฟิลด์ 3 ไปยังฟิลด์สุดท้าย ( $#Fคือจำนวนองค์ประกอบในอาเร@Fย์ดังนั้น@F[2..$#F]จะใช้อาเรย์สไลซ์เริ่มต้นที่องค์ประกอบที่ 3 จนถึงจุดสิ้นสุดของอาเรย์) แล้วพิมพ์ฟิลด์ที่ 1 a :จากนั้นฟิลด์ปัจจุบันและ:1.
  • print "\n" : แค่พิมพ์บรรทัดใหม่สุดท้ายขึ้นมา

12

นี่คือ น่ากลัว sed ทาง!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

อ่านง่ายขึ้น:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

หมายเหตุ

  • -r ใช้ ERE
  • s/old/new/แทนที่oldด้วยnew
  • ^([0-9]+) บันทึกตัวเลขบางส่วนที่จุดเริ่มต้นของบรรทัด
  • \1 backreference เป็นรูปแบบการบันทึกแรก
  • :a ติดป้ายส่วนนี้ของสคริปต์ a
  • ( |$) ช่องว่างหรือจุดสิ้นสุดของบรรทัด
  • t ทดสอบว่าการเปลี่ยนครั้งล่าสุดสำเร็จหรือไม่ถ้าเป็นเช่นนั้นให้ทำคำสั่งถัดไป
  • aค้นหาฉลาก:aและทำมันอีกครั้ง
  • s/ $// ลบพื้นที่ต่อท้าย

ดังนั้นหลังจากเพิ่มโครงสร้างในส่วนแรกเราจะค้นหาอินสแตนซ์สุดท้ายของโครงสร้างซ้ำ ๆ และนำไปใช้กับหมายเลขถัดไป ...

แต่ฉันเห็นด้วยกับเครื่องมืออื่น ๆ ทำให้มันง่ายขึ้น ...


ฉันรอวิธีแก้ปัญหาของคุณ: D
Ravexina

: D เอาฉันในขณะที่ @Ravexina - ฉันคิดว่า muru สามารถทำให้คนที่ทำความสะอาดได้
Zanna

5

ด้วย awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

หรือทุบตี:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

เอาท์พุท:

0 565: 10: 1 565: 12: 1 565: 23: 1 565: 18: 1 565: 17: 1 565: 25: 1 
1 564: 7: 1 564: 12: 1 564: 13: 1 564: 16: 1 564: 18: 1 564: 40: 1 564: 29: 1 564: 15: 1 

5

คุณสามารถทำมันได้ใน sed แต่ python ก็ใช้งานได้เช่นกัน

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

เนื้อหาของการreformatfile.pyเป็นดังนี้:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

มันทำงานอย่างไร ไม่มีอะไรพิเศษเกิดขึ้นเป็นพิเศษ เราเปิดอาร์กิวเมนต์บรรทัดคำสั่งแรกเป็นไฟล์สำหรับอ่านและดำเนินการแยกแต่ละบรรทัดเป็น "คำ" หรือแต่ละรายการ คำแรกกลายเป็นprefตัวแปรและเราพิมพ์รายการ stdout ที่สอง (คำ [1]) รายการที่ลงท้ายด้วยช่องว่าง ต่อไปเราจะสร้างชุดใหม่ของ "คำว่า" ผ่านรายการ comprehensions และฟังก์ชั่นในรายการชั่วคราวของคำนำแต่ละคำและสตริง.join() "1"ขั้นตอนสุดท้ายคือการพิมพ์ออกมา


4

ด้วยawk:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

ทุกอย่างเกี่ยวกับการจัดรูปแบบเขตข้อมูลที่คั่นด้วยช่องว่างในรูปแบบที่ต้องการ:

  • printf("%s ", $2) พิมพ์ฟิลด์ที่สองด้วยช่องว่างต่อท้าย

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) วนซ้ำในฟิลด์ที่ 3 ถึงวินาทีสุดท้ายและพิมพ์ฟิลด์ในรูปแบบที่ต้องการ (ฟิลด์แรก, จากนั้นโคลอน, จากนั้นฟิลด์ปัจจุบัน, จากนั้นโคลอนสุดท้าย 1) ที่มีช่องว่างต่อท้าย

  • printf("%s:%s:1\n", $1, $NF) พิมพ์ฟิลด์สุดท้ายด้วยการขึ้นบรรทัดใหม่

ตัวอย่าง:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.