2つの文字列があります。たとえば、次のように設定されます。
string1="test toast"
string2="test test"
私が望むのは、文字列の先頭から重なる部分を見つけることです。重複とは、上記の例で文字列「test t」を参照することです。
# I look for the command
command "$string1" "$string2"
# that outputs:
"test t"
文字列が存在する場合、string1="atest toast"; string2="test test"
チェックは最初から始まり、「a」は先頭にあるため重複しませんstring1
。
答え1
エラーチェックを追加することで、このような機能を考えることができます。
common_prefix() {
local n=0
while [[ "${1:n:1}" == "${2:n:1}" ]]; do
((n++))
done
echo "${1:0:n}"
}
答え2
これはbash内で完全に行うことができます。 Bashで文字列操作を繰り返すのは遅いですが、シェル操作の数が代数の単純なアルゴリズムがあるため、純粋なbashは長い文字列でも実行可能なオプションです。
longest_common_prefix () {
local prefix= n
## Truncate the two strings to the minimum of their lengths
if [[ ${#1} -gt ${#2} ]]; then
set -- "${1:0:${#2}}" "$2"
else
set -- "$1" "${2:0:${#1}}"
fi
## Binary search for the first differing character, accumulating the common prefix
while [[ ${#1} -gt 1 ]]; do
n=$(((${#1}+1)/2))
if [[ ${1:0:$n} == ${2:0:$n} ]]; then
prefix=$prefix${1:0:$n}
set -- "${1:$n}" "${2:$n}"
else
set -- "${1:0:$n}" "${2:0:$n}"
fi
done
## Add the one remaining character, if common
if [[ $1 = $2 ]]; then prefix=$prefix$1; fi
printf %s "$prefix"
}
標準ツールボックスには以下が含まれます。cmp
バイナリファイルを比較してみてください。デフォルトでは、これは最初の固有バイトのバイトオフセットを表します。ある文字列が別の文字列の接頭辞である特別な場合があります。cmp
STDERR から別のメッセージが生成されます。この問題を処理する簡単な方法は、最も短い文字列を使用することです。
longest_common_prefix () {
local LC_ALL=C offset prefix
offset=$(export LC_ALL; cmp <(printf %s "$1") <(printf %s "$2") 2>/dev/null)
if [[ -n $offset ]]; then
offset=${offset%,*}; offset=${offset##* }
prefix=${1:0:$((offset-1))}
else
if [[ ${#1} -lt ${#2} ]]; then
prefix=$1
else
prefix=$2
fi
fi
printf %s "$prefix"
}
cmp
バイト単位で動作しますが、bashの文字列操作は文字単位で動作します。これは、UTF-8文字セットを使用するなどのマルチバイトロケールでは異なります。上記の関数はバイト文字列の最長の接頭辞を出力します。この方法で文字列を処理するには、まず文字列を固定幅エンコーディングに変換する必要があります。ロケールの文字セットが Unicode のサブセットであると仮定すると、UTF-32 が適しています。
longest_common_prefix () {
local offset prefix LC_CTYPE="${LC_ALL:=$LC_CTYPE}"
offset=$(unset LC_ALL; LC_MESSAGES=C cmp <(printf %s "$1" | iconv -t UTF-32) \
<(printf %s "$2" | iconv -t UTF-32) 2>/dev/null)
if [[ -n $offset ]]; then
offset=${offset%,*}; offset=${offset##* }
prefix=${1:0:$((offset/4-1))}
else
if [[ ${#1} -lt ${#2} ]]; then
prefix=$1
else
prefix=$2
fi
fi
printf %s "$prefix"
}
答え3
sedでは、文字列に改行文字が含まれていないとします。
string1="test toast"
string2="test test"
printf "%s\n" "$string1" "$string2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'
答え4
これは私には粗雑に見えますが、無差別代入を通じてこれを行うことができます。
#!/bin/bash
string1="test toast"
string2="test test"
L=1 # Prefix length
while [[ ${string1:0:$L} == ${string2:0:$L} ]]
do
((L = L + 1))
done
echo Overlap: ${string1:0:$((L - 1))}
賢いアルゴリズムが存在することを望んでいますが、単純な検索では見つけることができません。