近々仕事で書くことになりそうなので、久しぶりにShift_JISとWindows-31Jのマッピング変換コードでも。
public class CodeReviser { public static void main(String[] args) { String target = args[0]; System.out.println("Before [" + target + "] Hex[" + toHexString(target) + "]"); String converted = reviseCode(target); System.out.println("After [" + converted + "] Hex[" + toHexString(converted) + "]"); } public static String reviseCode(String target) { StringBuilder builder = new StringBuilder(); for (char c : target.toCharArray()) { switch (c) { case '\u2014': // EM DASH builder.append('\u2015'); // HORIZONTAL BAR break; case '\u301C': // WAVE DASH builder.append('\uFF5E'); break; case '\u2016': // DOUBLE VERTICAL LINE builder.append('\u2225'); break; case '\u2212': // MINUS SIGN builder.append('\uFF0D'); break; case '\u00A2': // CENT SIGN builder.append('\uFFE0'); break; case '\u00A3': // POUND SIGN builder.append('\uFFE1'); break; case '\u00AC': // NOT SIGN builder.append('\uFFE2'); break; default: builder.append(c); break; } } return builder.toString(); } public static String toHexString(String target) { StringBuilder builder = new StringBuilder(); for (char c : target.toCharArray()) { builder.append("[\\u").append(Integer.toHexString(c)).append("]"); } return builder.toString(); } }
テスト。実行環境はCentOSなので、ターミナルの入力コードはShift_JISマッピングで動きますよ。
$ javac CodeReviser.java $ java CodeReviser "あ〜‖−¢£¬い" Before [あ〜‖−¢£¬い] Hex[[\u3042][\u301c][\u2016][\u2212][\ua2][\ua3][\uac][\u3044]] After [あ〜‖−¢£¬い] Hex[[\u3042][\uff5e][\u2225][\uff0d][\uffe0][\uffe1][\uffe2][\u3044]]
よく見かける決まりきったコードですが、もっと短くならん?ってことで、Mapを使うように変更。
import java.util.Collections; import java.util.Map; import java.util.HashMap; public class CodeReviser { private static final Map<Character, Character> CODE_MAP; static { Map<Character, Character> map = new HashMap<Character, Character>(); map.put('\u2014', '\u2015'); // EM DASH map.put('\u301C', '\uFF5E'); // WAVE DASH map.put('\u2016', '\u2225'); // DOUBLE VERTICAL LINE map.put('\u2212', '\uFF0D'); // MINUS SIGN map.put('\u00A2', '\uFFE0'); // CENT SIGN map.put('\u00A3', '\uFFE1'); // POUND SIGN map.put('\u00AC', '\uFFE2'); // NOT SIGN CODE_MAP = Collections.unmodifiableMap(map); } public static void main(String[] args) { …省略… } public static String reviseCodeUseMap(String target) { StringBuilder builder = new StringBuilder(); for (Character c : target.toCharArray()) { Character converted = CODE_MAP.get(c); if (converted != null) { builder.append(converted); } else { builder.append(c); } } return builder.toString(); } public static String reviseCode(String target) { …省略… } public static String toHexString(String target) { …省略… } }
mainはこう変更して…
public static void main(String[] args) { String target = args[0]; System.out.println("Before [" + target + "] Hex[" + toHexString(target) + "]"); String converted = reviseCode(target); System.out.println("After [" + converted + "] Hex[" + toHexString(converted) + "]"); target = args[0]; System.out.println("Before [" + target + "] Hex[" + toHexString(target) + "]"); converted = reviseCodeUseMap(target); System.out.println("After [" + converted + "] Hex[" + toHexString(converted) + "]"); }
稼働確認。
$ java CodeReviser "あ〜‖−¢£¬い" Before [あ〜‖−¢£¬い] Hex[[\u3042][\u301c][\u2016][\u2212][\ua2][\ua3][\uac][\u3044]] After [あ〜‖−¢£¬い] Hex[[\u3042][\uff5e][\u2225][\uff0d][\uffe0][\uffe1][\uffe2][\u3044]] Before [あ〜‖−¢£¬い] Hex[[\u3042][\u301c][\u2016][\u2212][\ua2][\ua3][\uac][\u3044]] After [あ〜‖−¢£¬い] Hex[[\u3042][\uff5e][\u2225][\uff0d][\uffe0][\uffe1][\uffe2][\u3044]]
同じ結果に。
が、よーく見ると最初のメソッドと変更後のメソッドでは、ちょっと内容が異なります。具体的には、
ここと
public static String reviseCode(String target) { StringBuilder builder = new StringBuilder(); for (char c : target.toCharArray()) {
ここ
public static String reviseCodeUseMap(String target) { StringBuilder builder = new StringBuilder(); for (Character c : target.toCharArray()) { Character converted = CODE_MAP.get(c);
後者はcharではなく、Characterになっております。この辺は、Javaのプリミティブはオブジェクトではないからですよねぇ。知らない人からすると、ちょっと混乱しそうな部分だと思います。というか、繰り返しのコードこそ減りましたが、実際にはあんまり短くなってない?
では、今度はScalaで書き直してみます。まずは最初のJavaを愚直に移植。
object CodeReviser { def main(args: Array[String]): Unit = { val target = args(0) println("Before [%s] Hex[%s]".format(target, toHexString(target))) var converted = reviseCode(target) println("After [%s] Hex[%s]".format(converted, toHexString(converted))) } def reviseCode(target: String): String = { val builder = new StringBuilder for (c <- target.toCharArray) { // for (c <- target) { // コレでもOK c match { case '\u2014' => builder += '\u2015' // EM DASH case '\u301C' => builder += '\uFF5E' // WAVE DASH case '\u2016' => builder += '\u2225' // DOUBLE VERTICAL LINE case '\u2212' => builder += '\uFF0D' // MINUS SIGN case '\u00A2' => builder += '\uFFE0' // CENT SIGN case '\u00A3' => builder += '\uFFE1' // POUND SIGN case '\u00AC' => builder += '\uFFE2' // NOT SIGN case _ => builder += c } } builder.toString } def toHexString(target: String): String = { val builder = new StringBuilder for (c <- target.toCharArray) { builder append "[\\u" builder append Integer.toHexString(c) builder append "]" } builder.toString } }
実行。
$ fsc CodeReviser.scala $ scala CodeReviser "あ〜‖−¢£¬い" Before [あ〜‖−¢£¬い] Hex[[\u3042][\u301c][\u2016][\u2212][\ua2][\ua3][\uac][\u3044]] After [あ〜‖−¢£¬い] Hex[[\u3042][\uff5e][\u2225][\uff0d][\uffe0][\uffe1][\uffe2][\u3044]]
うん、同じ結果。この時点で既にJava版よりかなり短いのですが、せっかくScalaを使っているのでもうちょっと宣言的に書き直してみましょう。
object CodeReviser { def main(args: Array[String]): Unit = { …省略… } def reviseCodeUseMapFunc(target: String): String = target map { c => c match { case '\u2014' => '\u2015' // EM DASH case '\u301C' => '\uFF5E' // WAVE DASH case '\u2016' => '\u2225' // DOUBLE VERTICAL LINE case '\u2212' => '\uFF0D' // MINUS SIGN case '\u00A2' => '\uFFE0' // CENT SIGN case '\u00A3' => '\uFFE1' // POUND SIGN case '\u00AC' => '\uFFE2' // NOT SIGN case _ => c } } def reviseCode(target: String): String = { …省略… } def toHexString(target: String): String = { …省略… }
一気に短くなりました。Stringにmapなんてメソッドあったか?と思われた方は、Scalaのimplicit conversion(暗黙の型変換)について調べてみてください。
実行結果は、もちろん同じです。
$ fsc CodeReviser.scala $ scala CodeReviser "あ〜‖−¢£¬い" Before [あ〜‖−¢£¬い] Hex[[\u3042][\u301c][\u2016][\u2212][\ua2][\ua3][\uac][\u3044]] After [あ〜‖−¢£¬い] Hex[[\u3042][\uff5e][\u2225][\uff0d][\uffe0][\uffe1][\uffe2][\u3044]] Before [あ〜‖−¢£¬い] Hex[[\u3042][\u301c][\u2016][\u2212][\ua2][\ua3][\uac][\u3044]] After [あ〜‖−¢£¬い] Hex[[\u3042][\uff5e][\u2225][\uff0d][\uffe0][\uffe1][\uffe2][\u3044]]
最後、このバージョンからMapを使ったものに変更してみましょう。先のパターンよりコードはそんなに短くなりませんが、JavaとはちょっとMapの使い方が異なるので面白いですよ。
object CodeReviser { def main(args: Array[String]): Unit = { val target = args(0) println("Before [%s] Hex[%s]".format(target, toHexString(target))) var converted = reviseCode(target) println("After [%s] Hex[%s]".format(converted, toHexString(converted))) println("Before [%s] Hex[%s]".format(target, toHexString(target))) converted = reviseCodeUseMapFunc(target) println("After [%s] Hex[%s]".format(converted, toHexString(converted))) println("Before [%s] Hex[%s]".format(target, toHexString(target))) converted = reviseCodeUseMap(target) println("After [%s] Hex[%s]".format(converted, toHexString(converted))) } val CODE_MAP: Map[Char, Char] = Map( '\u2014' -> '\u2015', // EM DASH '\u301C' -> '\uFF5E', // WAVE DASH '\u2016' -> '\u2225', // DOUBLE VERTICAL LINE '\u2212' -> '\uFF0D', // MINUS SIGN '\u00A2' -> '\uFFE0', // CENT SIGN '\u00A3' -> '\uFFE1', // POUND SIGN '\u00AC' -> '\uFFE2' // NOT SIGN ) def reviseCodeUseMap(target: String): String = target map { c => CODE_MAP.getOrElse(c, c) } def reviseCodeUseMapFunc(target: String): String = target map { c => …省略… } def reviseCode(target: String): String = { …省略… } def toHexString(target: String): String = { …省略… } }
MapのgetOrElseメソッドを使うことで、Javaのように存在チェックが入らないことがポイントです。
とまあここまで書いておいてなんですが、実際に仕事で書かなくちゃいけないのは、たぶんPHPなんですけどね!!