เรียงไฟล์ข้อความตามความยาวบรรทัดรวมถึงช่องว่าง


137

ฉันมีไฟล์ CSV ที่มีลักษณะเช่นนี้

AS2345, ASDF1232, ตัวอย่างนายธรรมดา, 110 Binary ave., แอตแลนติส, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232 นางตัวอย่างธรรมดา 1121110 Ternary st. 110 Binary ave .. , Atlantis, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, ตัวอย่างนายธรรมดา, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, ตัวอย่างนายธรรมดา, 110 Ternary ave., บางเมือง, RI, 12345, (999) 123-5555,1.56

ฉันต้องการจัดเรียงตามความยาวบรรทัดรวมถึงช่องว่าง คำสั่งต่อไปนี้ไม่รวมช่องว่างมีวิธีการแก้ไขเพื่อให้มันทำงานได้หรือไม่

cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'

21
ฉันต้องการอาศัยอยู่ใน Binary Avenue หรือ Ternary Street คนเหล่านั้นแน่นอนจะเห็นด้วยกับสิ่งต่าง ๆ เช่น "8192 เป็นจำนวนรอบ"
schnaader

คำตอบ:


224

ตอบ

cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-

หรือหากต้องการเรียงลำดับย่อยแบบดั้งเดิมของคุณ (อาจไม่ได้ตั้งใจ) ของบรรทัดที่มีความยาวเท่ากัน:

cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-

ในทั้งสองกรณีเราได้แก้ไขปัญหาที่ระบุไว้ของคุณโดยการย้ายออกจาก awk สำหรับการตัดครั้งสุดท้ายของคุณ

เส้นของความยาวที่ตรงกัน - สิ่งที่ต้องทำในกรณีที่เสมอกัน:

คำถามไม่ได้ระบุว่าต้องการการเรียงลำดับเพิ่มเติมหรือไม่สำหรับบรรทัดที่มีความยาวตรงกัน ฉันคิดว่านี่เป็นสิ่งที่ไม่ต้องการและแนะนำให้ใช้-s( --stable) เพื่อป้องกันบรรทัดดังกล่าวเรียงต่อกันและเก็บไว้ในลำดับที่สัมพันธ์ซึ่งเกิดขึ้นในอินพุต

(ผู้ที่ต้องการควบคุมการเรียงลำดับความสัมพันธ์เหล่านี้อาจดู--keyตัวเลือกของการเรียงลำดับ)

เหตุใดโซลูชันที่พยายามทำจึงล้มเหลว (การสร้างสายใหม่ awk):

มันเป็นเรื่องที่น่าสนใจที่จะสังเกตเห็นความแตกต่างระหว่าง:

echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'

พวกเขาให้ผลผลิตตามลำดับ

hello   awk   world
hello awk world

ส่วนที่เกี่ยวข้องของคู่มือ (gawk's)กล่าวถึงเฉพาะส่วนที่ awk จะสร้างทั้งหมด $ 0 (ขึ้นอยู่กับตัวคั่น ฯลฯ ) เมื่อคุณเปลี่ยนหนึ่งฟิลด์ ฉันคิดว่ามันไม่ใช่พฤติกรรมที่บ้าคลั่ง มันมีสิ่งนี้:

"ในที่สุดก็มีบางครั้งที่สะดวกในการบังคับให้ awk สร้างเร็กคอร์ดทั้งหมดใหม่โดยใช้ค่าปัจจุบันของฟิลด์และ OFS เมื่อต้องการทำเช่นนี้ให้ใช้การมอบหมายที่ดูไม่น่ากลัว:"

 $1 = $1   # force record to be reconstituted
 print $0  # or whatever else with $0

"สิ่งนี้บังคับให้ต้องสร้างเร็กคอร์ดใหม่"

ทดสอบอินพุตรวมถึงบางบรรทัดที่มีความยาวเท่ากัน:

aa A line   with     MORE    spaces
bb The very longest line in the file
ccb
9   dd equal len.  Orig pos = 1
500 dd equal len.  Orig pos = 2
ccz
cca
ee A line with  some       spaces
1   dd equal len.  Orig pos = 3
ff
5   dd equal len.  Orig pos = 4
g

1
heemayl ใช่มันเป็นขอบคุณ ฉันพยายามจับคู่รูปร่างของ OP ที่เป็นไปได้เพื่อให้เขาสามารถมุ่งเน้นเฉพาะความแตกต่างที่สำคัญระหว่างเขาและของฉัน
neillb

1
มันก็คุ้มค่าที่จะชี้ให้เห็นว่าcat $@มันหักเช่นกัน คุณต้องการอ้างอย่างแน่นอนเช่นcat "$@"
tripleee

27

แก้ปัญหา AWK จาก neillbดีมากถ้าคุณอยากจะใช้awkและจะอธิบายว่าทำไมมันยุ่งยากมี แต่ถ้าสิ่งที่คุณต้องการคือการได้รับงานทำอย่างรวดเร็วและไม่สนใจในสิ่งที่คุณทำมันในหนึ่งแก้ปัญหาคือการใช้งานsort()ฟังก์ชันของ Perl พร้อมคาสั่งการที่กำหนดเองเพื่อวนซ้ำบรรทัดอินพุต นี่คือหนึ่งซับ:

perl -e 'print sort { length($a) <=> length($b) } <>'

คุณสามารถใส่สิ่งนี้ลงในไพพ์ไลน์ของคุณได้ทุกที่ที่คุณต้องการไม่ว่าจะเป็นการรับ STDIN (จากcatหรือการเปลี่ยนเส้นทางของเชลล์) หรือเพียงแค่ให้ชื่อไฟล์เป็น perl เป็นอาร์กิวเมนต์อื่นและปล่อยให้มันเปิดไฟล์

ในกรณีของฉันฉันต้องการบรรทัดที่ยาวที่สุดก่อนดังนั้นฉันจึงสลับ$aและ$bเปรียบเทียบ


นี่เป็นวิธีแก้ปัญหาที่ดีกว่าเนื่องจาก awk ทำให้การเรียงลำดับที่ไม่คาดคิดเมื่อไฟล์อินพุตมีบรรทัดตัวเลขและตัวอักษรผสมตัวเลขที่นี่คำสั่ง oneline: $ cat testfile | perl -e 'การจัดเรียงการพิมพ์ {ความยาว ($ a) <=> ความยาว ($ b)} <>'
alemol

ด่วน! ทำไฟล์ 465,000 บรรทัด (หนึ่งคำต่อบรรทัด) ใน <1 วินาทีเมื่อผลลัพธ์เปลี่ยนเส้นทางไปยังไฟล์อื่น - ดังนั้น:cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
cssyphus

Windows พร้อม StrawberryPerl ทำงาน:type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
bryc


10

ผลการทดสอบเกณฑ์มาตรฐาน

ด้านล่างนี้เป็นผลของการวัดประสิทธิภาพในการแก้ไขปัญหาจากคำตอบอื่น ๆ สำหรับคำถามนี้

วิธีทดสอบ

  • 10 การทำงานแบบต่อเนื่องบนเครื่องที่เร็วค่าเฉลี่ย
  • Perl 5.24
  • awk 3.1.5 (เพ่งพิศ 4.1.0 ครั้งเร็วขึ้น 2%)
  • ไฟล์อินพุตคือ 550MB, monstrosity เส้น 6 ล้านเส้น (British National Corpus txt)

ผล

  1. perlทางออกของ Calebใช้เวลา 11.2 วินาที
  2. perlทางออกของฉันใช้เวลา 11.6 วินาที
  3. awkโซลูชันของ neillb # 1 ใช้เวลา 20 วินาที
  4. awkโซลูชันของ neillb # 2 ใช้เวลา 23 วินาที
  5. Anubhava ของawkการแก้ปัญหาเอา 24 วินาที
  6. awkทางออกของโจนาธานใช้เวลา 25 วินาที
  7. bashโซลูชันของ Fretzใช้เวลานานกว่าawkโซลูชั่น400 เท่า (ใช้กรณีทดสอบที่ถูกตัดทอนจำนวน 100,000 บรรทัด) มันใช้งานได้ดีใช้เวลาตลอดไป

พิเศษ perlตัวเลือก

นอกจากนี้ฉันได้เพิ่มอีกวิธี Perl:

perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

6

Pure Bash:

declare -a sorted

while read line; do
  if [ -z "${sorted[${#line}]}" ] ; then          # does line length already exist?
    sorted[${#line}]="$line"                      # element for new length
  else
    sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length
  fi
done < data.csv

for key in ${!sorted[*]}; do                      # iterate over existing indices
  echo -e "${sorted[$key]}"                       # echo lines with equal length
done

3

length()ฟังก์ชั่นไม่รวมถึงช่องว่าง ฉันจะทำการอัปเดตขั้นตอนเล็กน้อยของคุณ (รวมถึงการหลีกเลี่ยงUUOC )

awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'

sedคำสั่งโดยตรงเอาตัวเลขและลำไส้ใหญ่เพิ่มโดยawkคำสั่ง อีกทางหนึ่งคือไม่ให้ฟอร์แมตของคุณเป็นawk:

awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'

2

ฉันพบวิธีแก้ไขปัญหาเหล่านี้จะไม่ทำงานหากไฟล์ของคุณมีบรรทัดที่ขึ้นต้นด้วยตัวเลขเนื่องจากไฟล์เหล่านั้นจะถูกจัดเรียงเป็นตัวเลขพร้อมกับทุกบรรทัดที่นับ การแก้ปัญหาคือการให้(ทั่วไปเป็นตัวเลขเรียงลำดับ) ธงแทน(ตัวเลขเรียงลำดับ):sort-g-n

awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-

2
สวัสดีมาร์คัส ฉันไม่ได้สังเกตเนื้อหาของเส้น (เป็นตัวเลขหรือไม่) - ซึ่งตรงข้ามกับความยาวบรรทัด - ซึ่งมีผลต่อการเรียงลำดับยกเว้นในกรณีของบรรทัดที่มีความยาวที่ตรงกัน นี่คือสิ่งที่คุณหมายถึง? ในกรณีเช่นนี้ฉันไม่พบวิธีการเรียงลำดับการสลับจาก-nเป็นการแนะนำของคุณ-gเพื่อให้ได้รับการปรับปรุงใด ๆ ดังนั้นฉันจึงคาดว่าจะไม่ ตอนนี้ฉันได้พูดถึงวิธีการห้ามการเรียงลำดับย่อยของเส้นที่มีความยาวเท่ากัน (โดยใช้--stable) ไม่ว่าคุณจะหมายถึงหรือไม่ขอบคุณที่ให้ความสนใจกับฉัน! ฉันได้เพิ่มอินพุตที่พิจารณาแล้วเพื่อทดสอบด้วย
neillb

4
ไม่ให้ฉันอธิบายโดยทำลายมัน เพียงแค่awkส่วนหนึ่งจะสร้างรายการของบรรทัดนำหน้าด้วยความยาวบรรทัดและช่องว่าง ท่อsort -nจะทำงานตามที่คาดไว้ แต่ถ้ามีเส้นใดเส้นหนึ่งมีหมายเลขอยู่ที่จุดเริ่มต้นเส้นเหล่านั้นจะเริ่มต้นด้วยความยาว + ช่องว่าง + หมายเลข sort -nไม่สนใจพื้นที่นั้นและจะถือว่าเป็นหนึ่งหมายเลขตัดแบ่งจากความยาว + หมายเลข การใช้การ-gตั้งค่าสถานะจะหยุดที่ช่องว่างแรกแทนการเรียงลำดับที่ถูกต้อง ลองด้วยตัวคุณเองโดยสร้างไฟล์ที่มีบรรทัดนำหน้าด้วยตัวเลขแล้วรันคำสั่งทีละขั้นตอน
Markus Amalthea Magnuson

1
ฉันยังพบว่าsort -nไม่สนใจพื้นที่และสร้างการเรียงลำดับที่ไม่ถูกต้อง sort -gส่งออกลำดับที่ถูกต้อง
Robert Smith

ฉันไม่สามารถทำซ้ำปัญหาอธิบายที่มีใน-n เอกสารอธิบายเป็นที่มีประสิทธิภาพน้อยลงและอาจน้อยกว่าที่แม่นยำ (จะแปลงตัวเลขให้ลอย) ดังนั้นอาจจะไม่ได้ใช้มันถ้าคุณไม่จำเป็นต้อง sort (GNU coreutils) 8.21info-g
phils

เอกสารประกอบของ nb สำหรับ-n: "เรียงลำดับตัวเลขตัวเลขเริ่มต้นแต่ละบรรทัดและประกอบด้วยช่องว่างเสริมเครื่องหมาย '-' หรือไม่ก็ได้และตัวเลขศูนย์หรือมากกว่านั้นอาจคั่นด้วยตัวคั่นหลักพันตามด้วยตัวเลือกจุดทศนิยมและศูนย์หรือตัวเลขหลัก . หมายเลขที่ว่างจะถือเป็น '0' สถานที่ 'LC_NUMERIC' จะระบุอักขระทศนิยมและตัวคั่นหลักพันโดยค่าเริ่มต้นว่างเปล่าคือช่องว่างหรือแท็บ แต่ที่ 'LC_CTYPE' สามารถเปลี่ยนได้ "
phils


2

1) วิธีการแก้ปัญหา awk บริสุทธิ์ สมมติว่าความยาวบรรทัดนั้นไม่สามารถมากกว่า> 1024 ได้

ชื่อไฟล์แมว awk 'BEGIN {min = 1024; s = "";} {l = ความยาว ($ 0); ถ้า (l <min) {min = l; s = $ 0;}} END {print s} '

2) วิธีการแก้ปัญหาซับซับหนึ่งสมมติว่าทุกบรรทัดมีเพียง 1 คำ แต่สามารถทำใหม่ได้สำหรับกรณีใด ๆ ที่ทุกบรรทัดมีจำนวนคำเท่ากัน:

LINES = $ (ชื่อไฟล์แมว); สำหรับ k ใน $ LINES; ทำ printf "$ k"; echo $ k | wc-L; เสร็จสิ้น เรียงลำดับ -k2 | หัว -n 1 | ตัด -d "" -f1


1

นี่คือวิธีการเรียงลำดับหลายไบต์ที่เข้ากันได้ของความยาวบรรทัด มันต้องการ:

  1. wc -m มีให้สำหรับคุณ (macOS มีไว้)
  2. โลแคลปัจจุบันของคุณรองรับอักขระหลายไบต์เช่นโดยการตั้งค่า LC_ALL=UTF-8สถานที่ปัจจุบันของคุณรองรับอักขระหลายไบต์เช่นโดยการตั้งค่าคุณสามารถตั้งค่านี้ใน. bash_profile ของคุณหรือเพียงแค่เติมไว้ก่อนคำสั่งต่อไปนี้
  3. testfile มีการเข้ารหัสอักขระที่ตรงกับภาษาของคุณ (เช่น UTF-8)

นี่คือคำสั่งทั้งหมด:

cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-

การอธิบายทีละส่วน:

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l);←ทำสำเนาของแต่ละบรรทัดในตัวแปร awk lและ double-escapes ทุก'เส้นเพื่อให้บรรทัดสามารถถูกสะท้อนได้อย่างปลอดภัยในฐานะคำสั่งเชลล์ ( \047เป็นเครื่องหมายคำพูดเดี่ยวในเครื่องหมายฐานแปด)
  • cmd=sprintf("echo \047%s\047 | wc -m", l);wc -m←นี้เป็นคำสั่งที่เราจะดำเนินการซึ่งสะท้อนเส้นหลบหนีไป
  • cmd | getline c;c←รันคำสั่งและสำเนามูลค่านับจำนวนตัวอักษรที่ถูกส่งกลับเข้าไปในตัวแปร awk
  • close(cmd); ←ปิดไพพ์ไปยังคำสั่งเชลล์เพื่อหลีกเลี่ยงการกดขีด จำกัด ของระบบกับจำนวนไฟล์ที่เปิดในกระบวนการเดียว
  • sub(/ */, "", c);wc←จดจ้องพื้นที่สีขาวจากมูลค่านับจำนวนตัวอักษรที่ส่งกลับโดย
  • { print c, $0 } ←พิมพ์ค่าจำนวนตัวอักษรของช่องว่างช่องว่างและบรรทัดเดิม
  • | sort -ns←เรียงลำดับบรรทัด (ตามค่าการนับจำนวนอักขระที่สรุปไว้) เป็นตัวเลข ( -n) และคงลำดับการเรียงที่เสถียร ( -s)
  • | cut -d" " -f2- ←ลบค่าการนับจำนวนอักขระที่สรุปไว้

มันช้า (เพียง 160 บรรทัดต่อวินาทีใน Macbook Pro ที่เร็ว) เพราะมันจะต้องรันคำสั่งย่อยสำหรับแต่ละบรรทัด

หรือเพียงแค่ทำสิ่งนี้ด้วยตัวเองgawk(ตั้งแต่เวอร์ชัน 3.1.5, gawk รู้ได้หลายไบต์) ซึ่งจะเร็วขึ้นอย่างมีนัยสำคัญ มันเป็นปัญหาอย่างมากในการหลบหนีและการโควตสองครั้งเพื่อส่งผ่านเส้นผ่านคำสั่งเชลล์จาก awk ได้อย่างปลอดภัย แต่นี่เป็นวิธีเดียวที่ฉันจะพบว่าไม่จำเป็นต้องติดตั้งซอฟต์แวร์เพิ่มเติม (gawk ไม่พร้อมใช้งานบน MacOS)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.