web-dev-qa-db-ja.com

文字列からn番目ごとの文字を抽出する

this 質問の解決策を見つけようとしています。これまでの私の問題への私のアプローチは以下の通りです。

  • すべての文字を一緒に追加して、長い文字列にします。
  • 上記の手順の後で、空白またはタブスペースをすべて削除して、大きな文字列を1つだけ作成します。

以下のコマンドで上記の手順を確立することができました。

column -s '\t' inputfile | tr -d '[:space:]'

したがって、このような入力ファイルの場合、

1   0   0   0   0   0

0   1   1   1   0   0

上記のコマンドを適用すると、次のような値になります。

100000011100

さて、この大きな糸の中で、私は以下のようなアプローチを適用しようとしています。

6つごとに抽出番目 文字(元のOPが必要とするもの)を文字列の最後まで配列要素に追加します。

したがって、基本的には、上記のステップで、配列要素を次のように作成しようとしています。

10(1st と7番目 キャラクター)、 01(2nd と8番目 キャラクター)、 01(3rd と9番目 キャラクター)、 01(4番目 と10番目 キャラクター)、 00(5番目 と11番目 キャラクター)、 00(6番目 と12番目 キャラクター) 。

だから私の質問は、どのようにすべてのnを抽出できますか番目 キャラクターを配列に追加してさらに進めることができますか? (この場合、n = 6)。

6
Ramesh

2行

以下は、bash配列を生成するpure -bashソリューションです。

s="100000011100"
array=($(
    for ((i=0; i<${#s}-6; i++))
    do
        echo "${s:$i:1}${s:$((i+6)):1}"
    done
    ))
echo "${array[@]}"

これにより、質問に示されているのと同じ出力が生成されます。

10 01 01 01 00 00

ここでの重要な要素は、bashの部分文字列展開の使用です。 Bashでは、${parameter:offset:length}を介して、変数[parameterなど)から部分文字列を抽出できます。この場合、オフセットはループ変数iによって決定され、長さは常に1です。

任意の行数の一般的な解決策

たとえば、元の文字列が18文字で、0〜5のiのi番目、i + 6番目、およびi + 12番目の文字を抽出するとします。

s="100000011100234567"
array=($(
    for ((i=0; i<6; i++))
    do
        new=${s:$i:1}
        for ((j=i+6; j<${#s}; j=j+6))
        do 
            new="$new${s:$j:1}"
        done
        echo "$new"
    done
    ))

echo "${array[@]}"

これは出力を生成します:

102 013 014 015 006 007

この同じコードは、任意の数の6文字の行に拡張されます。たとえば、sに3行(18文字)がある場合:

s="100000011100234567abcdef"

次に、出力は次のようになります。

102a 013b 014c 015d 006e 007f
5
John1024

Perlの使用:

$ echo 100000011100 | Perl -nle '
    for ($i = 0; $i < length()/2; $i++) {
        print substr($_,$i,1), substr($_,$i+6,1);
    }
'
10
01
01
01
00
00

これは2行で機能します。任意の行で作業したい場合は、大きな文字列を構築するのではなく、行を直接処理する必要があります。この入力では:

1   0   0   0   0   0                                                           
0   1   1   1   0   0                                                           
0   0   0   0   0   0

試してください:

$ Perl -anle '
    for ($i = 0; $i <= $#F; $i++) {
      Push @{$h{$i}}, $F[$i];
    }
    END {
        print @{$h{$_}} for keys %h;
    }
' file
000
010
000
100
010
010
4
cuonglm

シェルソリューションとして、getoptsがおそらく最も簡単です。 getoptsについてのことは、あなたが求めていることを正確に行うことがPOSIXで指定されていることです-シェルループでバイトストリームを処理します。奇妙に聞こえるかもしれません。私がこれを学ぶ前に私と同じような人なら、おそらくまぁ、コマンドラインスイッチを処理するはずだと思っていたからです。 これは本当ですが、そうです。検討してください:

_-thisisonelongstringconsistingofseparatecommandlineswitches
_

はい、getoptsはそれを処理する必要があります。ループでその文字を文字ごとに分割し、シェル変数_$OPTARG_または名前で指定する別の文字のいずれかで、呼び出すときに取得する具体的な方法に応じて、各文字を返す必要があります。さらに、シェル変数でエラーを返す必要があり、シェル変数_$OPTIND_でエラーが発生したときに進行状況を保存する必要があります何らかの方法で対処できる場合は、中断したところから再開します。そして、それは単一のサブシェルを呼び出さずに仕事全体をしなければなりません。

だから我々が持っているとしましょう:

_arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
_

うーん...うまくいったかな?

_echo "$((${#arg}/6))" "$#"
482 482
_

それはすばらしい...

_eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
_

ご覧のとおり、getoptsコマンドは、文字列の6バイトごとに配列を完全に設定します。そして、それはこのような数字である必要はありません-それはシェルセーフな文字である必要もありません-そして、私が上記で_01234565789_を使ったようにターゲット文字を指定する必要すらありません。私はこれを多くのシェルで繰り返しテストしましたが、すべてうまくいきます。いくつかの癖があります-空白文字である場合、bashは最初の文字を破棄します-dashは、特に禁止されている唯一のPOSIXであっても、_:_コロンを指定されたパラメーターとして受け入れます。しかし、getoptsがエラーを返した場合でも、現在のopt charの値を_$OPTARG_に引き続き格納するため、これらは問題になりません(指定したopt varに割り当てられた?で表されます)であり、オプションが引数を持つべきであると宣言していない限り、明示的に_$OPTARG_の設定を解除します。そして、空白文字は一種の良いものです-リーディングスペースのみを破棄します、これは素晴らしいです、なぜなら未知の値を扱うとき、あなたは行う:

_getopts : o -" $unknown_value"
_

...最初の文字が実際に受け入れられたargs文字列に含まれる危険なしにループを開始します。これにより、getoptsが_$OPTARG_内のすべてを一度に引数として貼り付けます。

次に別の例を示します。

_OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"                         
do printf '\\%04o' "'$OPTARG"; done  

\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166
_

getoptsを使用したばかりなので、最初の行に_$OPTIND=1_を設定しました。リセットするまで、次の呼び出しは中断したところから続行されることを期待しています。つまり、_"${arg2}"_が必要です。しかし、私は与える気がなく、今は別のことをしているので、_$OPTIND_をリセットして通知します。

この例では、zshを使用しました。これは、先頭のスペースについて不正確ではないため、最初の文字は8進数の40(スペース文字)です。私は通常、そのようにgetoptsを使用しませんが、通常はavoid各バイトに対してwrite()を実行するために使用し、代わりに、変数に含まれる出力を別のシェル変数に割り当てます-私は上記のように、setを使用して上記のようにしました。次に、準備ができたら文字列全体を取得し、通常は最初のバイトを削除します。

3
mikeserv

sedが最初に思い浮かぶものです。

$ echo 1234567890abcdefghijklmnopqrstuvwxyz | sed 's/.\{5\}\(.\)/\1/g'
6bhntz

5人のキャラクターと一致し、6番目をキャプチャし、それらすべてをそのキャプチャされたキャラクターで置き換えます。

ただし、文字列の長さが正確に6の倍数でない場合、これには問題があります。

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{5\}\(.\)/\1/g' 
6bhntuvwxy

しかし、sedを少し変更することでこれを修正できます。

$ echo 1234567890abcdefghijklmnopqrstuvwxy | sed 's/.\{1,5\}\(.\{0,1\}\)/\1/g'
6bhnt

正規表現の貪欲な性質により、可変長の一致は可能な限り一致します。キャプチャするものが何も残っていない場合はキャプチャされず、文字が削除されるだけです。

1
Patrick