web-dev-qa-db-ja.com

sedを使用したテキスト操作

現在、次のような内容のテキストファイルが複数あります(多くの行があります)。

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

各行を次の形式に変更したい:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Sedを使用して上記を行う方法はありますか?または、Pythonに頼る必要がありますか?

12
user695634

はい、sedでできますが、他のツールの方が簡単です。例えば:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

説明

awkは入力の各行を空白で分割し(デフォルト)、各フィールドを$1$2$Nとして保存します。そう:

  • printf "%s ", $2;は、2番目のフィールドと末尾のスペースを出力します。
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }:フィールド3から最後のフィールド(NFはフィールドの数)を反復処理し、それぞれについて、最初のフィールド、:、次に現在のフィールドを出力しますおよび:1
  • print "":これは最後の改行を出力するだけです。

またはPerl:

$ Perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

説明

-aは、Perlawkのように動作させ、その入力を空白で分割します。ここでは、フィールドは配列@Fに格納されます。つまり、最初のフィールドは$F[0]、2番目の$F[1]などになります。

  • print "$F[1] ":2番目のフィールドを出力します。
  • print "$F[0]:$_:1 " for @F[2..$#F];:フィールド3から最後のフィールドまで繰り返します($#Fは配列@Fの要素数です。したがって、@F[2..$#F]は3番目の要素から始まる配列スライスを取得します。配列の最後)、1番目のフィールド、:、次に現在のフィールド、:1を出力します。
  • print "\n":これは最後の改行を出力するだけです。
22
terdon

がここにあります 恐ろしい sed方法!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

より読みやすい:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

ノート

  • -r EREを使用
  • s/old/new/oldnewに置き換えます
  • ^([0-9]+)行の先頭にいくつかの数値を保存します
  • \1最初に保存されたパターンへの後方参照
  • :aスクリプトのこのセクションにラベルを付けるa
  • ( |$)スペースまたは行末
  • t最後の置換が成功したかどうかをテストします。成功した場合は、次のコマンドを実行します
  • aラベル:aを見つけて、もう一度実行します
  • s/ $//末尾のスペースを削除

そのため、最初の部分に構造を追加した後、構造の最後のインスタンスを繰り返し見つけて、次の番号に適用します...

しかし、私は他のツールがそれを簡単にすることに同意します...

12
Zanna

Awkの場合:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

またはbashを使用:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

出力:

 0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
 1 564:7:1 564:12 :1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 
5
Cyrus

まあ、あなたはsedでそれを行うことができますが、pythonも動作します。

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

reformatfile.pyの内容は次のとおりです。

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

これはどのように作動しますか?特に特別なことは何もありません。最初のコマンドライン引数を読み取り用のファイルとして開き、各行を「単語」または個々のアイテムに分解します。最初の単語はpref変数になり、2番目の標準出力(words [1])にスペースで終わる項目を出力します。次に、リスト内包表記と、pref、各Word、および文字列"1"の一時リストでの.join()関数を使用して、新しい「単語」のセットを作成します。最後のステップはそれらを印刷することです

5

awkの場合:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

スペースで区切られたフィールドを目的の形式にフォーマットすることがすべてです。

  • printf("%s ", $2)は、2番目のフィールドに末尾スペースを出力します

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i)は、最後から3番目から2番目のフィールドを繰り返し、目的の形式(最初のフィールド、次にコロン、次に現在のフィールド、次にコロン、最後に1)でフィールドを出力します。

  • printf("%s:%s:1\n", $1, $NF)は最後のフィールドを改行付きで出力します

例:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
4
heemayl