Sed ile metin işleme


12

Şu anda, içeriği (birçok satır ile) gibi görünen birden fazla metin dosyaları var:

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

Her satırı aşağıdaki biçime sahip olacak şekilde değiştirmek istiyorum:

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 kullanarak yukarıdaki yapmanın herhangi bir yolu var mı? Yoksa Python'a başvurmam gerekir mi?

Yanıtlar:


22

Bunu sed ile yapabilirsiniz, evet, ancak diğer araçlar daha basittir. Örneğin:

$ 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 

açıklama

awk olarak her alanlar tasarrufu (varsayılan olarak) boşluk üzerinde girişin her satırı bölecek $1, $2, $N. Yani:

  • printf "%s ", $2; 2. alanı ve sondaki alanı yazdırır.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: alanlar 3 üzerinden son alana ( NFalan sayısıdır) yinelenir ve her biri için 1. alanı, a :, ardından geçerli alanı ve a'yı yazdırır :1.
  • print "" : bu sadece son bir satırsonu yazdırır.

Veya 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çıklama

-aMarkaları perlgibi davranır awkve boşluk üzerindeki girişini bölün. Burada, alanlar dizide saklanır @F, yani 1. alan $F[0], 2. $F[1]vb.

  • print "$F[1] " : 2. alanı yazdırın.
  • print "$F[0]:$_:1 " for @F[2..$#F];: alan 3'ü son alana yineleyin ( $#Fdizideki öğelerin sayısıdır @F, bu nedenle @F[2..$#F]dizinin sonuna kadar 3. öğeden başlayarak bir dizi dilimi alır) ve 1. alanı, a :, ardından geçerli alanı ve bir :1.
  • print "\n" : bu sadece son bir satırsonu yazdırır.

12

Burada bir korkunç sed yol!

$ 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

Daha okunaklı:

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/ $//'

notlar

  • -r ERE kullan
  • s/old/new/yerine oldsahipnew
  • ^([0-9]+) satırın başında bazı numaraları kaydet
  • \1 ilk kaydedilen desene geri başvuru
  • :a betiğin bu bölümünü etiketle a
  • ( |$) boşluk veya satır sonu
  • t son değiştirmenin başarılı olup olmadığını test edin - eğer öyleyse, sonraki komutu yapın
  • aetiketi bul :ave tekrar yap
  • s/ $// arka boşluğu kaldır

Yapıyı ilk kısma ekledikten sonra, yapının son örneğini tekrar tekrar bulup bir sonraki sayıya uyguluyoruz ...

Ama diğer araçların bunu kolaylaştırdığını kabul ediyorum ...


Sed çözümünü bekliyordum: D
Ravexina

: D beni bir süre aldı @Ravexina - Ben muru daha temiz bir yapabiliriz sanırım
Zanna

5

Awk ile:

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

veya bash ile:

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

Çıktı:

0565: 10: 1556: 12: 1556: 23: 1556: 18: 1556: 17: 1556: 25: 1 
1 564: 7: 1 564: 12: 15004: 13: 15004: 16: 1564: 18: 15004: 40: 1574: 29: 15004: 15: 1 

5

Peki, sed'de yapabilirsiniz, ancak python da çalışır.

$ ./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

İçeriği şu reformatfile.pyşekildedir:

#!/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))

Bu nasıl çalışıyor? Gerçekten özel olan hiçbir şey yok. İlk komut satırı argümanını okumak için dosya olarak açıyoruz ve her satırı "kelimeler" e veya tek tek öğelere ayırmaya devam ediyoruz. İlk kelimeler prefdeğişkendir ve boşlukla biten stdout ikinci (kelimeler [1]) öğeye yazdırırız. Daha sonra .join()geçici bir pref, her kelime ve dize listesi üzerinde liste kavrayışı ve işlevi ile yeni bir "kelime" seti oluşturuyoruz "1". Son adım, bunları yazdırmak


4

İle 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

Her şey boşlukla ayrılmış alanları istenen formatta formatlamakla ilgilidir:

  • printf("%s ", $2) ikinci alanı bir boşlukla yazdırır

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) 3. ila ikinci son alanları yineler ve alanları istenen biçimde (ilk alan, sonra iki nokta üst üste, sonra geçerli alan, sonra iki nokta üst üste, 1) sondaki boşlukla yazdırır

  • printf("%s:%s:1\n", $1, $NF) son alanı yeni satırla yazdırır

Misal:

% 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
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.