String の split(String regex) は、頻繁に良く使われるメジャーなメソッドである。
でも、区切り文字(デリミタ)に対してエスケープがあり、エスケープ文字を考慮して split でリストを求めたい。
サンプル文字列
「$.aaa.\.bb\.bbb.cc\.\.c.\.\ddd\..ee\\ee.f\f」
デリミタ '.' に対してエスケープする文字は、`\` のケース
splitして 改行して並べた結果、以下を期待する。
$ aaa .bb.bbb cc..c .ddd. ee\ee ff
StringTokenizer で実行する方法
ただし、デリミタ、エスケープ文字はメソッド引数で指定できない
public static List<String> splittolist(String str){ List<String> list = new ArrayList<>(); StringTokenizer st = new StringTokenizer(str, ".", true); String s = ""; while (st.hasMoreTokens()) { String c = st.nextToken(); if (c.equals(".")) { if (s.endsWith("\\")){ s += c; }else { list.add(s.replaceAll("\\\\", "")); s = ""; } }else { s += c; } } list.add(s.replaceAll("\\\\", "")); return list; }
StringTokenizer で実行する方法
デリミタ、エスケープ文字を指定できるようにする。
public static List<String> tokenToList(String str, char sep, char escape){ List<String> list = new ArrayList<>(); String sp = new String(new char[]{ sep }); String escapes = new String(new char[]{ escape, escape }); StringTokenizer st = new StringTokenizer(str, sp, true); String s = ""; while (st.hasMoreTokens()){ String c = st.nextToken(); if (c.equals(sp)) { if (s.charAt(s.length()-1)==escape) { s += c; }else{ list.add(s.replaceAll(escapes, "")); s = ""; } }else { s += c; } } list.add(s.replaceAll(escapes, "")); return list; }
char 型で処理する方法
public static List<String> tokenToList(String str, char sep, char escape){ List<String> list = new ArrayList<>(); StringBuilder sb = new StringBuilder(); boolean isEscape = false; for(char c:str.toCharArray()) { if (isEscape) { isEscape = false; }else if(c==escape){ isEscape = true; continue; }else if(c==sep){ list.add(sb.toString()); sb.setLength(0); continue; } sb.append(c); } list.add(sb.toString()); return list; }
String str = "$.aaa.\\.bb\\.bbb.cc\\.\\.c.\\.\\ddd\\..ee\\\\ee.f\\f"; List<String> list = tokenToList(str, '.', '\\'); list.stream().forEach(e->{ System.out.println("["+e+"]"); });
上の結果
[$] [aaa] [.bb.bbb] [cc..c] [.ddd.] [ee\ee] [ff]