簡體   English   中英

AWK FPAT 無法按預期進行字符串解析

[英]AWK FPAT not working as expected for string parsing

我必須解析一個非常長的字符串(來自標准輸入)。 它基本上是一個.sql 文件。 我必須從中獲取數據。 我正在解析數據,以便將其轉換為 csv。 為此,我使用的是 awk。 就我而言,一個示例片段(兩條記錄)如下:

b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
echo $b|awk 'BEGIN {FPAT = "([^\\)]+)|('\''[^'\'']+'\'')"}{print $1}'

在我的正則表達式中,我說的是“)”括號上的拆分,或者如果找到單引號,則忽略所有文本,直到找到最后一個引號。 但是我的output如下:

(abc@xyz.com,www.example.com,'field2,(2

我期待這個 output

(abc@xyz.com,www.example.com,'field2,(2)'

我的代碼中的問題在哪里。 我搜索了很多並檢查了 awk 手冊,但沒有成功。

我在下面的第一個答案是錯誤的,您正在嘗試做的事情有一個 ERE:

$ echo "$b" | awk -v FPAT="[(]([^)]|'[^']*')*)" '{for (i=1; i<=NF; i++) print $i}'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

原始答案,另一種方法:

您需要一種 2-pass 方法,首先將引用字段中的所有)替換為輸入中尚不存在的內容(例如 RS),然后識別(...)字段並將 RS 放回) s在打印它們之前:

$ echo "$b" |
awk -F"'" -v OFS= '
    {
        for (i=2; i<=NF; i+=2) {
            gsub(/)/,RS,$i)
            $i = FS $i FS
        }
        FPAT = "[(][^)]*)"
        $0 = $0
        for (i=1; i<=NF; i++) {
            gsub(RS,")",$i)
            print $i
        }
        FS = FS
    }
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

由於 FPAT,以上內容僅適用於 gawk(或者我們可以使用 gawk patsplit() ),而其他 awk 則使用了 while-match()-substr() 循環:

$ echo "$b" |
awk -F"'" -v OFS= '
    {
        for (i=2; i<=NF; i+=2) {
            gsub(/)/,RS,$i)
            $i = FS $i FS
        }
        while ( match($0,/[(][^)]*)/) ) {
            field = substr($0,RSTART,RLENGTH)
            gsub(RS,")",field)
            print field
            $0 = substr($0,RSTART+RLENGTH)
        }
    }
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

使用您在 GNU awk中顯示的示例編寫和測試。 這可以在簡單的字段分隔符設置中完成,請嘗試執行一次,其中b是您的 shell 變量,其中包含您的顯示值。

echo "$b" | awk -F'\\),\\(' '{print $1}'
(abc@xyz.com,www.example.com,'field2,(2)'

說明:只需將awk程序的字段分隔符設置為\\),\\(用於您的輸入和打印它的第一個字段。

與 Ed 建議的類似的正則表達式方法,但我通常更喜歡使用RSRT而不是FPAT

b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
awk -v RS="[(]('[^']*'|[^)])*[)]" 'RT {print RT}' <<< "$b"
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')

如果你想接近一次,也許試試這個

{mawk/mawk2/gawk} 'BEGIN { OFS = FS = "\047"; ORS = RS = "\n";

        XFS = "\376\004\377"; 
        XRS = "\051" ORS;
    
    } ! /[\051]/ { print; next; } { for (x=1; x <= NF; x += 2) { 

        gsub(/[\051][^\050]*/, XFS, $(x)); } } gsub(XFS, XRS) || 1'

我用 2 個 gsub 這樣做,以防它開始在下面發送行並產生意想不到的后果。 \051 = ")", \050 是開放的。

  • 如果沒有找到右括號,則告訴它立即打印並繼續前進,從而進一步增強了它(所以根本沒有要拆分的東西)

一旦我用單引號 \047 拆分它,它只會在奇數字段上循環(因為偶數字段恰好是一對單引號中你想要避免砍掉的那些)。

至於 XFS,只需使用幾乎不可能遇到的字節來選擇您選擇的任意組合。 如果您想安全起見,可以測試該行中是否存在 XFS,並使用一些替代組合。 基本上是在不會與實際輸入數據發生沖突的行中間插入一個分隔符。 它本身並不是萬無一失的,但是遇到 UTF16 字節順序標記和 ASCII 控制字符組合的可能性相當低。

(如果您遇到 XFS,很可能您一開始就已經損壞了數據,因為 300 系列八進制必須后跟 200 系列八進制才能成為有效的 UTF8)

這樣,我根本不需要 FPAT。

*在結尾處更新為“|| 1”作為安全保護,但實際上並不需要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM