ブラウザでテキスト エリアの文からカタカナだけを複数の物は1つにしてアイウエオ順に抽出してレーベンシュタイン距離の順に似ているカタカナの組み合わせから表示する(JavaScriptと)HTMLのコード
カタカナの誤字脱字や、カタカナの日本語での表記のゆれを見つけやすくします。
ファンタジー小説などで長いカタカナの名前が多数ある場合の校正に役立つと思います。
カタカナを抽出して、2つずつ文字の「正規化されたレーベンシュタイン距離」を計算します。
「正規化されたレーベンシュタイン距離」が小さいほど、2つの文字は似ています。
ただし、「レーベンシュタイン距離」は計算の処理の負担が重いので、また、カタカナの単語数の階乗に比例して計算回数とメモリの使用量が増えるので、「表示する正規化されたレーベンシュタイン距離の最大」を0.3くらいに設定してアプリケーションを動かしてみてください。著者のパソコンは性能が悪いので、カタカナの単語数が多数の場合に1.0で動かしたらブラウザがクラッシュしたりパソコンがフリーズしました。
ちなみに、「レーベンシュタイン距離」は文字数に比例して大きくなるので、「レーベンシュタイン距離」を2つの文字の文字数のうち大きい方で割った「正規化されたレーベンシュタイン距離」を利用します。
「正規化されたレーベンシュタイン距離」は0以上1以下の実数に収まります。
Githubで(JavaScriptと)HTMLのソース コードをパブリック ドメインで公開しております。
マイクロソフトのBing検索エンジンで「github eliphas1810-tools」などで検索してみてください。
残念ながらグーグル検索エンジンでは検索できません。
不具合が有るかもしれないので利用は自己責任でお願いいたします。
2024年7月31日時点の最新のChromeとFirefoxで動作を確認しました。
コピーする場合は、2文字の全角空白を4文字の半角空白に置換してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>カタカナの抽出とレーベンシュタイン距離の計算</title>
<!-- Public Domain -->
</head>
<body>
<div>
<textarea id="text" cols="80" rows="10" placeholder="カタカナを抽出してレーベンシュタイン距離を計算したい文をここに記入してください。"></textarea>
</div>
<br />
<div>
表示する0以上1以下の正規化されたレーベンシュタイン距離の最大<input type="text" id="maxNormalizedLevenshteinDistanceToShow" value="0.3" />
</div>
<div>
<input type="range" id="maxNormalizedLevenshteinDistanceToShowSlider" min="0" max="100" value="30" step="1" style="width: 100%;" />
</div>
<br />
<div>
<button type="button" id="extractKatakana">カタカナを抽出してレーベンシュタイン距離を計算</button>
</div>
<br />
<p id="message"></p>
<br />
<div>
<textarea id="katakanaList" cols="80" rows="10" placeholder="抽出されたカタカナの全ての一覧がここに書き込まれます。"></textarea>
</div>
<br />
<div>
<textarea id="katakanaPairList" cols="80" rows="10" placeholder="レーベンシュタイン距離の順に、最も似ているカタカナの2つ1組から、カタカナの2つ1組の一覧がここに書き込まれます。"></textarea>
</div>
<br />
<br />
<div>
<button type="button" id="clear">文を消去する</button>
</div>
<script>
function $(id) {
return document.getElementById(id);
}
//レーベンシュタイン距離を計算
function calculateLevenshteinDistance(firstString, secondString) {
var maxX = firstString.length;
var maxY = secondString.length;
var table = [];
for (var x = 0; x <= maxX; x++) {
table[x] = [];
table[x][0] = x;
}
for (var y = 0; y <= maxY; y++) {
table[0][y] = y;
}
for (var x = 1; x <= maxX; x++) {
for(var y = 1; y <= maxY; y++) {
var cost = null;
if (firstString.charAt(x - 1) == secondString.charAt(y - 1)) {
cost = 0;
} else {
cost = 1;
}
table[x][y] = Math.min(
table[x - 1][y] + 1,
table[x][y - 1] + 1,
table[x - 1][y - 1] + cost
);
}
}
return table[maxX][maxY];
}
//2つの文字の文字数のうち最大文字数で割って、0以上1以下に正規化されたレーベンシュタイン距離を計算
function normalizeLevenshteinDistance(firstString, secondString) {
//レーベンシュタイン距離を計算
var levenshteinDistance = calculateLevenshteinDistance(firstString, secondString);
return (levenshteinDistance / Math.max(firstString.length, secondString.length));
}
$("maxNormalizedLevenshteinDistanceToShowSlider").onchange = function () {
$("maxNormalizedLevenshteinDistanceToShow").value = ($("maxNormalizedLevenshteinDistanceToShowSlider").value / 100);
};
$("extractKatakana").onclick = function () {
$("message").innerHTML = "";
$("katakanaList").value = "";
$("katakanaPairList").value = "";
if ($("maxNormalizedLevenshteinDistanceToShow").value.match("^[01]\\.?[0-9]*$") == null) {
$("message").innerHTML = "0以上1以下の実数を「表示する正規化されたレーベンシュタイン距離の最大」に記入してください。";
return;
}
var maxNormalizedLevenshteinDistanceToShow = parseFloat($("maxNormalizedLevenshteinDistanceToShow").value);
if (maxNormalizedLevenshteinDistanceToShow == NaN) {
$("message").innerHTML = "0以上1以下の実数を「表示する正規化されたレーベンシュタイン距離の最大」に記入してください。";
return;
}
if (maxNormalizedLevenshteinDistanceToShow < 0.0) {
$("message").innerHTML = "0以上1以下の実数を「表示する正規化されたレーベンシュタイン距離の最大」に記入してください。";
return;
}
if (1.0 < maxNormalizedLevenshteinDistanceToShow) {
$("message").innerHTML = "0以上1以下の実数を「表示する正規化されたレーベンシュタイン距離の最大」に記入してください。";
return;
}
var text = $("text").value;
var katakanaArray = [];
var wordArray = text.match(/[ァ-ヺー][ァ-ヺー ]*/g);
if (wordArray == null) {
$("message").innerHTML = "";
return;
}
for (var wordIndex = 0; wordIndex < wordArray.length; wordIndex++) {
var word = wordArray[wordIndex];
katakanaArray.push(word);
}
katakanaArray = Array.from(new Set(katakanaArray));
katakanaArray.sort();
$("katakanaList").value = katakanaArray.join("\n");
var katakanaPairArray = [];
var maxFirstIndex = katakanaArray.length - 2;
var maxSecondIndex = katakanaArray.length - 1;
for (var firstIndex = 0; firstIndex <= maxFirstIndex; firstIndex++) {
var firstString = katakanaArray[firstIndex];
for (var secondIndex = firstIndex + 1; secondIndex <= maxSecondIndex; secondIndex++) {
var secondString = katakanaArray[secondIndex];
var normalizedLevenshteinDistance = normalizeLevenshteinDistance(firstString, secondString);
if (maxNormalizedLevenshteinDistanceToShow < normalizedLevenshteinDistance) {
continue;
}
katakanaPairArray.push({
"firstString": "" + firstString,
"secondString": "" + secondString,
"normalizedLevenshteinDistance": normalizedLevenshteinDistance - 0.0,
"toString": function () {
return this.normalizedLevenshteinDistance + "\n" + this.firstString + "\n" + this.secondString;
}
});
}
}
var compare = function (a, b) {
if (a.normalizedLevenshteinDistance == b.normalizedLevenshteinDistance) {
return 0;
}
if (a.normalizedLevenshteinDistance < b.normalizedLevenshteinDistance) {
return -1;
}
return 1;
};
katakanaPairArray.sort(compare);
$("katakanaPairList").value = katakanaPairArray.join("\n\n");
};
$("clear").onclick = function () {
$("text").value = "";
$("katakanaList").value = "";
$("katakanaPairList").value = "";
$("message").innerHTML = "";
};
</script>
</body>
</html>