Sed/awk/perl: コンマで区切られた値の逆順、別のテキストを保持

Sed/awk/perl: コンマで区切られた値の逆順、別のテキストを保持

次のテキストがあります。

LABEL1
    .BYTE 01, 02, 03, 04, 05
    .BYTE 01, 02, 03

カンマで区切られた値の順序を逆にするだけです。

LABEL1
    .BYTE 05, 04, 03, 02, 01
    .BYTE 03, 02, 01

次のように処理する必要があります。

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $03, $05, $07, $00
;-------------------
ITINERARY_01F
    .BYTE $03, $05, $07, $09, $00
;-------------------
ITINERARY_01G
    .BYTE $28, $0D, $00
;-------------------
ITINERARY_01H
    .BYTE $28, $0D, $0F, $13, $00
;-------------------
ITINERARY_01I
    .BYTE $28, $0D, $0F, $11, $00
;-------------------
ITINERARY_01J
    .BYTE $03, $05, $07, $09, $20, $1E, $00
;-------------------
ITINERARY_01K
    .BYTE $28, $0D, $0F, $13, $15, $00
;-------------------
ITINERARY_01L
    .BYTE $03, $05, $07, $09, $20, $1E, $1C, $27
    .BYTE $00
;---------------------

「.BYTE」の後の値を除いて、何も変更する必要はありません。この値は16進形式の逆順でなければならず、 "$"を接頭辞として使用します... "編集"してすみませんが、今これを見ました。もう一度ありがとうございます!

答え1

私はここでこれをしますsed

sed '/,/!b                                                   
s/\( *[^ ]*\)\(.*\)/\2,\n\1/;:t
s/\([^,]*,\)\(\n.*\)/\2\1/;tt
s/\n\(.*\),/\1/' <<\DATA
LABEL1
    .BYTE 01, 02, 03, 04, 05
    .BYTE 01, 02, 03        
LABEL1
    .BYTE 01, 02, 03, 04, 05
    .BYTE 01, 02, 03
DATA

出力

LABEL1
    .BYTE 05, 04, 03, 02, 01 
    .BYTE 03, 02, 01 
LABEL1
    .BYTE 05, 04, 03, 02, 01 
    .BYTE 03, 02, 01 

現在行にカンマがあることを確認してください。!カンマがない場合、sed bスクリプトは終了し、その行は自動的に印刷されます。行するカンマを含めると、sed次のようになります。

  1. s///まず、次のものを交換してラインを準備します。
    • \( *[^ ]*\)- 0個以上の空白シーケンスが最初に発生した直後に\1引用された0個以上の空白以外の文字シーケンスが表示されます。
    • \(.*\)- オンライン上の他のすべての内容は次のように引用されます\2
    • ...そして\2,\n\1
    • ノート- このように、\n右の代替フィールドにエスケープ文字を使用するs///ことは完全に移植可能ではありません。これをサポートしていない場合は、ステートメントでsedリテラル改行を置き換えることでこれを実行できます。n
  2. :というブランチ/テストタグを定義しますt
  3. それでも動作しますが、sed s///代替は次のとおりです。
    • \([^,]*,\)- ゼロ個以上の非カンマ文字のシーケンスそれから引用された単一のカンマは次のとおりです\1
    • \(\n.*\)\n- 少なくとも1つのewline文字で始まり、パターンスペースに残っているすべての項目/すべての項目が続くシーケンス(\2...で引用)
    • ...そして\2\1
  4. 以前の ests///置換がt成功した場合は、est タグsedに分岐してもう一度やり直してください。:t
  5. 最後に、いくつかのsedクリーンアップと交換:
    • \n\(.*\),- ewline文字が最初に表示され、\n最後にカンマが表示された場合...
    • \1- ...そしてその間のすべて。

sed再帰的置換と同様に、\newline区切り文字は一度に1つのカンマ区切りフィールドの後ろに移動します。\newlineがその行の最初の文字になると、交換は停止します。l再帰交換プロセスの進行方法は次のとおりです。

 01, 02, 03, 04, 05,\n    .BYTE$
 01, 02, 03, 04,\n    .BYTE 05,$
 01, 02, 03,\n    .BYTE 05, 04,$
 01, 02,\n    .BYTE 05, 04, 03,$
 01,\n    .BYTE 05, 04, 03, 02,$
\n    .BYTE 05, 04, 03, 02, 01,$

初期準備の交換後は、sedカンマと挿入された行文字を除いて何も区別されません\n。だからどのカンマで区切られた値はうまく機能します。これは長いビットを実行した結果です。

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $00, $07, $05, $03 
;-------------------
ITINERARY_01F
    .BYTE $00, $09, $07, $05, $03 
;-------------------
ITINERARY_01G
    .BYTE $00, $0D, $28 
;-------------------
ITINERARY_01H
    .BYTE $00, $13, $0F, $0D, $28 
;-------------------
ITINERARY_01I
    .BYTE $00, $11, $0F, $0D, $28 
;-------------------
ITINERARY_01J
    .BYTE $00, $1E, $20, $09, $07, $05, $03 
;-------------------
ITINERARY_01K
    .BYTE $00, $15, $13, $0F, $0D, $28 
;-------------------
ITINERARY_01L
    .BYTE $27, $1C, $1E, $20, $09, $07, $05, $03
    .BYTE $00
;---------------------

答え2

文書revbytes2.awk:

#!/usr/bin/awk -f
BEGIN {
        FS=",? +"
}
NF>2 && match($0,"^ +\.BYTE ") {
        printf substr($0,1,RSTART+RLENGTH-1)
        for(i=NF;i>3;i--) printf $i", "
        print $3
        next
}
1

FS=",? +"awk次のスペースと.BYTEバイト,の間のスペースシーケンスをフィールド区切り文字として認識します。

各行に対してスペースで始まり、その後にスペースが続く2つ以上のフィールドを持つ行を見つけ、式の副作用として.BYTE合計でこのプレフィックスの始まりと長さを覚えています。RSTARTRLENGTHmatch(...)

一致するものが見つかり、2つ以上のフィールドがある場合、およびを使用して元の行RSTARTからプレフィックスを切り取り、残りのRLENGTHフィールドは逆順に印刷されます。

空白と.BYTE空白の接頭辞がない場合、またはフィールドが2つ以下の場合、その行はそのまま印刷されます。したがって.BYTE、元に戻すことがないため、1バイトのみを定義する-lineに対してもこれが行われます。

テスト実行:

$ diff -u$(wc -l <input) input <(awk -f revbytes2.awk input)
--- input       2014-10-19 06:04:48.280714146 +0200
+++ /dev/fd/63  2014-10-19 22:40:01.385538235 +0200
@@ -1,42 +1,42 @@
 ITINERARY_ARRAY_01
     .BYTE <ITINERARY_00A
     .BYTE <ITINERARY_01A
     .BYTE <ITINERARY_02A
     .BYTE <ITINERARY_03A
     .BYTE <ITINERARY_04A
     .BYTE <ITINERARY_05A
     .BYTE <ITINERARY_06A
     .BYTE <ITINERARY_07A
     .BYTE <ITINERARY_08A
     .BYTE <ITINERARY_09A
     .BYTE <ITINERARY_10A
     .BYTE <ITINERARY_11A
     .BYTE <ITINERARY_12A
     .BYTE <ITINERARY_13A
     .BYTE <ITINERARY_14A
 ;-------------------
 ITINERARY_01E
-    .BYTE $03, $05, $07, $00
+    .BYTE $00, $07, $05, $03
 ;-------------------
 ITINERARY_01F
-    .BYTE $03, $05, $07, $09, $00
+    .BYTE $00, $09, $07, $05, $03
 ;-------------------
 ITINERARY_01G
-    .BYTE $28, $0D, $00
+    .BYTE $00, $0D, $28
 ;-------------------
 ITINERARY_01H
-    .BYTE $28, $0D, $0F, $13, $00
+    .BYTE $00, $13, $0F, $0D, $28
 ;-------------------
 ITINERARY_01I
-    .BYTE $28, $0D, $0F, $11, $00
+    .BYTE $00, $11, $0F, $0D, $28
 ;-------------------
 ITINERARY_01J
-    .BYTE $03, $05, $07, $09, $20, $1E, $00
+    .BYTE $00, $1E, $20, $09, $07, $05, $03
 ;-------------------
 ITINERARY_01K
-    .BYTE $28, $0D, $0F, $13, $15, $00
+    .BYTE $00, $15, $13, $0F, $0D, $28
 ;-------------------
 ITINERARY_01L
-    .BYTE $03, $05, $07, $09, $20, $1E, $1C, $27
+    .BYTE $27, $1C, $1E, $20, $09, $07, $05, $03
     .BYTE $00
 ;---------------------

比較mawkgawk出力:

$ diff <(mawk -f revbytes2.awk input) <(gawk -f revbytes2.awk input)
gawk: revbytes2.awk:5: warning: escape sequence `\.' treated as plain `.'

標準出力ではあまり差がないようです。いいね!

"^ +\056BYTE ""^ +\.BYTE "式に書かないと警告は消えますmatch(...)

たぶんgawk、一般ユーザーは警告を避けるより良い方法を知っているかもしれません。

答え3

私はそうします:

perl -MTie::File -e'
    tie @lines,"Tie::File","your_file";
    for(@lines){
        next unless /,/; # Skip lines with no commas
        $csv = /(\s*[^,\s]+,.*)/;
        $new_csv = join ",",reverse split /,/,$csv;
        s/\Q$csv/$new_csv/;
    }'

妻の声明!

ファイルが変更されます。現場で。必要でない場合は、ファイルの仮想コピーを使用してください。

元のファイルのバージョンは変更されません。

perl -pe'
        next unless /,/; # Skip lines with no commas
        chomp;
        $csv = /(\s*[^,\s]+,.*)/;
        $new_csv = join ",",reverse split /,/,$csv;
        $new_csv .= "\n"; # The newline removed by chomp
        s/\Q$csv/$new_csv/;
    ' your_file

仮説

  • カンマの周りのスペースは気にしません。
  • 最初の CSV 値は.BYTE1 つ以上のスペースにオフセットされます。
  • 「逆順」とは、数値の降順で並べ替えるのではなく、ファイルで見つかった順序を逆にすることを意味します。

入力する

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $03, $05, $07, $00
;-------------------
ITINERARY_01F
    .BYTE $03, $05, $07, $09, $00
;-------------------
ITINERARY_01G
    .BYTE $28, $0D, $00
;-------------------
ITINERARY_01H
    .BYTE $28, $0D, $0F, $13, $00
;-------------------
ITINERARY_01I
    .BYTE $28, $0D, $0F, $11, $00
;-------------------
ITINERARY_01J
    .BYTE $03, $05, $07, $09, $20, $1E, $00
;-------------------
ITINERARY_01K
    .BYTE $28, $0D, $0F, $13, $15, $00
;-------------------
ITINERARY_01L
    .BYTE $03, $05, $07, $09, $20, $1E, $1C, $27
    .BYTE $00
;---------------------

出力

ITINERARY_ARRAY_01
    .BYTE <ITINERARY_00A
    .BYTE <ITINERARY_01A
    .BYTE <ITINERARY_02A
    .BYTE <ITINERARY_03A
    .BYTE <ITINERARY_04A
    .BYTE <ITINERARY_05A
    .BYTE <ITINERARY_06A
    .BYTE <ITINERARY_07A
    .BYTE <ITINERARY_08A
    .BYTE <ITINERARY_09A
    .BYTE <ITINERARY_10A
    .BYTE <ITINERARY_11A
    .BYTE <ITINERARY_12A
    .BYTE <ITINERARY_13A
    .BYTE <ITINERARY_14A
;-------------------
ITINERARY_01E
    .BYTE $00, $07, $05, $03
;-------------------
ITINERARY_01F
    .BYTE $00, $09, $07, $05, $03
;-------------------
ITINERARY_01G
    .BYTE $00, $0D, $28
;-------------------
ITINERARY_01H
    .BYTE $00, $13, $0F, $0D, $28
;-------------------
ITINERARY_01I
    .BYTE $00, $11, $0F, $0D, $28
;-------------------
ITINERARY_01J
    .BYTE $00, $1E, $20, $09, $07, $05, $03
;-------------------
ITINERARY_01K
    .BYTE $00, $15, $13, $0F, $0D, $28
;-------------------
ITINERARY_01L
    .BYTE $27, $1C, $1E, $20, $09, $07, $05, $03
    .BYTE $00
;---------------------

答え4

入力に応じて、以下を使用できますperl

$ perl -MText::Tabs -anle '
    BEGIN {$tabstop = 4};
    print and next if /^\S/;
    @nums = grep { $_ =~ /\d+/ } @F;
    map { s/\D//g } @nums;
    map { $_ = (pop @nums) . (@nums==0 ? "" : ",")
        if $_ =~ /\d+/ } @F;
    print expand "\t@F";
' file
LABEL1
    .BYTE 05, 04, 03, 02, 01
    .BYTE 03, 02, 01

元の入力がソートされたとします。それ以外の場合は@nums = sort { $a <=> $b } grep { $_ =~ /\d+/ } @F;使用できます@nums = grep { $_ =~ /\d+/ } @F;

関連情報