Metin dosyasını boşluklar dahil satır uzunluğuna göre sıralama


138

Şöyle bir CSV dosyam var

AS2345, ASDF1232, Bay Plain Sample, 110 Binary ave., Atlantis, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, Bayan Plain Sample, 1121110 Ternary st. 110 İkili ave .., Atlantis, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, Bay Plain Sample, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555,1.56
AS2345, ASDF1232, Bay Plain Sample, 110 Ternary ave., Some City, RI, 12345, (999) 123-5555,1.56

Boşlukları içeren satır uzunluğuna göre sıralamam gerekiyor. Aşağıdaki komut boşluk içermez, benim için çalışacak şekilde değiştirmenin bir yolu var mı?

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

21
Gerçekten bu insanlar kesinlikle gibi şeyler kabul edeceğini, İkili Avenue veya Üçlü Street yaşamak istiyorum "8192 olan yuvarlak bir sayı"
schnaader

Yanıtlar:


225

Cevap

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

Veya, herhangi bir eşit uzunlukta çizginin orijinal (belki de kasıtsız) alt sıralamasını yapmak için:

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

Her iki durumda da, belirtilen kesintinizi son kesintiniz için awk'den uzaklaşarak çözdük.

Eşleşen uzunluk çizgileri - kravat durumunda ne yapmalı:

Soru, eşleşen uzunluktaki satırlar için daha fazla sıralama istenip istenmediğini belirtmedi. Bunun istenmeyen olduğunu varsaydım ve bu tür çizgilerin birbirine göre sıralanmasını önlemek için -s( --stable) kullanımını önerdim ve girişte göründükleri sırayla saklayın.

(Bu bağları sıralama konusunda daha fazla kontrol isteyenler sıralama --keyseçeneğine bakabilirler .)

Sorunun girişim çözümü neden başarısız oluyor (awk hat yeniden oluşturma):

Arasındaki farkı not etmek ilginçtir:

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

Sırasıyla verim verirler

hello   awk   world
hello awk world

İlgili bölümü (gawk en) manuel yalnızca bir alan değiştirdiğinizde awk (vb ayırıcı dayanarak) 0 $ tüm yeniden edeceğini bir kenara olarak bahseder. Sanırım bu çılgın bir davranış değil. Şuna sahiptir:

"Son olarak, alanların ve OFS'nin geçerli değerini kullanarak awk'yi tüm kaydı yeniden oluşturmaya zorlamanın uygun olduğu zamanlar vardır. Bunu yapmak için, görünüşte zararsız atamayı kullanın:"

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

Diyerek şöyle devam etti: "Bu, awk'ı kaydı yeniden oluşturmaya zorluyor."

Eşit uzunlukta bazı çizgiler içeren test girişi:

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, evet öyle, teşekkürler. Onunla benimki arasında sadece önemli farklılıklara odaklanabilmesi için OP'nin mümkün olan girişim çözümünün şeklini eşleştirmeye çalıştım.
neillb

1
cat $@Kırık olduğunu da belirtmeye değer . Kesinlikle alıntı yapmak istersiniz, örneğincat "$@"
tripleee

27

Neillb gelen AWK çözüm gerçekten kullanmak istiyorsanız büyük awkve bir güçlük yoktur neden açıklıyor, ama ne istediğinizi işi çabuk halletmek için olduğunu ve bunu ne umursamazsak, bir çözüm kullanımına olan Perl'in sort()giriş satırları üzerinde yineleme yapmak için özel bir kaparison rutini ile işlevi. İşte bir astar:

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

Bunu, STDIN ( catveya kabuk yönlendirmesinden) alan boru hattınıza ihtiyacınız olan her yere koyabilir veya dosya adını perl'e başka bir argüman olarak verebilir ve dosyayı açmasına izin verebilirsiniz.

Benim durumumda önce en uzun hatlara ihtiyacım vardı, bu yüzden takas ettim $ave $bkarşılaştırma yaptım .


Giriş dosyası sayısal ve alfanümerik satırlar içerdiğinde awk beklenmedik sıralamaya neden olduğu için bu daha iyi bir çözümdür Burada oneline komutu: $ cat testfile | perl -e 'baskı sıralaması {uzunluk ($ a) <=> uzunluk ($ b)} <>'
alemol

Hızlı! Çıktı başka bir dosyaya yönlendirildiğinde 465.000 satır dosyası (satır başına bir kelime) <1 saniye içinde mi - bu nedenle:cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
cssyphus

StrawberryPerl ile Windows çalışır:type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
bryc

14

Bunun yerine şu komutu deneyin:

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

10

Deney sonuçları

Aşağıda, bu soruya verilen diğer yanıtların çözümlerine ilişkin bir karşılaştırmanın sonuçları verilmiştir.

Test metodu

  • Hızlı bir makinede ortalama 10 sıralı çalışma
  • Perl 5.24
  • awk 3.1.5 (gawk 4.1.0 kez ~% 2 daha hızlıydı)
  • Giriş dosyası 550 MB, 6 milyon satırlık bir canavarlıktır (British National Corpus txt)

Sonuçlar

  1. Caleb'in perlçözümü 11.2 saniye sürdü
  2. Benim perlçözüm 11.6 saniye sürdü
  3. neillb'inawk 1. çözümü 20 saniye sürdü
  4. neillb'inawk # 2 numaralı çözümü 23 saniye sürdü
  5. anubhava'nın awkçözümü 24 saniye sürdü
  6. Jonathan'ın awkçözümü 25 saniye sürdü
  7. Fretz'in bashçözümü , çözümlerden 400 kat daha uzun sürer awk(100.000 satırlık kesik bir test çantası kullanarak). İyi çalışıyor, sadece sonsuza dek sürüyor.

Ekstra perlseçenek

Ayrıca başka bir Perl çözümü ekledim:

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

6

Saf 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()Fonksiyon boşluk içermez. Boru hattınızda küçük değişiklikler yapardım ( UUOC'dan kaçınmak da dahil ).

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

sedKomutu doğrudan tarafından eklenen basamak ve kolon kaldırır awkkomutu. Alternatif olarak, biçimlendirmenizi şunlardan uzak tutun awk:

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

2

Sayılan tüm sayılarla birlikte sayısal olarak sıralanacağından, dosyanız bir sayı ile başlayan satırlar içeriyorsa bu çözümlerin işe yaramayacağını buldum. Çözelti vermek yerine bayrağı (genel sayısal-tür) (sayısal-sıralama):sort-g-n

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

2
Merhaba Markus. Satır içeriğini (sayısal veya değil) - satır uzunluğunun aksine - eşleşen uzunluklara sahip satırlar dışında sıralama üzerinde herhangi bir etkisi olduğunu gözlemlemiyorum. Demek istediğin bu mu? Bu gibi durumlarda, sıralama yöntemlerini herhangi bir iyileştirme -nsağlamak için önerdiğinizden anahtarlamaya bulamadım -g, bu yüzden beklemiyorum. Şimdi, cevabımda, eşit uzunlukta çizgilerin alt sıralamasının (kullanılarak --stable) nasıl yasaklanacağını ele aldım . Demek istediğin buydu ya da olmadı, dikkatime sunduğun için teşekkürler! Ayrıca test etmek için düşünülmüş bir girdi ekledim.
neillb

4
Hayır, açıklayayım. Sadece awkparça, satır uzunluğu ve boşluk ile öneklenmiş satırların bir listesini oluşturur. Boru tesisatı sort -nbeklendiği gibi çalışacaktır. Ancak bu satırlardan herhangi birinin başında bir sayı varsa, bu satırlar uzunluk + boşluk + sayı ile başlar. sort -nbu alanı göz ardı eder ve uzunluğa + sayıdan birleştirilmiş bir sayı olarak ele alır. -gBunun yerine bayrağı kullanmak ilk boşlukta duracak ve doğru bir sıralama sağlayacaktır. Sayı ön ekli satırlar içeren bir dosya oluşturarak kendiniz deneyin ve komutu adım adım çalıştırın.
Markus Amalthea Magnuson

1
Ayrıca sort -nalanı göz ardı ettiğini ve yanlış bir sıralama ürettiğini buldum . sort -gdoğru sırayı verir.
Robert Smith

Birlikte açıklanan sorunu yeniden edemez -niçinde sort (GNU coreutils) 8.21. infoDokümantasyon açıklar -gsen gerekmez eğer öyleyse muhtemelen bunu kullanmıyorum, daha az verimli ve (o yüzen numaraları dönüştürür) potansiyel olarak daha az hassas olarak.
phils

nb belgeleri için -n: "Sayısal olarak sırala. Sayı her satırdan başlar ve isteğe bağlı boşluklar, isteğe bağlı bir '-' işareti ve muhtemelen bin ayırıcıyla ayrılmış sıfır veya daha fazla basamaktan oluşur, isteğe bağlı olarak ondalık nokta karakteri ve sıfır veya daha fazla basamaktan oluşur Boş bir sayı '0' olarak kabul edilir. 'LC_NUMERIC' yerel ayarı ondalık nokta karakterini ve binlerce ayırıcıyı belirtir. Varsayılan olarak boşluk bir boşluk veya sekmedir, ancak 'LC_CTYPE' yerel ayarı bunu değiştirebilir. "
phils

2

POSIX Awk ile:

{
  c = length
  m[c] = m[c] ? m[c] RS $0 : $0
} END {
  for (c in m) print m[c]
}

Misal


2

1) saf awk çözeltisi. Diyelim ki satır uzunluğu> 1024'ten fazla olamaz

kedi dosya adı | awk 'BEGIN {dk = 1024; s = "";} {l = uzunluk (0 $); eğer (l <dk) {dk = l; s = 0 $;}} END {yazdırın s} '

2) tüm satırların sadece 1 kelimesi olduğunu varsayarak bir satır bash çözümü, ancak tüm satırların aynı sayıda kelimeye sahip olduğu her durumda yeniden çalışabilir:

LINES = $ (kedi dosya adı); k için $ LINES; printf "$ k" yapın; echo $ k | wc-L; bitti | sırala -k2 | kafa -n 1 | kes -d "" -f1


1

Çizgileri uzunluğa göre sıralamak için çok baytlı uyumlu bir yöntem. Gerektirir:

  1. wc -m sizin için kullanılabilir (macOS'de var).
  2. Geçerli yerel ayarınız, örneğin ayarlayarak çok baytlı karakterleri destekler LC_ALL=UTF-8. Bunu .bash_profile dosyanızda veya yalnızca aşağıdaki komuttan önce ekleyerek ayarlayabilirsiniz.
  3. testfile yerel ayarınızla eşleşen bir karakter kodlaması var (ör. UTF-8).

İşte tam komut:

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-

Kısmen açıklama:

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l);← awk değişkenindeki her satırın bir kopyasını oluşturur ve satırın bir kabuk komutu olarak güvenli bir şekilde yankılanması için sekiz satırdan lkaçar '( \047sekizli gösterimde tek bir alıntıdır).
  • cmd=sprintf("echo \047%s\047 | wc -m", l);← Bu, kaçacağımız satırı yansıtan yürüteceğimiz komuttur wc -m.
  • cmd | getline c;← komutu yürütür ve awk değişkenine döndürülen karakter sayısı değerini kopyalar c.
  • close(cmd); ← bir işlemde açık dosya sayısı üzerinde bir sistem sınırına çarpmamak için boruyu shell komutuna kapatın.
  • sub(/ */, "", c);←, döndürülen karakter sayısı değerinden beyaz boşluk keser wc.
  • { print c, $0 } ← satırın karakter sayısı değerini, bir boşluk ve orijinal satırı yazdırır.
  • | sort -ns← satırları (ekli karakter sayısı değerlerine göre) sayısal olarak ( -n) sıralar ve kararlı sıralama düzenini ( -s) korur .
  • | cut -d" " -f2- ←, önceden eklenen karakter sayısı değerlerini kaldırır.

Her satır için bir alt komut yürütmesi gerektiğinden yavaştır (hızlı bir Macbook Pro'da saniyede yalnızca 160 satır).

Alternatif olarak, bunu sadece gawk(3.1.5 sürümünden itibaren, gawk çok baytlı olarak bilinir) ile yapın, ki bu daha hızlı olacaktır. Awk'den bir kabuk komutuyla satırları güvenli bir şekilde geçirmek için tüm kaçan ve çift tırnak yapmak çok sorun, ancak bu ek yazılım yüklemeyi gerektirmeyen bulabildiğim tek yöntem (gawk varsayılan olarak mevcut değil Mac os işletim sistemi).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.