RSのRTと同等の、正規表現で使用される正確なフィールド区切り文字FSを格納するフィールドはありますか?

Jan 04 2021

ではGNU Awkのの4.1.2レコード分割してgawk、私たちは読むことができます:

RSが単一文字の場合RT、同じ単一文字が含まれます。ただし、RSが正規表現の場合、は正規表現にRT一致する実際の入力テキストを含みます。

この変数RTは、場合によっては非常に便利です。

同様に、フィールド区切り文字として正規表現を設定できます。たとえば、ここでは「;」のいずれかにすることができます。または "|":

$ 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

ただし、データを再度パックする場合、2つのフィールドの間にどの区切り文字が表示されたかを知る方法はありません。したがって、前の例でフィールドをループし、を使用してフィールドを再度一緒FSに出力する場合は、すべての場合に式全体が出力されます。

$ 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で実行できるのと同様に、各フィールドを分割するために使用される特定のフィールドセパレータを使用して、フィールドを「再パック」する方法はありますか?

(質問で与えられた例はかなり単純ですが、要点を示すためだけです)

回答

8 anubhava Jan 04 2021 at 16:34

各フィールドを分割するために使用される特定のフィールドセパレータを使用して、フィールドを「再パック」する方法はありますか?

これを使用するgnu-awk split()と、提供された正規表現を使用して、一致した区切り文字の4番目のパラメーターが追加されます。

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

より読みやすいバージョン:

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番目のパラメータで使用される正規表現によって一致するテキストの配列を格納する4番目のsepsパラメータに注意してください。split/[;|]/

もちろんそれは短い&ような単純なようではないRSORSRTのように書くことができ、。

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

以下のよう@anubhavaが言及、gawkがありsplit()(とpatsplit()にあるFPATようsplit()にあるFS-を参照https://www.gnu.org/software/gawk/manual/gawk.html#String-Functions)あなたがやりたいことをする。POSIX awkで同じ機能が必要な場合は、次のようにします。

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

フィールドセパレータが" "他のすべてのフィールドセパレータ値とは2つの異なることを意味するため、上記の特定の処理に注意してください。

  1. フィールドは実際には空白のチェーンで区切られ、
  2. $ 1(この場合はflds [1])を入力するときは、先頭の空白は無視されます。したがって、空白が存在する場合は、すべてのseps [N]が関連付けられているため、この目的のためにseps [0] `に取り込む必要があります。その前にflds [N]があります。

たとえば、次の3つの入力ファイルで上記を実行します。

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

==> file2 <==
hello how are_you

==> file3 <==
    hello how are_you

次の出力が得られます。各フィールドはフィールド番号として表示され、次にフィールド値が内に表示され、[...]次にセパレータが内<...>に表示され、すべてが内に表示されます{...}seps[0]FSが" "あり、レコードが空白で始まる場合に入力されます)。

$ 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

分割する別のオプションは、matchを使用してフィールド区切り文字を見つけ、それらを配列に読み込むことです。

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"