Bashで2つの文字列の重なりを見つける方法は? [閉鎖]

Bashで2つの文字列の重なりを見つける方法は? [閉鎖]

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バイナリファイルを比較してみてください。デフォルトでは、これは最初の固有バイトのバイトオフセットを表します。ある文字列が別の文字列の接頭辞である特別な場合があります。cmpSTDERR から別のメッセージが生成されます。この問題を処理する簡単な方法は、最も短い文字列を使用することです。

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))}

賢いアルゴリズムが存在することを望んでいますが、単純な検索では見つけることができません。

関連情報