Como desabitar um literal de string Java em Java?

Estou processando algum código-fonte Java usando Java. Eu estou extraindo os literais de string e os alimentando a uma function pegando uma String. O problema é que eu preciso passar a versão sem escape do String para a function (ou seja, isso significa converter \n para uma nova linha, e \\ para um único \ , etc).

Existe uma function dentro da API Java que faz isso? Se não, posso obter tal funcionalidade de alguma biblioteca? Obviamente, o compilador Java tem que fazer essa conversão.


PS – Caso alguém queira saber: Estou tentando desobstruir literais de string em arquivos Java descompilados e ofuscados

EDIT: Você pode baixar a fonte completa para a function que discuto abaixo. Eu também discuto isto mais detalhadamente nesta resposta .

O problema

O org.apache.commons.lang.StringEscapeUtils.unescapeJava() dado aqui como “a resposta” é realmente muito pouca ajuda.

  • Você tem que fornecer para carregar ainda outro arquivo jarcom com buttloads de lixo que você não precisa ou quer.
  • Tem uma licença. Algumas pessoas não querem se preocupar com uma licença, não importa quão boa ou ruim seja.
  • Esquece \0 de nulo.
  • Não lida com octal.
  • Ele não pode lidar com os tipos de escapes admitidos pelo java.util.regex.Pattern.compile() e tudo que o usa, incluindo \a , \e , e especialmente \cX .
  • Ele não tem suporte para pontos de código lógicos Unicode por número, apenas para o dano cerebral idiotivo UTF-16.
  • É escrito por algum idiota que nem sabe a diferença entre uma barra e uma barra invertida .
  • O código-fonte está cheio de retornos de carro irritantes.
  • Está escrito para ter um argumento de writer , então se você não o passar, ele ainda tem que criar um StringWriter para a saída, então converta isso para passar de volta para você.
  • Isso se parece com o código UCS-2, não com o código UTF-16: eles usam a interface codePoint depreciada em vez da interface codePoint , promulgando assim a ilusão de que um char Java tem a garantia de manter um caractere Unicode. Não é. Eles só escapam com essa cegueira para os planos astrais, porque nenhum substituto UTF-16 vai acabar procurando por qualquer coisa que esteja procurando.

Como muitos dos outros pontos, sua desconcertante vergonha sobre os nomes dos pontos de código U+2F e U+5C não lhes dá confiança alguma. Para o registro:

  / 47 002F SOLIDUS = slash, virgule x (latin letter dental click - 01C0) x (combining long solidus overlay - 0338) x (fraction slash - 2044) x (division slash - 2215) \ 92 005C REVERSE SOLIDUS = backslash x (combining reverse solidus overlay - 20E5) x (set minus - 2216) 

A solução

Então, esta manhã, finalmente me cansei de não conseguir ler em cordas com escapes embutidos nelas. Eu precisava disso para escrever o conjunto de testes para um projeto maior e mais intersting: converter de forma transparente as indefensáveis ​​expressões regulares do Java indefensáveis ​​em Unicode em versões onde você pode usar todos os \w , \W , \s , \S , \v , \V , \h , \H , \d , \D , \b , \B , \X e \R nos seus padrões e faça com que eles funcionem corretamente com o Unicode. Tudo que faço é rewrite a string padrão; ele ainda compila com a function padrão java.util.regex.Pattern.compile() , então tudo funciona como esperado. O unescaper de string intencionalmente passa qualquer \b por inalterado, no caso de você chamá-lo antes de chamar a function de conversor para tornar o Java regexes compatível com Unicode, já que ele tem que lidar com \b no sentido de limite.

De qualquer forma, aqui está a string unescaper, que apesar de ser menos interessante, resolve a questão do OP sem todas as irritações do código do Apache. Ele poderia lidar com um pouco de aperto em alguns lugares, mas eu rapidamente o cortei algumas horas antes do almoço apenas para colocá-lo em prática para ajudar a conduzir a suíte de testes. A outra function é muito mais trabalho: aquela me levou o dia todo ontem, droga.

 /* * * unescape_perl_string() * * Tom Christiansen  * Sun Nov 28 12:55:24 MST 2010 * * It's completely ridiculous that there's no standard * unescape_java_string function. Since I have to do the * damn thing myself, I might as well make it halfway useful * by supporting things Java was too stupid to consider in * strings: * * => "?" items are additions to Java string escapes * but normal in Java regexes * * => "!" items are also additions to Java regex escapes * * Standard singletons: ?\a ?\e \f \n \r \t * * NB: \b is unsupported as backspace so it can pass-through * to the regex translator untouched; I refuse to make anyone * doublebackslash it as doublebackslashing is a Java idiocy * I desperately wish would die out. There are plenty of * other ways to write it: * * \cH, \12, \012, \x08 \x{8}, \u0008, \U00000008 * * Octal escapes: \0 \0N \0NN \N \NN \NNN * Can range up to !\777 not \377 * * TODO: add !\o{NNNNN} * last Unicode is 4177777 * maxint is 37777777777 * * Control chars: ?\cX * Means: ord(X) ^ ord('@') * * Old hex escapes: \xXX * unbraced must be 2 xdigits * * Perl hex escapes: !\x{XXX} braced may be 1-8 xdigits * NB: proper Unicode never needs more than 6, as highest * valid codepoint is 0x10FFFF, not maxint 0xFFFFFFFF * * Lame Java escape: \[IDIOT JAVA PREPROCESSOR]uXXXX must be * exactly 4 xdigits; * * I can't write XXXX in this comment where it belongs * because the damned Java Preprocessor can't mind its * own business. Idiots! * * Lame Python escape: !\UXXXXXXXX must be exactly 8 xdigits * * TODO: Perl translation escapes: \Q \U \L \E \[IDIOT JAVA PREPROCESSOR]u \l * These are not so important to cover if you're passing the * result to Pattern.compile(), since it handles them for you * further downstream. Hm, what about \[IDIOT JAVA PREPROCESSOR]u? * */ public final static String unescape_perl_string(String oldstr) { /* * In contrast to fixing Java's broken regex charclasss, * this one need be no bigger, as unescaping shrinks the string * here, where in the other one, it grows it. */ StringBuffer newstr = new StringBuffer(oldstr.length()); boolean saw_backslash = false; for (int i = 0; i < oldstr.length(); i++) { int cp = oldstr.codePointAt(i); if (oldstr.codePointAt(i) > Character.MAX_VALUE) { i++; /****WE HATES UTF-16! WE HATES IT FOREVERSES!!!****/ } if (!saw_backslash) { if (cp == '\\') { saw_backslash = true; } else { newstr.append(Character.toChars(cp)); } continue; /* switch */ } if (cp == '\\') { saw_backslash = false; newstr.append('\\'); newstr.append('\\'); continue; /* switch */ } switch (cp) { case 'r': newstr.append('\r'); break; /* switch */ case 'n': newstr.append('\n'); break; /* switch */ case 'f': newstr.append('\f'); break; /* switch */ /* PASS a \b THROUGH!! */ case 'b': newstr.append("\\b"); break; /* switch */ case 't': newstr.append('\t'); break; /* switch */ case 'a': newstr.append('\007'); break; /* switch */ case 'e': newstr.append('\033'); break; /* switch */ /* * A "control" character is what you get when you xor its * codepoint with '@'==64. This only makes sense for ASCII, * and may not yield a "control" character after all. * * Strange but true: "\c{" is ";", "\c}" is "=", etc. */ case 'c': { if (++i == oldstr.length()) { die("trailing \\c"); } cp = oldstr.codePointAt(i); /* * don't need to grok surrogates, as next line blows them up */ if (cp > 0x7f) { die("expected ASCII after \\c"); } newstr.append(Character.toChars(cp ^ 64)); break; /* switch */ } case '8': case '9': die("illegal octal digit"); /* NOTREACHED */ /* * may be 0 to 2 octal digits following this one * so back up one for fallthrough to next case; * unread this digit and fall through to next case. */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': --i; /* FALLTHROUGH */ /* * Can have 0, 1, or 2 octal digits following a 0 * this permits larger values than octal 377, up to * octal 777. */ case '0': { if (i+1 == oldstr.length()) { /* found \0 at end of string */ newstr.append(Character.toChars(0)); break; /* switch */ } i++; int digits = 0; int j; for (j = 0; j < = 2; j++) { if (i+j == oldstr.length()) { break; /* for */ } /* safe because will unread surrogate */ int ch = oldstr.charAt(i+j); if (ch < '0' || ch > '7') { break; /* for */ } digits++; } if (digits == 0) { --i; newstr.append('\0'); break; /* switch */ } int value = 0; try { value = Integer.parseInt( oldstr.substring(i, i+digits), 8); } catch (NumberFormatException nfe) { die("invalid octal value for \\0 escape"); } newstr.append(Character.toChars(value)); i += digits-1; break; /* switch */ } /* end case '0' */ case 'x': { if (i+2 > oldstr.length()) { die("string too short for \\x escape"); } i++; boolean saw_brace = false; if (oldstr.charAt(i) == '{') { /* ^^^^^^ ok to ignore surrogates here */ i++; saw_brace = true; } int j; for (j = 0; j < 8; j++) { if (!saw_brace && j == 2) { break; /* for */ } /* * ASCII test also catches surrogates */ int ch = oldstr.charAt(i+j); if (ch > 127) { die("illegal non-ASCII hex digit in \\x escape"); } if (saw_brace && ch == '}') { break; /* for */ } if (! ( (ch >= '0' && ch < = '9') || (ch >= 'a' && ch < = 'f') || (ch >= 'A' && ch < = 'F') ) ) { die(String.format( "illegal hex digit #%d '%c' in \\x", ch, ch)); } } if (j == 0) { die("empty braces in \\x{} escape"); } int value = 0; try { value = Integer.parseInt(oldstr.substring(i, i+j), 16); } catch (NumberFormatException nfe) { die("invalid hex value for \\x escape"); } newstr.append(Character.toChars(value)); if (saw_brace) { j++; } i += j-1; break; /* switch */ } case 'u': { if (i+4 > oldstr.length()) { die("string too short for \\u escape"); } i++; int j; for (j = 0; j < 4; j++) { /* this also handles the surrogate issue */ if (oldstr.charAt(i+j) > 127) { die("illegal non-ASCII hex digit in \\u escape"); } } int value = 0; try { value = Integer.parseInt( oldstr.substring(i, i+j), 16); } catch (NumberFormatException nfe) { die("invalid hex value for \\u escape"); } newstr.append(Character.toChars(value)); i += j-1; break; /* switch */ } case 'U': { if (i+8 > oldstr.length()) { die("string too short for \\U escape"); } i++; int j; for (j = 0; j < 8; j++) { /* this also handles the surrogate issue */ if (oldstr.charAt(i+j) > 127) { die("illegal non-ASCII hex digit in \\U escape"); } } int value = 0; try { value = Integer.parseInt(oldstr.substring(i, i+j), 16); } catch (NumberFormatException nfe) { die("invalid hex value for \\U escape"); } newstr.append(Character.toChars(value)); i += j-1; break; /* switch */ } default: newstr.append('\\'); newstr.append(Character.toChars(cp)); /* * say(String.format( * "DEFAULT unrecognized escape %c passed through", * cp)); */ break; /* switch */ } saw_backslash = false; } /* weird to leave one at the end */ if (saw_backslash) { newstr.append('\\'); } return newstr.toString(); } /* * Return a string "U+XX.XXX.XXXX" etc, where each XX set is the * xdigits of the logical Unicode code point. No bloody brain-damaged * UTF-16 surrogate crap, just true logical characters. */ public final static String uniplus(String s) { if (s.length() == 0) { return ""; } /* This is just the minimum; sb will grow as needed. */ StringBuffer sb = new StringBuffer(2 + 3 * s.length()); sb.append("U+"); for (int i = 0; i < s.length(); i++) { sb.append(String.format("%X", s.codePointAt(i))); if (s.codePointAt(i) > Character.MAX_VALUE) { i++; /****WE HATES UTF-16! WE HATES IT FOREVERSES!!!****/ } if (i+1 < s.length()) { sb.append("."); } } return sb.toString(); } private static final void die(String foa) { throw new IllegalArgumentException(foa); } private static final void say(String what) { System.out.println(what); } 

Como alguém pode ver claramente a partir do código Java acima, eu sou realmente um programador C - Java é tudo menos minha linguagem favorita. Eu tenho medo que eu realmente tenha que ficar do lado de Rob Pike em sua famosa falsa conversa pública sobre este assunto.

Nuff disse.

De qualquer forma, é apenas um hackery rápido de manhã, mas se ajudar os outros, você é bem-vindo a ele - sem amarras. Se você melhorar, eu adoraria que você me enviasse suas melhorias, mas você certamente não precisa.

Você pode usar o método String unescapeJava(String) de StringEscapeUtils do Apache Commons Lang .

Aqui está um trecho de exemplo:

  String in = "a\\tb\\n\\\"c\\\""; System.out.println(in); // a\tb\n\"c\" String out = StringEscapeUtils.unescapeJava(in); System.out.println(out); // ab // "c" 

A class utility tem methods para escaping e anular as strings para Java, Java Script, HTML, XML e SQL. Também possui sobrecargas que gravam diretamente em um java.io.Writer .


Ressalvas

Parece que o StringEscapeUtils manipula escape Unicode com um u , mas não escapa octal, ou o Unicode escapa com utes externos.

  /* Unicode escape test #1: PASS */ System.out.println( "\u0030" ); // 0 System.out.println( StringEscapeUtils.unescapeJava("\\u0030") ); // 0 System.out.println( "\u0030".equals(StringEscapeUtils.unescapeJava("\\u0030")) ); // true /* Octal escape test: FAIL */ System.out.println( "\45" ); // % System.out.println( StringEscapeUtils.unescapeJava("\\45") ); // 45 System.out.println( "\45".equals(StringEscapeUtils.unescapeJava("\\45")) ); // false /* Unicode escape test #2: FAIL */ System.out.println( "\uu0030" ); // 0 System.out.println( StringEscapeUtils.unescapeJava("\\uu0030") ); // throws NestableRuntimeException: // Unable to parse unicode value: u003 

Uma citação do JLS:

As saídas octais são fornecidas para compatibilidade com C, mas podem expressar apenas valores Unicode \u0000 a \u00FF , portanto, as escapes Unicode são geralmente preferidas.

Se a sua string puder conter escapes octal, você pode convertê-los para escapes Unicode primeiro ou usar outra abordagem.

O extrínseco u também é documentado da seguinte forma:

A linguagem de programação Java especifica uma maneira padrão de transformar um programa escrito em Unicode em ASCII que altera um programa em um formato que pode ser processado por ferramentas baseadas em ASCII. A transformação envolve a conversão de quaisquer escapes Unicode no texto de origem do programa para ASCII adicionando um extra u – por exemplo, \uxxxx se torna \uuxxxx – enquanto simultaneamente convertendo caracteres não ASCII no texto de origem para escapes Unicode contendo um único u cada .

Essa versão transformada é igualmente aceitável para um compilador da linguagem de programação Java e representa exatamente o mesmo programa. A origem exata do Unicode pode ser restaurada posteriormente a partir desse formato ASCII convertendo cada seqüência de escape onde vários u ‘s estão presentes em uma seqüência de caracteres Unicode com menos u , enquanto convertendo simultaneamente cada seqüência de escape com um único u para o Unicode correspondente personagem.

Se sua string puder conter escape Unicode com u StringEscapeUtils , talvez seja necessário pré-processar isso antes de usar StringEscapeUtils .

Alternativamente, você pode tentar escrever seu próprio unescaper literal de string Java do zero, certificando-se de seguir as especificações exatas do JLS.

Referências

  • JLS 3.3 Escape Unicode
  • JLS 3.10.6 Sequências de Escape para Caracteres e Literais de Seqüências

Deparei com um problema semelhante, também não fiquei satisfeito com as soluções apresentadas e implementei essa solução sozinho.

Também disponível como um Gist no Github :

 /** * Unescapes a string that contains standard Java escape sequences. * 
    *
  • \b \f \n \r \t \" \' : * BS, FF, NL, CR, TAB, double and single quote.
  • *
  • \X \XX \XXX : Octal character * specification (0 - 377, 0x00 - 0xFF).
  • *
  • \uXXXX : Hexadecimal based Unicode character.
  • *
* * @param st * A string optionally containing standard java escape sequences. * @return The translated string. */ public String unescapeJavaString(String st) { StringBuilder sb = new StringBuilder(st.length()); for (int i = 0; i < st.length(); i++) { char ch = st.charAt(i); if (ch == '\\') { char nextChar = (i == st.length() - 1) ? '\\' : st .charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar < = '7') { String code = "" + nextChar; i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) < = '7') { code += st.charAt(i + 1); i++; if ((i < st.length() - 1) && st.charAt(i + 1) >= '0' && st.charAt(i + 1) < = '7') { code += st.charAt(i + 1); i++; } } sb.append((char) Integer.parseInt(code, 8)); continue; } switch (nextChar) { case '\\': ch = '\\'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case '\"': ch = '\"'; break; case '\'': ch = '\''; break; // Hex Unicode: u???? case 'u': if (i >= st.length() - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + st.charAt(i + 2) + st.charAt(i + 3) + st.charAt(i + 4) + st.charAt(i + 5), 16); sb.append(Character.toChars(code)); i += 5; continue; } i++; } sb.append(ch); } return sb.toString(); }

Eu sei que essa questão era antiga, mas eu queria uma solução que não envolvesse bibliotecas fora daquelas incluídas no JRE6 (ou seja, o Apache Commons não é aceitável), e criei uma solução simples usando o java.io.StreamTokenizer incorporado:

 import java.io.*; // ... String literal = "\"Has \\\"\\\\\\\t\\\" & isn\\\'t \\\r\\\n on 1 line.\""; StreamTokenizer parser = new StreamTokenizer(new StringReader(literal)); String result; try { parser.nextToken(); if (parser.ttype == '"') { result = parser.sval; } else { result = "ERROR!"; } } catch (IOException e) { result = e.toString(); } System.out.println(result); 

Saída:

 Has "\ " & isn't on 1 line. 

Veja isso em http://commons.apache.org/lang/ :

StringEscapeUtils

StringEscapeUtils.unescapeJava(String str)

Estou um pouco atrasado nisso, mas pensei em fornecer minha solução, pois precisava da mesma funcionalidade. Decidi usar a API do Compilador Java, o que a torna mais lenta, mas torna os resultados precisos. Basicamente eu vivo criar uma class, em seguida, retornar os resultados. Aqui está o método:

 public static String[] unescapeJavaStrings(String... escaped) { //class name final String className = "Temp" + System.currentTimeMillis(); //build the source final StringBuilder source = new StringBuilder(100 + escaped.length * 20). append("public class ").append(className).append("{\n"). append("\tpublic static String[] getStrings() {\n"). append("\t\treturn new String[] {\n"); for (String string : escaped) { source.append("\t\t\t\""); //we escape non-escaped quotes here to be safe // (but something like \\" will fail, oh well for now) for (int i = 0; i < string.length(); i++) { char chr = string.charAt(i); if (chr == '"' && i > 0 && string.charAt(i - 1) != '\\') { source.append('\\'); } source.append(chr); } source.append("\",\n"); } source.append("\t\t};\n\t}\n}\n"); //obtain compiler final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); //local stream for output final ByteArrayOutputStream out = new ByteArrayOutputStream(); //local stream for error ByteArrayOutputStream err = new ByteArrayOutputStream(); //source file JavaFileObject sourceFile = new SimpleJavaFileObject( URI.create("string:///" + className + Kind.SOURCE.extension), Kind.SOURCE) { @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return source; } }; //target file final JavaFileObject targetFile = new SimpleJavaFileObject( URI.create("string:///" + className + Kind.CLASS.extension), Kind.CLASS) { @Override public OutputStream openOutputStream() throws IOException { return out; } }; //file manager proxy, with most parts delegated to the standard one JavaFileManager fileManagerProxy = (JavaFileManager) Proxy.newProxyInstance( StringUtils.class.getClassLoader(), new Class[] { JavaFileManager.class }, new InvocationHandler() { //standard file manager to delegate to private final JavaFileManager standard = compiler.getStandardFileManager(null, null, null); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("getJavaFileForOutput".equals(method.getName())) { //return the target file when it's asking for output return targetFile; } else { return method.invoke(standard, args); } } }); //create the task CompilationTask task = compiler.getTask(new OutputStreamWriter(err), fileManagerProxy, null, null, null, Collections.singleton(sourceFile)); //call it if (!task.call()) { throw new RuntimeException("Compilation failed, output:\n" + new String(err.toByteArray())); } //get the result final byte[] bytes = out.toByteArray(); //load class Class< ?> clazz; try { //custom class loader for garbage collection clazz = new ClassLoader() { protected Class< ?> findClass(String name) throws ClassNotFoundException { if (name.equals(className)) { return defineClass(className, bytes, 0, bytes.length); } else { return super.findClass(name); } } }.loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } //reflectively call method try { return (String[]) clazz.getDeclaredMethod("getStrings").invoke(null); } catch (Exception e) { throw new RuntimeException(e); } } 

É preciso uma matriz para que você possa descontinuar em lotes. Então, o seguinte teste simples é bem-sucedido:

 public static void main(String[] meh) { if ("1\02\03\n".equals(unescapeJavaStrings("1\\02\\03\\n")[0])) { System.out.println("Success"); } else { System.out.println("Failure"); } } 

Eu me deparei com o mesmo problema, mas não fiquei encantado com nenhuma das soluções que encontrei aqui. Então, eu escrevi um que itera sobre os caracteres da string usando um matcher para encontrar e replace as seqüências de escape. Essa solução pressupõe input formatada corretamente. Ou seja, pula alegremente sobre escapes sem sentido, e decodifica escapes Unicode para alimentação de linha e retorno de carro (que não podem aparecer em um literal de caractere ou string literal, devido à definição de tais literais e à ordem das fases de tradução para Java fonte). Desculpas, o código é um pouco embalado por brevidade.

 import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Decoder { // The encoded character of each character escape. // This array functions as the keys of a sorted map, from encoded characters to decoded characters. static final char[] ENCODED_ESCAPES = { '\"', '\'', '\\', 'b', 'f', 'n', 'r', 't' }; // The decoded character of each character escape. // This array functions as the values of a sorted map, from encoded characters to decoded characters. static final char[] DECODED_ESCAPES = { '\"', '\'', '\\', '\b', '\f', '\n', '\r', '\t' }; // A pattern that matches an escape. // What follows the escape indicator is captured by group 1=character 2=octal 3=Unicode. static final Pattern PATTERN = Pattern.compile("\\\\(?:(b|t|n|f|r|\\\"|\\\'|\\\\)|((?:[0-3]?[0-7])?[0-7])|u+(\\p{XDigit}{4}))"); public static CharSequence decodeString(CharSequence encodedString) { Matcher matcher = PATTERN.matcher(encodedString); StringBuffer decodedString = new StringBuffer(); // Find each escape of the encoded string in succession. while (matcher.find()) { char ch; if (matcher.start(1) >= 0) { // Decode a character escape. ch = DECODED_ESCAPES[Arrays.binarySearch(ENCODED_ESCAPES, matcher.group(1).charAt(0))]; } else if (matcher.start(2) >= 0) { // Decode an octal escape. ch = (char)(Integer.parseInt(matcher.group(2), 8)); } else /* if (matcher.start(3) >= 0) */ { // Decode a Unicode escape. ch = (char)(Integer.parseInt(matcher.group(3), 16)); } // Replace the escape with the decoded character. matcher.appendReplacement(decodedString, Matcher.quoteReplacement(String.valueOf(ch))); } // Append the remainder of the encoded string to the decoded string. // The remainder is the longest suffix of the encoded string such that the suffix contains no escapes. matcher.appendTail(decodedString); return decodedString; } public static void main(String... args) { System.out.println(decodeString(args[0])); } } 

Devo observar que o Apache Commons Lang3 não parece sofrer as fraquezas indicadas na solução aceita. Ou seja, StringEscapeUtils parece manipular escapes octal e vários caracteres u de escapes Unicode. Isso significa que, a menos que você tenha algum motivo para evitar o Apache Commons, provavelmente deve usá-lo em vez da minha solução (ou qualquer outra solução aqui).

Para o registro, se você usar Scala, você pode fazer:

 StringContext.treatEscapes(escaped) 

org.apache.commons.lang3.StringEscapeUtils de commons-lang3 está marcado como obsoleto agora. Você pode usar org.apache.commons.text.StringEscapeUtils#unescapeJava(String) . Requer uma dependência adicional do Maven :

   org.apache.commons commons-text 1.4  

e parece lidar com alguns casos mais especiais, por exemplo, unescapes:

  • escapou de barras invertidas, aspas simples e duplas
  • valores octal e unicode com escape
  • \\b , \\n , \\t , \\f , \\r

Se você estiver lendo chars com escape unicode de um arquivo, então você terá dificuldades para fazer isso porque a string será lida literalmente junto com um escape para a barra invertida:

my_file.txt

 Blah blah... Column delimiter=; Word delimiter=\u0020 #This is just unicode for whitespace .. more stuff 

Aqui, quando você ler a linha 3 do arquivo, a string / linha terá:

 "Word delimiter=\u0020 #This is just unicode for whitespace" 

e o char [] na string mostrará:

 {...., '=', '\\', 'u', '0', '0', '2', '0', ' ', '#', 't', 'h', ...} 

O Commons StringUnescape não irá tirar isso do seu lugar (tentei unescapeXml ()). Você terá que fazer isso manualmente conforme descrito aqui .

Portanto, a subcadeia “\ u0020” deve se tornar um único caractere ‘\ u0020’

Mas se você estiver usando este “\ u0020” para fazer String.split("... ..... ..", columnDelimiterReadFromFile) que está realmente usando o regex internamente, ele funcionará diretamente porque a string lida do arquivo foi escapada e é perfeito para usar no padrão regex !! (Confuso?)

Você pode querer dar uma olhada na implementação do Eclipse de Stringliteral .