
私は解決策を見つけようとしていますこれ質問です。これまでのところ、この問題に対する私のアプローチは次のとおりです。
- すべての文字を連結して長い文字列にします。
- 上記の手順の後、空白またはタブスペースをすべて削除して、1 つの大きな文字列だけになるようにします。
上記の手順を以下のコマンドで実行できました。
column -s '\t' inputfile | tr -d '[:space:]'
このような入力ファイルの場合、
1 0 0 0 0 0
0 1 1 1 0 0
上記のコマンドを適用すると、値は次のようになります。
100000011100
現在、この大きな文字列では、以下のようなアプローチを適用しようとしています。
6 文字ごとに抽出し(元の OP の希望どおり)、文字列の末尾まで配列要素に追加します。
つまり、基本的に上記の手順では、配列要素を次のように作成しようとしています。
10
(1番目と 7番目の文字)、01
(2番目と 8番目の文字)、01
(3番目と 9番目の文字)、01
(4番目と 10番目の文字)、00
(5番目と 11番目の文字)、00
(6番目と 12番目の文字)。
そこで私の質問は、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の使用です部分文字列の拡張parameter
Bash では、 を介して、たとえば変数から部分文字列を抽出できます${parameter:offset:length}
。この場合、オフセットはループ変数によって決定されi
、長さは常に です1
。
任意の行数に対する一般的なソリューション
たとえば、元の文字列に 18 文字あり、0 から 5 までの 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
答え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
これは 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
答え3
シェルソリューションとしては、getopts
おそらく最も簡単です。POSIXgetopts
仕様では、まさにあなたが求めていること、つまりシェルループでバイトストリームを処理することが求められています。奇妙に聞こえるかもしれませんが、私がこれを学ぶ前の私と同じように、おそらく次のように考えていたでしょう。まあ、コマンドラインスイッチを処理するものだと思っていました。それは真実ですが、最初のことも真実です。次のことを考慮してください。
-thisisonelongstringconsistingofseparatecommandlineswitches
はい、getopts
それを処理する必要があります。ループで文字ごとに分割し、$OPTARG
呼び出し時にどの程度詳細に指定したかに応じて、シェル変数または名前で指定した別の変数に各文字を返す必要があります。さらに、シェル変数にエラーを返す必要があり、進捗状況を保存するシェル変数でそれが実行さ$OPTIND
れると、中断したところから再開する何らかの方法で対処できる場合、サブシェルを 1 つも呼び出さずにすべての作業を実行する必要があります。
たとえば、次のようになります。
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
受け入れます。ただし、エラーが返されても、現在の opt 文字の値が引き続き格納される:
ため、これらのことは問題になりません。getopts
$OPTARG
(指定した opt 変数に割り当てられた ? で表されます)それ以外の場合は、$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
$OPTIND=1
最初の行で設定したのは、使用したばかりgetopts
で、リセットするまで、次の呼び出しが中断したところから続行されることを期待しているからです。言い換えると、それが望んでいることです。しかし、私は与える気がなく、今は別のことをしているので、どの時点でリセットしてよいかを"${arg2}"
知らせます。$OPTIND
この例で私が使ったのはzsh
、先頭のスペースについて文句を言わない、つまり最初の文字が8進数の40であるスペース文字です。getopts
しかし、私は通常そのようには使いません。避ける各バイトに対してを実行しwrite()
、代わりにその出力 (変数に入っている) を別のシェル変数に割り当てます (上でset
after で行ったのと同じ方法です)。その後、準備ができたら文字列全体を取得し、通常は最初のバイトを削除します。
答え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
正規表現の貪欲な性質により、可変長の一致は可能な限り一致し、キャプチャするものが何も残っていない場合はキャプチャされず、文字が削除されるだけです。