文字列からすべてのn番目の文字を抽出します。

文字列からすべてのn番目の文字を抽出します。

解決策を見つけようとしています。これ質問。これまでこの問題を解決した方法は次のとおりです。

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

次のコマンドを使用して上記の手順を構築できました。

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

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

1   0   0   0   0   0

0   1   1   1   0   0

上記のコマンドを適用した後、私の値は次のようになります。

100000011100

それでは、この大きな文字列に次の方法を適用してみましょう。

元のOPで要求されているように、すべての6文字を抽出して文字列の最後まで配列要素に追加します。

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

101番目7番目の文字)、2番目018番目の文字)、(3番目9番目の文字)、(4番目10番目の文字)、(5番目11番目の文字)、(6番目と12文字)最初の文字)最初の文字)。01010000

だから私の質問は、追加処理のために配列に追加できるようにすべてのn文字をどのように抽出できますか? (この場合 n=6)。

答え1

2行

bash配列を生成する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を使用すると、parameterviaなどの変数から部分文字列を抽出できます${parameter:offset:length}。私たちの場合、オフセットはループ変数によって決まり、i長さは常にです1

すべてのライン数に対する汎用ソリューション

たとえば、元の文字列が18文字であると仮定すると、iのi番目、i + 6番目、i + 12番目の文字を0から5まで抽出しようとします。だから:

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文字の行に拡張できます。たとえば、s3行(18文字)がある場合:

s="100000011100234567abcdef"

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

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

答え2

使用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

両方のラインで動作します。任意の行を処理するには、大きな文字列を作成するのではなく、行を直接処理する必要があります。次の入力を渡してください。

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

答え3

シェルソリューションとして、getoptsおそらく最も簡単です。問題は、getoptsPOSIXが指定されており、正確に目的の操作を実行することです。つまり、シェルループ内のバイトストリームを処理します。奇妙に聞こえるということを知っています。私がこの言葉を学ぶ前に皆さんも私と同じなら、次のように考えることもできますから。まあ、私はそれがコマンドラインスイッチを処理しなければならないと思いました。これは本当ですが、最初のものも同じです。考慮する:

-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最初の文字が空白文字の場合は破棄されます。コロンは、POSIXが明示的に禁止する唯一の引数であるにもかかわらず、指定された引数としてdash受け入れられます。:しかし、問題ではありません。エラーを返しても、getopts現在のopt char値は保存され続けるからです。$OPTARG(指定されたopt変数に割り当てられた?で表されます)$OPTARGそれ以外の場合は、オプションに引数が必要であると宣言しない限り、明示的に設定が解除されます。スペースは良いです。スペースは1つだけ捨てます。リーダー不明な値を処理するときは、次のようにすることができます。

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

$OPTIND=1今使ったので最初の行に設定しましたが、getoptsリセットする前に次の呼び出しが中断された部分から続くことを期待します。"${arg2}"つまり、欲しいものです。しかし、与えたくなく、今は別のことをしていて、$OPTINDいつ始められるかをお知らせするためにリセットしました。

私が使用したこの文字では、zsh先行スペースに反対しません。したがって、最初の文字は8進数40スペース文字です。しかし、私は通常getoptsこのように使用しません。主に使用します。避けるwrite()上記のように、各バイトに対して操作を実行し、その出力(変数の)を別のシェル変数に割り当てますset。その後、準備ができたら文字列全体を取得できます。これにより通常は最初のバイトが削除されます。

答え4

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

正規表現の欲張りな性質のため、可能であれば可変長一致が一致し、キャプチャする項目が残っていない場合はキャプチャされずに文字が削除されます。

関連情報