エキサイト株式会社の中です。
最近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);