4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【ひらがな⇔カタカナ】あいまい検索にヒットしたテキストを一括置換する方法【大文字⇔小文字】

Last updated at Posted at 2021-04-28

あらすじ

下記のような処理をつくる必要がありました。

  1. ひらがな・カタカナどちらでもヒットする曖昧検索をおこなう
  2. ヒットしたテキストを、正規表現を使って置換する

(ちなみに具体的には、長文の中からユーザーが入力した検索ワードをあいまい検索し、ヒットした箇所を<span>で囲んだ状態の文字列に変換し、強調表示させる、というタスクでした。)

さてどうやって実装したもんか、と参考記事をググってみましたが、
ドンピシャな記事はなかなか出てきませんでした。

そのかわり「正規表現であいまい検索をする方法」みたいな記事はいくつかでてきました。
それは「検索対象と検索ワードをそれぞれひらがな(orカタカナ)に変換して、正規チェックする」という内容でした。

 つまり検索対象が私の名前はゴンザレスたろうですという文で、
 検索ワードがゴンザレスタロウだった場合、
 検索対象を私の名前はごんざれすたろうですに変換、
 検索ワードもごんざれすたろうに変換した上で
 正規表現で検索する、という方法です。

素直さがウリである(?)わたしは、言われたとおりにコードを書いてみたのですが、
このような方法だと、ヒットしたテキストを正規置換に利用しようとしたとき、結構複雑な処理が必要でした。

なぜなら、
ヒットしたテキストは、すでにひらがなに変換されているものなので、
ひらがな・カタカナ表記をそのままの状態で置換をしたい場合、
文章の何文字目から何文字目がヒットしていたのかを判定し、
元々の文章から該当する箇所の表記をひっぱってくる必要がありました。
しかも、JavaScriptには、正規マッチした文字列をすべて一気に置換してくれるような関数や、
マッチしている箇所のインデックス番号を取得するような関数が存在しないので、
ヒットしたテキストを1つ1つ置換しつつ、
置換後の文字列と置換前の文字列のインデックスのズレも考慮して置換をしていく必要があったのです。

 たとえば正規置換で、私の名前はゴンザレスたろうです
 私の名前は<span>ゴンザレスたろう</span>ですに変換したい場合、
 置換によって追加される13文字のインデックスのズレを加味したうえで
 ひきつづき置換処理を続ける必要がありました。
 (ヒットする箇所が1箇所だけではない可能性があるので。)

絶望に暮れ、悠久とも思える10分もの間悩んでいたわたしに、
発想の女神の祝福が舞い降りました。
検索対象を置換するんじゃなくて、そもそも正規表現をひらがなでもカタカナでもヒットする形にすれば、とってもシンプルになる!
そのことに気づいたわたしは、早速コードを書きはじめました・・・。

コード

function katakanaToHiragana(src) {
    return src.replace(/[\u30a1-\u30f6]/g, function(match) {
        const chr = match.charCodeAt(0) - 0x60;
        return String.fromCharCode(chr);
    });
}

function hiraganaToKatagana(src) {
    return src.replace(/[\u3041-\u3096]/g, function(match) {
        const chr = match.charCodeAt(0) + 0x60;
        return String.fromCharCode(chr);
    });
}

function escapeRegExp(string) {
    return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}

function generateFuzzyRegExp(searchWord) {
    searchWord = escapeRegExp(searchWord);
    const chars = searchWord.split('').map(char=>{
        const hiragana = this.katakanaToHiragana( char );
        const katakana = this.hiraganaToKatagana( char );
        if( hiragana===katakana ) return char;
        return `(${hiragana}|${katakana})`;
    });
    const fuzzyRegExp = new RegExp( `(${chars.join('')})`, 'ig' );
    return fuzzyRegExp;
}

説明

katakanaToHiragana hiraganaToKatakana
カタカナをひらがなに、ひらがなをカタカナに変換する関数です。
こちらのリポジトリを参考にさせていただきました。

escapeRegexp
正規表現と認識されてしまう文字をエスケープする関数です。
こちらの記事を参考にさせていただきました。

generateFuzzyRegExp
引数に渡された文字列から、
カタカナ・ひらがな・大文字・小文字を区別しない正規表現を生成する関数です。

エスケープ後の文字列を1文字ずつ分割し、
(あ|ア)のようなパターンに変換して配列に格納しています。
joinで1つの文字列にまとめたら、
まとめて $1 で拾えるようにカッコで囲ってあげて、
正規表現に変換してあげれば完成です!

(正規表現のiオプションによって、英大文字・英小文字は自動的に区別しない仕様となります。)

感想

技術レベル的にはたいしたことないですが、
発想の転換って大事だな〜と思いました。

4
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?