同じ長さの2つのデータ行があるとします。
abcdb#lae#blabl#a
abc~bola~xblabl~a
#
最初の行の文字(最初の行には1つ以上の#を含めることができます)を削除し、次の行の同じ場所にある文字を削除する必要があります。
abcdblaeblabla
abc~bla~blabla
私はこれを試しましたが、sed '/#/{n;s/~//g}'
私が望むよりも多くの文字を削除します。
答え1
アッ
これらの方法は各行のペア(1と2、3と4など)に対して繰り返され、各ペアの#
最初の行にある文字数だけ処理し、各ペアの2行が同じであると仮定します。長さ。
GNU awk(Linux)およびBSD awk(Mac)と互換性があります。
部分文字列を使用します。
awk '{ a=$0 ; gsub(/#/,"",$0) ; print $0 ; getline ; for (n=1;n<=length(a);n++) if ( substr(a,n,1) != "#" ) printf "%s",substr($0,n,1) ; printf "%s",RS }' file.txt
より狭い画面に合わせて再フォーマットされた同じコード:
awk '{
a=$0 ;
gsub(/#/,"",$0) ;
print $0 ;
getline ;
for (n=1;n<=length(a);n++)
if ( substr(a,n,1) != "#" )
printf "%s",substr($0,n,1) ;
printf "%s",RS
}' file.txt
a=$0
最初の行のコピーを保存します。gsub(/#/,"",$0) ; print $0
#
コピーではなく、最初の行のすべての項目を削除し、変更された最初の行を印刷します。getline
次の行に移動します。for (n=1;n<=length(a);n++)
コピー 最初の行の各文字を段階的に実行します。if ( substr(a,n,1) != "#" )
この単一文字のサブストリングではない場合#
...printf "%s",substr($0,n,1)
...その後、2行目の対応する位置に文字を印刷します。
printf "%s",RS
2行目を改行文字で終了します。
配列を使用して下さい:
awk '{ c=d="" ; elements=split($0,a,"") ; getline ; split($0,b,"") ; for (n=1;n<=elements;n++) if (a[n]!="#") { c = c a[n] ; d = d b[n] } ; print c ; print d }' file.txt
より狭い画面のための再フォーマット:
awk '{
c=d="" ;
elements=split($0,a,"") ;
getline ;
split($0,b,"") ;
for (n=1;n<=elements;n++)
if (a[n]!="#")
{ c = c a[n] ; d = d b[n] } ;
print c ;
print d
}' file.txt
c=d=""
2つの空の文字列を初期化します。これは入力2行の修正版になります。入力ライン数が2本を超える場合は、この手順が必要です。elements=split($0,a,"")
入力の最初の行を配列要素ごとに1文字ずつ配列に変換します。配列要素の数を変数として保存しますelements
。getline
次の行に移動します。split($0,b,"")
入力の2行目を配列要素ごとに1文字ずつ含む配列に変換します。for (n=1;n<=elements;n++)
配列の最初の行にある各要素を段階的に実行します。if (a[n]!="#")
この単一文字配列要素がない場合#
...{ c = c a[n] ; d = d b[n] }
...その後、2行ごとに文字を配置しますn
。
print c ; print d
この2行の新しいバージョンを印刷してください。
警告する:Mac(BSD)バージョンのawkは、配列要素を数値順に自動処理しません。これは最初に私に素晴らしい結果を与えました。
「for(indx in array)」ループが配列を巡回する順序は、POSIX awkでは定義されておらず、実装ごとに異なります。 gawkを使用すると、PROCINFO ["sorted_in"]に事前定義された特別な値を割り当てて順序を制御できます。
要素はGNU awkのように1,2,3,...
作成されてもまだ番号が付けられていますが、BSD awkが使用されている場合は必ずしもその順序で表示されるわけではありません。したがって、無効な文字が表示されます。split
for (n in array)
この問題を解決するには、たとえば、配列を作成するときに配列の長さ(要素数)を保存し、ここで行ったように要素に対してelements=split($0,a,"")
反復for (n=1;n<=elements;n++)
を使用できます。
入力例(file.txt
):
abcdb#lae#blabl#a
abc~bola~xblabl~a
#alpha#beta#gamma#delta#epsilon#
abcdefghijklmnopqrstuvwxyzabcdef
出力例:
abcdblaeblabla
abc~bla~blabla
alphabetagammadeltaepsilon
bcdefhijkmnopqstuvwyzabcde
答え2
次の方法で sed を使用してこれを実行できます。 2 行をパターン空間にインポートした後、2 行の先頭に 2 つのマーカーを配置します。
その後、一度に1文字ずつ右に移動し始めます。移動中にマーカーの右側に何があるかを確認し、それに応じて行動します。
マーカーがパターン空間の終わりに達すると停止します。これで表示されたタスクが完了したら、それをインポートします。そうすれば、欲しいものが何でも残ります。表示は次のとおりです。\ n
sed -Ee '
/#/N;/\n/!b
s/\n/&&/;s/^/\n/
:a
/\n#(.*\n.*\n)./{
s//\n\1/;ba
}
s/\n(.)(.*\n.*)\n(.)/\1\n\2\3\n/
/\n$/!ba
s/\n//;s///2
' input
Perlの使用は、次のアイデアに従って解決できます。
perl -pe '
next unless /#/;
my($n,$p) = (scalar <>);
while ( /#/g ) {
pos($n) = pos() - 1 - $p++;
$n =~ s/\G.//;
}
y/#//d;s/\z/$n/;
' input_file
布材:
1. Skip lines that donot have hash char.
2. Save the next line in $n and init. $p counter which keeps track of the number of hash chars erased till now.
3. Monitor the position of the hash char in a while loop and using info generate the position of the char to be deleted in next line.
4. Erase it using the \G metachar in s///
5. In the final step remove the hash chars from present line and append the next line to it.
今回は、配列を使用する別のアプローチが示されています。
perl -aF'' -ne '
print,next unless /#/;
print,last if eof;
my @I = grep { $F[$_] ne "#" } 0 .. $#F;
my @N = split //, <>;
print @F[@I], @N[@I];
' input_file
布材:
1. Invoke Perl to split each line on a per character basis and have it stored in the array @F anew for every line read.
2. Record the array indices for which the array element is a non hash character.
3. Readin the next line, split it on a per character basis and store in array @N.
4. Now its a matter of selecting the indices we stored in @I and fetch those from arrays @F and @N.
正規表現方法:
perl -pe '
$_ .= <> unless eof;
s/\G.(.*\n.{@{[+pos]}})./$1/ while /(?=#.*\n.)/g;
' input_file
説明する:
° 最後の行でない限り、現在の行に次の行を追加します。
°whileループを介して最初の行にハッシュ文字の位置を記録します。
° 次に、元の行のハッシュ文字と次の行の対応する文字を削除します。
° -p オプションは、while ループを終了すると自動的に $_ を標準出力に出力します。
純粋な文字列の操作方法:
perl -pe '
last if eof;
my $n = <>;
while ( (my $p = index($_,"#")) > -1 ) {
substr($_, $p, 1) = "" for $_, $n;
}
$_ .= $n;
' input_file
これには、組み込みインデックスを使用してハッシュ位置を確認し、組み込みサブストリングで最初の行と次の行に2回使用する操作が含まれます。
答え3
これはにありますawk
。それを見るとき#
、それが行のどこにあるかを決定します。その後、その行と後続のすべての行について、対応する文字位置が行から削除されます。
awk '
/#/ { pound=index($0, "#") }
{
if (pound)
print substr($0, 1, pound-1) substr($0, pound+1)
else
print
}
'
答え4
Gnu awkでgensubを使用する
awk '
/#/{
a=$0
b=length()
getline
$0=a RS$0
while($0!=a){
a=$0
$0=gensub("([^#]*)#(.{"b--"}).","\\1\\2",1)}
}1' infile
説明する:
/#/: 各行に # があります。
a = $ 0:行
b=length(): b の長さを取得します。
getline: 次の行を取得します
$0=a RS$0: a に格納された前の行をバッファ $0 の先頭に追加し、その後に RS レコード区切り文字を追加します。
$0 には 2 行が含まれます。
while($0!=a) : a に格納された行が $0 バッファと異なる間
a=$0: $0 バッファを取得します。
$0=gensub("([^#]*)#(.{"b--"}).","\\1\\2",1): $0 の最初の # と対応する 2 行目を削除します。文字
また、最初の行の長さを 1(b--) 短くします。 1#が削除されるからです。
1:最初の行に#がない場合は$ 0を印刷します。