Javaで一括文字列変換

エキサイト株式会社の中です。

最近Javaで大量の文字列を一括置換する処理作ったので紹介します。

性能部分にちょっと不安がありますが。。。

wikipediaでも自動で特定のワードに対してリンクになりますが、イメージはあんな感じです。

ユースケース

特定の文字列がはいったら、youtubeの検索のリンクの変更にしたい。

入力例

 自動でyoutubeのリンクに変わります。例えばうどん、カレーです。

出力例

 自動でyoutubeのリンクに変わります。例えば<a href="https://www.youtube.com/results?search_query=うどん">うどん</a>、<a href="https://www.youtube.com/results?search_query=カレー">カレー</a>です。

条件

  • hrefにするときは、置換文字列にする
  • 置換文字列はすべて同じ処理で構わない。
  • 簡単に追加できるように
  • いったん固定文字列で構わない。
    • 「いったん」というワードを聞くと、大体あとから拡張される。

コード例

置換文字列マッピング表(DB使わない)

置換対象文字列設定

enumで事前に置換対象文字列を()で囲みます。 staticで事前にPatternを|区切りでコンパイルしているものを設定します。 あえて、説明することはないかもしれませんが、Pattern.compileを事前にstaticで宣言しておくことで 毎回コンパイルが走らなくてすみます。 WORDを増やすことで、置換対象が変わります。 このコード自体はいつでも捨てられるように実装します。

import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public enum WordTargetType {

    WORD_1("(うどん)"),
    WORD_2("(おから)"),
    WORD_3("(カレー)"),
    WORD_4("(グラタン)"),
    WORD_5("(スパゲティ)"),
    WORD_6("(素麺)"),
    WORD_7("(チャーハン)"),
    WORD_8("(佃煮)"),
    WORD_9("(トマトソース)"),
    WORD_10("(パスタ)"),
    ;

    private String word;

    private static Pattern wordTargetPattern;

    static {
        wordTargetPattern = Pattern.compile(Stream.of(WordTargetType
                .values())
                .map(e -> e.getWord())
                .collect(Collectors.joining("|")));
    }


    WordTargetType(String word) {
        this.word = word;
    }

    public String getWord() {
        return this.word;
    }

    public static Pattern getPatternCompile() {
        return wordTargetPattern;
    }
}

ドメインモデル

newするときに、置換対象文字列を設定すると、replaceTextに置換後のテキストが作成されます。 イミュータブルなモデルにするために@Getterをセットすることにします。 replaceAllを使うことで、対象の文字列が一気に変わります。

import com.exblog.core.config.WordTargetType;
import lombok.Getter;
import lombok.experimental.Accessors;

@Getter
@Accessors(chain = true)
public class WordDomainModel {

    private final static String LINK = "<a href=\"https://www.youtube.com/results?search_query=$0/\">$0</a>";

    private String replaceText = "";

    public WordDomainModel(String text){
        this.replaceText = WordTargetType
                .getPatternCompile()
                .matcher(text)
                .replaceAll(LINK);
    }

    
    /**
     * DBからコンパイルのパターンを取得する場合は、引数にパターンを増やしてnew でオブジェクトを作れば良い。
     */
    public WordDomainModel(String text, Pattern pattern){
        this.replaceText = pattern
                .matcher(text)
                .replaceAll(LINK);
    }
}

使用例

以下のように使用すると、シンプルでみやすいと思います。

    public String getWrappedWord(String text) {
        return new WordDomainModel(text)
                .getReplaceText();
    }

UT

注意点として、置換する処理はどのプログラミングを使っても性能(レスポンス速度)が遅いので、 どこまで許容するかはプロジェクトによりますが、以下の例では500msいかになるようなテストにしています。 十分な長さの文字列の変換を試して下さい。

        long start = System.currentTimeMillis();
        final WordDomainModel wordDomainModel = new WordDomainModel("うどん おから");
        long end = System.currentTimeMillis();
        Assertions.assertFalse(wordDomainModel.getReplaceText().isEmpty());
        Assertions.assertTrue(end - start < 500);