Javaのプロパティファイルのエスケープが欲しい
仕事でいくつかの顧客のためのデプロイをする際に設定ファイルを文字列置換ツールを自分で作って使っているのですが、propertiesファイルの値に日本語を入れる必要が生じたときに通常の文字列変換だけやってテキストファイル書き込みをやってしまうと例の
日本語 -> \u65E5\u672C\u8A9E
にならなくてなにか面倒になるかもしれないと思って使えそうなものを色々と調べました。
Apache Commons の org.apache.commons.text.StringEscapeUtils クラスの以下のメソッドが
static String escapeJava(String input)
static String unescapeJava(String input)
多バイト文字の問題は解決してくれるのですが、java propertiesファイルはシングルバイトの特殊文字もいくつかエスケープしますし、どの特殊文字がエスケープされるのかを調べるのも面倒だし間違えそうなので、java.util.Properties クラスを使って文字列エスケープをすれば間違いないと思い以下のコードを書きました。
Javaソースコード
PropertiesEscaping.java
package com.example.properties;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesEscaping {
private static final String KEY = "__key__";
private static final String KEY_AND_EQ = KEY + "=";
// エスケープメソッド
public static String escape(String input) {
String value = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Properties prop = new Properties();
prop.setProperty(KEY, input); // propertiesに値をセット
prop.store(bos, null); // ストリームに書き出して
byte[] bytes = bos.toByteArray(); // バイト列を取得
String content = new String(bytes); // バイト列を文字列に
String[] lines = content.split("\n"); // propertiesファイルそのものなので...
for (String line : lines) {
if (line.startsWith(KEY_AND_EQ)) {
// 目的の行が見つかったのでキーとイコールを省く
value = line.substring(KEY_AND_EQ.length());
break; // 目的の文字列をゲット
}
}
bos.close(); // 閉じておきましょう
}
catch (IOException ex) {
ex.printStackTrace(System.err);
value = null; // エラーなので念の為...
}
return value;
}
// アンエスケープメソッド
public static String unescape(String input) {
String value = null;
String content = KEY_AND_EQ + input; // キー&バリューの一行
try {
// バイト変換してストリームにセット
ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes());
Properties prop = new Properties();
prop.load(bis); // バイトストリームをPropertiesに読み込ませ
value = prop.getProperty(KEY); // 目的の文字列をゲット
bis.close(); // 閉じておきましょう
}
catch (Exception ex) {
ex.printStackTrace(System.err);
value = null; // エラーなので念の為...
}
return value;
}
// メイン
public static void main(String[] args) {
String val1 = "~!@#$%^&*()_+`-=|[{}]\\:\";'<>?,./ 日本語 Qiita";
String val2 = escape(val1);
String val3 = unescape(val2);
System.out.println("val1: " + val1);
System.out.println("val2: " + val2);
System.out.println("val3: " + val3);
}
}
コードで書くと上記のような簡単なものになります。
説明はコメントの通りです。
コンパイルと実行
$ javac com/example/properties/PropertiesEscaping.java
$ java -cp . com.example.properties.PropertiesEscaping
val1: ~!@#$%^&*()_+`-=|[{}]\:";'<>?,./ 日本語 Qiita
val2: ~\!@\#$%^&*()_+`-\=|[{}]\\\:";'<>?,./ \u65E5\u672C\u8A9E Qiita
val3: ~!@#$%^&*()_+`-=|[{}]\:";'<>?,./ 日本語 Qiita
必要だったのはescapeメソッドだけでしたが、unescapeメソッドも作ってみました。
unescapeメソッドは必要なシーンがちょっと思い浮かびませんね。