伏字処理は「内容は伝えつつ、直接は書かない」ための技
マスク処理は「個人情報などを見せていい範囲だけ残す」テクニックでした。
一方で 伏字処理 は、もう少し“表現寄り”のテクニックです。
NGワードを伏せたい。
不適切な単語をそのまま出さずに「○○」にしたい。
レビュー画面では具体名を隠して、雰囲気だけ伝えたい。
こういうときに使うのが伏字処理です。
「情報を守る」というより、
「表現をマイルドにする」「直接的な表現を避ける」ために使われることが多いです。
マスク処理との違いをまず押さえる
マスク処理は「位置ベース」で考えることが多いです。
先頭何文字を残すか、末尾何文字を残すか、などです。
伏字処理は、どちらかというと「内容ベース」です。
特定の単語やフレーズが含まれていたら、それを伏字にする。
例えば、
「バカ」という単語を「○○」にする。
「機密」という単語を「**」にする。
といった具合です。
つまり、マスク処理は「文字列のどの位置を隠すか」、
伏字処理は「文字列のどの“意味(単語)”を隠すか」、
という違いがある、とイメージしておくと分かりやすいです。
基本形:NGワードを丸ごと伏字にする
単純な「完全一致置換」から始める
まずは一番シンプルな、「NGワードを全部同じ伏字にする」パターンから始めます。
public final class Censor {
private Censor() {}
public static String censorWord(String text, String ngWord, String replacement) {
if (text == null) {
return null;
}
if (ngWord == null || ngWord.isEmpty()) {
return text;
}
if (replacement == null) {
replacement = "○○";
}
return text.replace(ngWord, replacement);
}
}
Java使い方はこうなります。
String s1 = "あいつはバカだと言った。";
System.out.println(Censor.censorWord(s1, "バカ", "○○"));
// あいつは○○だと言った。
String s2 = "この資料は機密情報です。";
System.out.println(Censor.censorWord(s2, "機密", "**"));
// この資料は**情報です。
Javaここでのポイントは、「単純な replace でも“伏字っぽいこと”はすぐにできる」という感覚を持つことです。
ただし、このままだと「部分一致しすぎる」問題が出てきます。
例えば、「バカンス」という単語まで「○○ンス」になってしまうかもしれません。
これをどう扱うかが、伏字処理の設計でとても重要になります。
NGワードを複数まとめて伏字にする
まずは「全部同じ伏字」でよい場合
NGワードが1つだけ、ということはあまりありません。
複数の単語をまとめて伏字にしたいことがほとんどです。
import java.util.List;
public final class Censor {
private Censor() {}
public static String censorWords(String text, List<String> ngWords, String replacement) {
if (text == null) {
return null;
}
if (ngWords == null || ngWords.isEmpty()) {
return text;
}
if (replacement == null) {
replacement = "○○";
}
String result = text;
for (String ng : ngWords) {
if (ng == null || ng.isEmpty()) {
continue;
}
result = result.replace(ng, replacement);
}
return result;
}
}
Java使い方はこうです。
List<String> ng = List.of("バカ", "アホ", "機密");
String s = "バカとかアホとか言うな。この資料は機密情報だ。";
System.out.println(Censor.censorWords(s, ng, "○○"));
// ○○とか○○とか言うな。この資料は○○情報だ。
Javaここでの重要ポイントは、「NGワードのリストを“データ”として持ち、処理ロジックから分離している」ことです。
NGワードの追加・削除は設定ファイルやDB側で行い、
コード側は「リストにあるものを全部伏字にする」という汎用ロジックにしておくと、運用が楽になります。
伏字の“長さ”をどうするか問題
全部同じ記号にするか、文字数に合わせるか
伏字処理でよく悩むのが、「伏字の長さをどうするか」です。
常に「○○」のように固定長にするのか。
元の文字数に合わせて「バカ」なら「○○○」にするのか。
元の文字数に合わせると、
「どのくらいの長さの単語だったか」は分かってしまいますが、
読みやすさや自然さは上がります。
元の長さに合わせる例を見てみましょう。
public final class Censor {
private Censor() {}
public static String censorWordWithSameLength(String text, String ngWord, char maskChar) {
if (text == null) {
return null;
}
if (ngWord == null || ngWord.isEmpty()) {
return text;
}
String replacement = repeat(maskChar, ngWord.length());
return text.replace(ngWord, replacement);
}
private static String repeat(char ch, int count) {
StringBuilder sb = new StringBuilder(count);
for (int i = 0; i < count; i++) {
sb.append(ch);
}
return sb.toString();
}
}
Java使い方はこうです。
String s = "あいつはバカだ。こいつもバカだ。";
System.out.println(Censor.censorWordWithSameLength(s, "バカ", '*'));
// あいつは***だ。こいつも***だ。
Javaここでのポイントは、「伏字の長さを元の単語と同じにすることで、“伏せつつもリズムは保つ”」という感覚です。
小説やレビューコメントなど、文章としての自然さを重視したいときに有効です。
正規表現を使った“もう少し賢い”伏字処理
単語境界を意識した伏字
単純な replace だと、「バカンス」まで伏字になってしまう問題がありました。
これを避けるために、正規表現で「単語としての“バカ”だけ」を狙うこともできます。
日本語だと「単語境界」の定義が難しいですが、
例えば「前後がひらがな・カタカナ・漢字・英数字ではない場合だけ伏字にする」といった工夫が考えられます。
ここでは、あくまでイメージとして、簡易的な例を示します。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public final class CensorRegex {
private CensorRegex() {}
public static String censorWordSimpleBoundary(String text, String ngWord, String replacement) {
if (text == null) {
return null;
}
if (ngWord == null || ngWord.isEmpty()) {
return text;
}
if (replacement == null) {
replacement = "○○";
}
// 前後が「文字・数字・ひらがな・カタカナ・漢字」以外、というざっくりした境界
String pattern = "(?<![\\p{L}\\p{N}ぁ-んァ-ン一-龥])"
+ Pattern.quote(ngWord)
+ "(?![\\p{L}\\p{N}ぁ-んァ-ン一-龥])";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(text);
return m.replaceAll(replacement);
}
}
Java使い方はこうです。
String s = "バカと言うな。バカンスに行きたい。";
System.out.println(CensorRegex.censorWordSimpleBoundary(s, "バカ", "○○"));
// ○○と言うな。バカンスに行きたい。
Javaここでの重要ポイントは、「完全に正確でなくても、“誤爆を減らすための工夫”を入れられる」ということです。
正規表現は強力ですが、複雑にしすぎると保守が難しくなります。
「どこまで厳密にやるか」は、システムの性質や要求に応じて決めるのが現実的です。
伏字処理で必ず意識してほしいこと
一つ目は、「伏字にする“対象”をきちんと定義する」ことです。
NGワードのリストなのか、個人名なのか、商品名なのか。
それによって、完全一致にするのか、部分一致にするのか、正規表現を使うのかが変わります。
二つ目は、「伏字の“形”を統一する」ことです。○○ にするのか、*** にするのか、[伏字] にするのか。
システム全体で統一しておくと、ユーザーにも開発者にも分かりやすくなります。
三つ目は、「どの層で伏字にするか」を決めることです。
DBに保存する前に伏字にするのか。
画面に出す直前で伏字にするのか。
ログに書く直前で伏字にするのか。
おすすめは、DBには生の値を持ち、
画面やログなど「人の目に触れるところ」で伏字にする設計です。
これにより、「内部処理では正確な値を使える」「外に出るときだけ表現をマイルドにする」という分離ができます。
まとめ:伏字処理ユーティリティで身につけたい感覚
伏字処理は、「情報を完全に隠す」のではなく、
「内容はなんとなく伝えつつ、直接的な表現を避ける」ためのテクニックです。
まずは、String#replace を使った単純な伏字から始めて、
「NGワードのリストをデータとして持つ」「伏字の長さをどうするか決める」
といった設計の感覚を身につけてください。
そのうえで、必要に応じて正規表現を使った“誤爆を減らす伏字”や、
メールアドレス・名前・商品名など、対象ごとの専用伏字ロジックを薄いラッパーとして用意していく。
もしあなたのコードのどこかに、
そのまま出したくない単語や表現を replace で場当たり的に置き換えている箇所があれば、
それを題材にして、ここで作った Censor や CensorRegex のようなユーティリティにまとめてみてください。
それだけで、「表現のコントロールができる、実務レベルの文字列処理」に一歩近づけます。
