Normal ifadede kullanılan, RS için RT'ye eşdeğer, tam alan ayırıcı FS'yi depolayan bir alan var mı?

Jan 04 2021

In GNU awk en 4.1.2 Kayıt Yarma ilegawk biz okuyabilir:

RSTek bir karakter olduğunda , RTaynı tek karakteri içerir. Bununla birlikte, RSnormal bir ifade olduğunda , normal ifadeyle RTeşleşen gerçek girdi metnini içerir.

Bu değişken bazı durumlardaRT çok kullanışlıdır .

Benzer şekilde, alan ayırıcı olarak bir normal ifade belirleyebiliriz. Örneğin, burada biz onun ";" olmasına izin veriyoruz. veya "|":

$ gawk -F';' '{print NF}' <<< "hello;how|are you" 2 # there are 2 fields, since ";" appears once $ gawk -F'[;|]' '{print NF}' <<< "hello;how|are you"
3  # there are 3 fields, since ";" appears once and "|" also once

Bununla birlikte, verileri tekrar paketlemek istersek, iki alan arasında hangi ayırıcının göründüğünü bilmenin bir yolu yoktur. Dolayısıyla, önceki örnekte alanlar arasında döngü yapmak ve kullanarak bunları tekrar birlikte yazdırmak istersem FS, her durumda tüm ifadeyi yazdırır:

$ gawk -F'[;|]' '{for (i=1;i<=NF;i++) printf ("%s%s", $i, FS)}' <<< "hello;how|are you"
hello[;|]how[;|]are you[;|]  # a literal "[;|]" shows in the place of FS

RT'nin yapmasına benzer şekilde, her birini bölmek için kullanılan belirli alan ayırıcısını kullanarak alanları "yeniden paketlemenin" bir yolu var mı?

(soruda verilen örnekler oldukça basittir, ancak sadece noktayı göstermek için)

Yanıtlar

8 anubhava Jan 04 2021 at 16:34

Alanları, her birini bölmek için kullanılan belirli alan ayırıcısını kullanarak "yeniden paketlemenin" bir yolu var mı?

Kullanımı gnu-awk split()o verilen regex kullanarak sınırlayıcı eşleşti için ekstra 4 parametresi vardır:

s="hello;how|are you"
awk 'split($0, flds, /[;|]/, seps) {for (i=1; i in seps; i++) printf "%s%s", flds[i], seps[i]; print flds[i]}' <<< "$s"

hello;how|are you

Daha okunaklı bir versiyon:

s="hello;how|are you"
awk 'split($0, flds, /[;|]/, seps) { for (i=1; i in seps; i++) printf "%s%s", flds[i], seps[i] print flds[i] }' <<< "$s"

3. parametrede, yani 3. parametrede kullanılan normal ifadeye göre eşleşen bir metin dizisi depolayan 4. sepsparametreyi not edin .split/[;|]/

Tabii ki Kısa ve olarak basit gibi olmadığı RS, ORSve RTşu şekilde yazılabilir:

awk -v RS='[;|]' '{ORS = RT} 1' <<< "$s"
5 EdMorton Jan 04 2021 at 22:41

Olarak @anubhava söz , Gawk sahiptir split()(ve patsplit()için olan FPATşekilde split()olduğunu FS- Bkzhttps://www.gnu.org/software/gawk/manual/gawk.html#String-Functions) istediğinizi yapmak için. Bir POSIX awk ile aynı işlevselliği istiyorsanız, o zaman:

$ cat tst.awk function getFldsSeps(str,flds,fs,seps, nf) { delete flds delete seps str = $0

    if ( fs == " " ) {
        fs = "[[:space:]]+"
        if ( match(str,"^"fs) ) {
            seps[0] = substr(str,RSTART,RLENGTH)
            str = substr(str,RSTART+RLENGTH)
        }
    }

    while ( match(str,fs) ) {
        flds[++nf] = substr(str,1,RSTART-1)
        seps[nf]   = substr(str,RSTART,RLENGTH)
        str = substr(str,RSTART+RLENGTH)
    }

    if ( str != "" ) {
        flds[++nf] = str
    }

    return nf
}

{
    print
    nf = getFldsSeps($0,flds,FS,seps)
    for (i=0; i<=nf; i++) {
        printf "{%d:[%s]<%s>}%s", i, flds[i], seps[i], (i<nf ? "" : ORS)
    }
}

Alan ayırıcısının, " "diğer tüm alan ayırıcı değerlerinden 2 şey farklı olduğu anlamına geldiği durumda yukarıdaki özel işleme dikkat edin :

  1. Alanlar aslında herhangi bir beyaz boşluğun zincirleriyle ayrılır ve
  2. Baştaki beyaz boşluk, $ 1 (veya bu durumda flds [1]) doldurulurken göz ardı edilmelidir ve böylece beyaz boşluk, varsa, her seps [N] ilişkilendirildiğinden, amaçlarımız için seps [0] `içinde yakalanmalıdır. önündeki flds [N] ile.

Örneğin, yukarıdakileri bu 3 girdi dosyasında çalıştırmak:

$ head file{1..3}
==> file1 <==
hello;how|are you

==> file2 <==
hello how are_you

==> file3 <==
    hello how are_you

Her alanın alan numarası olarak görüntülendiği aşağıdaki çıktıyı elde ederiz, ardından içindeki alan değeri ve [...]ardından içindeki ayırıcı <...>, hepsi içinde {...}( seps[0]IFF doldurulmuş olduğunu " "ve kaydın beyaz boşlukla başladığını unutmayın ):

$ awk -F'[,|]' -f tst.awk file1
hello;how|are you
{0:[]<>}{1:[hello;how]<|>}{2:[are you]<>}

$ awk -f tst.awk file2 hello how are_you {0:[]<>}{1:[hello]< >}{2:[how]< >}{3:[are_you]<>} $ awk -f tst.awk file3
    hello how are_you
{0:[]<    >}{1:[hello]< >}{2:[how]< >}{3:[are_you]<>}
3 RamanSailopal Jan 04 2021 at 16:51

Bölmek için alternatif bir seçenek, alan ayırıcılarını bulmak ve bunları bir dizide okumak için eşleştirmeyi kullanmaktır:

awk -F'[;|]' '{
    str=$0; # Set str to the line while (match(str,FS)) { # Loop through rach match of the field separator map[cnt+=1]=substr(str,RSTART,RLENGTH); # Create an array of the field separators str=substr(str,RSTART+RLENGTH) # Set str to the rest of the string after the match string } for (i=1;i<=NF;i++) { printf "%s%s",$i,map[i] # Loop through each record, printing it along with the field separator held in the array map.
    } 
    printf "\n" 
   }' <<< "hello;how|are you"