Apakah ada bidang yang menyimpan pemisah bidang FS yang digunakan saat dalam ekspresi reguler, setara dengan RT untuk RS?

Jan 04 2021

Dalam Pemisahan Rekaman 4.1.2 GNU Awk dengangawk kita dapat membaca:

Ketika RSsatu karakter, RTberisi karakter tunggal yang sama. Namun, bila RSmerupakan ekspresi reguler, RTberisi teks masukan sebenarnya yang cocok dengan ekspresi reguler.

Variabel RTini sangat berguna dalam beberapa kasus .

Demikian pula, kita dapat menyetel ekspresi reguler sebagai pemisah bidang. Misalnya, di sini kami mengizinkannya menjadi ";" atau "|":

$ 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

Namun, jika kami ingin mengemas data lagi, kami tidak memiliki cara untuk mengetahui pemisah mana yang muncul di antara dua bidang. Jadi jika dalam contoh sebelumnya saya ingin mengulang melalui bidang dan mencetaknya bersama lagi dengan menggunakan FS, ini mencetak seluruh ekspresi dalam setiap kasus:

$ 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

Apakah ada cara untuk "mengemas kembali" bidang menggunakan pemisah bidang tertentu yang digunakan untuk memisahkan masing-masing, serupa dengan yang dapat dilakukan RT?

(contoh yang diberikan dalam pertanyaan agak sederhana, tetapi hanya untuk menunjukkan maksudnya)

Jawaban

8 anubhava Jan 04 2021 at 16:34

Apakah ada cara untuk "mengemas kembali" bidang menggunakan pemisah bidang tertentu yang digunakan untuk memisahkan masing-masing

Penggunaan gnu-awk split()yang memiliki parameter ke-4 ekstra untuk pembatas yang cocok menggunakan regex yang disediakan:

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

Versi yang lebih mudah dibaca:

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"

Catat sepsparameter ke-4 splityang menyimpan larik teks yang cocok dengan ekspresi reguler yang digunakan dalam parameter ke-3 yaitu /[;|]/.

Tentu saja tidak sesingkat & sesederhana itu RS, ORSdan RT, yang bisa dituliskan sebagai:

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

Sebagai @anubhava menyebutkan , melongo memiliki split()(dan patsplit()yang untuk FPATsebagai split()adalah FS- lihathttps://www.gnu.org/software/gawk/manual/gawk.html#String-Functions) untuk melakukan apa yang Anda inginkan. Jika Anda menginginkan fungsionalitas yang sama dengan POSIX awk maka:

$ 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)
    }
}

Perhatikan penanganan khusus di atas dari kasus di mana pemisah bidang " "karena itu berarti 2 hal berbeda dari semua nilai pemisah bidang lainnya:

  1. Bidang sebenarnya dipisahkan oleh rantai spasi putih, dan
  2. Ruang kosong utama harus diabaikan ketika mengisi $ 1 (atau flds [1] dalam kasus ini) dan sehingga ruang putih, jika ada, harus diambil dalam seps [0] `untuk tujuan kita karena setiap seps [N] dikaitkan dengan flds [N] yang mendahuluinya.

Misalnya, menjalankan di atas pada 3 file masukan ini:

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

==> file2 <==
hello how are_you

==> file3 <==
    hello how are_you

kita akan mendapatkan output berikut di mana setiap bidang ditampilkan sebagai nomor bidang lalu nilai bidang di dalamnya [...]kemudian pemisah di dalamnya <...>, semua di dalamnya {...}(perhatikan bahwa seps[0]IFF diisi dengan FS " "dan catatan dimulai dengan spasi):

$ 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

Opsi alternatif untuk memisahkan adalah menggunakan kecocokan untuk menemukan pemisah bidang dan membacanya menjadi sebuah larik:

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"