una - unescape json java
¿Cómo deshacerse de un literal de cadena de Java en Java? (11)
Estoy procesando un código fuente de Java usando Java. Estoy extrayendo los literales de cadena y alimentándolos a una función tomando una cadena. El problema es que necesito pasar la versión no guardada de String a la función (es decir, esto significa convertir /n
a una nueva línea, y //
a una sola /
, etc.).
¿Hay una función dentro de la API de Java que hace esto? Si no, ¿puedo obtener dicha funcionalidad de alguna biblioteca? Obviamente, el compilador de Java tiene que hacer esta conversión.
PD : en caso de que alguien quiera saber: estoy tratando de desencriptar los literales de cadenas en los archivos ocultos de Java ofuscados
Encontré el mismo problema, pero no me enamoré de ninguna de las soluciones que encontré aquí. Entonces, escribí uno que itera sobre los caracteres de la cadena usando un matcher para encontrar y reemplazar las secuencias de escape. Esta solución asume una entrada con formato correcto. Es decir, se saltea sin pensar en escapes sin sentido, y decodifica escapes Unicode para avance de línea y retorno de carro (que de otra manera no pueden aparecer en un literal de caracteres o literal de cadena, debido a la definición de tales literales y el orden de las fases de traducción para Java fuente). Disculpas, el código está un poco empaquetado por brevedad.
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]));
}
}
Debo señalar que Apache Commons Lang3 no parece sufrir las debilidades indicadas en la solución aceptada. Es decir, StringEscapeUtils
parece manejar escapes oculares y múltiples u
caracteres de escapes Unicode. Eso significa que a menos que tengas alguna razón ardiente para evitar Apache Commons, probablemente deberías usarlo en lugar de mi solución (o cualquier otra solución aquí).
Encontré un problema similar, no estaba satisfecho con las soluciones presentadas y lo implementé yo mismo.
También disponible como Gist en Github :
/**
* Unescapes a string that contains standard Java escape sequences.
* <ul>
* <li><strong>\b \f \n \r \t \" \''</strong> :
* BS, FF, NL, CR, TAB, double and single quote.</li>
* <li><strong>\X \XX \XXX</strong> : Octal character
* specification (0 - 377, 0x00 - 0xFF).</li>
* <li><strong>\uXXXX</strong> : Hexadecimal based Unicode character.</li>
* </ul>
*
* @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();
}
Es posible que desee echar un vistazo a la implementación de Eclipse de Stringliteral .
Llegué un poco tarde a esto, pero pensé en proporcionar mi solución porque necesitaba la misma funcionalidad. Decidí usar la API del compilador de Java, que lo hace más lento, pero hace que los resultados sean precisos. Básicamente, vivo creando una clase y luego devuelvo los resultados. Este es el 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);
}
}
Se necesita una matriz para poder realizar unescape en lotes. Entonces, la siguiente prueba simple tiene éxito:
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");
}
}
Para el registro, si usa Scala, puede hacer:
StringContext.treatEscapes(escaped)
Puede utilizar String unescapeJava(String)
método String unescapeJava(String)
de StringEscapeUtils
de Apache Commons Lang .
Aquí hay un fragmento de ejemplo:
String in = "a//tb//n///"c///"";
System.out.println(in);
// a/tb/n/"c/"
String out = StringEscapeUtils.unescapeJava(in);
System.out.println(out);
// a b
// "c"
La clase de utilidad tiene métodos para escapes y desaparición de cadenas para Java, Java Script, HTML, XML y SQL. También tiene sobrecargas que escribe directamente en java.io.Writer
.
Advertencias
Parece que StringEscapeUtils
maneja los escapes Unicode con un u
, pero no con los escapes o Unicode escapa con los extraños.
/* 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
Una cita de JLS:
Los escapes octales se proporcionan para compatibilidad con C, pero solo pueden expresar valores Unicode
/u0000
través de/u00FF
, por lo que generalmente se prefieren los escapes Unicode.
Si su cadena puede contener escapes octales, es posible que desee convertirlos a escapes Unicode primero o utilizar otro enfoque.
El u
extraño también está documentado de la siguiente manera:
El lenguaje de programación de Java especifica una forma estándar de transformar un programa escrito en Unicode en ASCII que cambia un programa en un formulario que puede ser procesado por herramientas basadas en ASCII. La transformación implica convertir cualquier escape Unicode en el texto fuente del programa a ASCII añadiendo un
u
adicional; por ejemplo,/uxxxx
convierte en/uuxxxx
mientras convierte simultáneamente caracteres que no son ASCII en el texto fuente en escapes Unicode que contienen un único u cada .Esta versión transformada es igualmente aceptable para un compilador del lenguaje de programación Java y representa exactamente el mismo programa. La fuente Unicode exacta puede recuperarse más tarde de esta forma ASCII convirtiendo cada secuencia de escape donde hay múltiples
u
presentes en una secuencia de caracteres Unicode con unau
menor, mientras simultáneamente convierte cada secuencia de escape con una solau
en el único Unicode correspondiente personaje.
Si su cadena puede contener escapes Unicode con u
extraña, entonces es posible que también deba preprocesar esto antes de utilizar StringEscapeUtils
.
Alternativamente, puede intentar escribir su propio descifrado literal de cadena de Java desde cero, asegurándose de seguir las especificaciones JLS exactas.
Referencias
Sé que esta pregunta era antigua, pero quería una solución que no implique bibliotecas fuera de las incluidas JRE6 (es decir, Apache Commons no es aceptable), y se me ocurrió una solución simple usando el built-in java.io.StreamTokenizer
:
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);
Salida:
Has "/ " & isn''t
on 1 line.
Si está leyendo unicode caracteres escapados de un archivo, tendrá dificultades para hacerlo porque la cadena se leerá literalmente junto con un escape para la barra diagonal inversa:
my_file.txt
Blah blah...
Column delimiter=;
Word delimiter=/u0020 #This is just unicode for whitespace
.. more stuff
Aquí, cuando leas la línea 3 del archivo, la cadena / línea tendrá:
"Word delimiter=/u0020 #This is just unicode for whitespace"
y el char [] en la cadena mostrará:
{...., ''='', ''//', ''u'', ''0'', ''0'', ''2'', ''0'', '' '', ''#'', ''t'', ''h'', ...}
Commons StringUnescape no liberará esto para usted (intenté unescapeXml ()). Tendrás que hacerlo manualmente como se describe aquí .
Por lo tanto, la subcadena "/ u0020" debería convertirse en 1 sola char ''/ u0020''
Pero si está utilizando este "/ u0020" para hacer String.split("... ..... ..", columnDelimiterReadFromFile)
que realmente está usando regex internamente, funcionará directamente porque se escapó la cadena leída del archivo ¡y es perfecto para usar en el patrón de expresiones regulares! (¿Confuso?)
Vea esto de http://commons.apache.org/lang/ :
StringEscapeUtils.unescapeJava(String str)
org.apache.commons.lang3.StringEscapeUtils
de commons-lang3 está marcado como obsoleto ahora. Puede usar org.apache.commons.text.StringEscapeUtils#unescapeJava(String)
lugar. Requiere una dependencia adicional de Maven :
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.4</version>
</dependency>
y parece manejar algunos casos más especiales, por ejemplo, unescapes:
- escapes de barras invertidas, comillas simples y dobles
- escapó valores octal y unicode
-
//b
,//n
,//t
,//f
,//r
EDITAR: Puede descargar la fuente completa para la función que analizo a continuación. También lo discuto con más detalle en esta respuesta .
El problema
El org.apache.commons.lang.StringEscapeUtils.unescapeJava()
dado aquí como "la respuesta" es realmente muy poca ayuda en absoluto.
- Tienes que prever cargar otro archivo jar descomunal con cargas de crumble que no necesitas o no quieres.
- Tiene una licencia Algunas personas no quieren preocuparse por una licencia, sin importar cuán buena o mala sea realmente.
- Se olvida de
/0
para null. - No maneja octal en absoluto .
- No puede manejar el tipo de escapes admitidos por
java.util.regex.Pattern.compile()
y todo lo que lo usa, incluidos/a
,/e
, y especialmente/cX
. - No tiene soporte para puntos de código lógico Unicode por número, solo para el idiota daño cerebral UTF-16.
- Está escrito por un maldito idiota que ni siquiera sabe la diferencia entre una barra inclinada y una barra invertida .
- El código fuente está lleno de retornos de carro molestos.
- Está escrito para tomar un argumento de
writer
, por lo que si no lo pasa, todavía tiene que crear unStringWriter
ficticio para la salida, luegoStringWriter
para que se lo devuelva. - Parece un código UCS-2, no un código UTF-16: usan la interfaz
charAt
depreciada en lugar de la interfazcodePoint
, lo que promulga el delirio de que unchar
Java está garantizado para contener un carácter Unicode. No es. Solo se salvan con esta ceguera a los aviones astrales porque ningún sustituto UTF-16 terminará buscando lo que están buscando.
Al igual que muchos de los otros puntos, su vergonzosa ignorancia sobre los nombres de los puntos de código U+2F
y U+5C
no les inspira confianza en absoluto. Para el 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)
La solución
Así que esta mañana finalmente me cansé de no poder leer en cuerdas con escapes incrustados en ellos. Lo necesitaba para escribir el banco de pruebas para un proyecto más grande y más interesante: la conversión transparente de las expresiones regulares de ignorancia desconocida de Unicode de Java en versiones donde se pueden usar todas las /w
, /W
, /s
, /S
, /v
, /V
, /h
, /H
, /d
, /D
, /b
, /B
, /X
y /R
en sus patrones y que realmente funcionen correctamente con Unicode. Todo lo que hago es reescribir la cadena del patrón; todavía compila con la función java.util.regex.Pattern.compile()
estándar, por lo que todo funciona como se esperaba. El desencadenador de cadena pasa intencionalmente cualquier /b
sin tocar, en caso de que lo llame antes de llamar a la función del convertidor para hacer expresiones regulares de Java con reconocimiento Unicode, ya que eso tiene que tratar con /b
en el sentido del límite.
De todos modos, aquí está el string unescaper, que aunque el menos interesante del par, resuelve la pregunta del OP sin todas las irritaciones del código Apache. Podría manejar un poco el ajuste en un par de lugares, pero rápidamente lo pirateé unas pocas horas antes del almuerzo para ponerlo en marcha y ayudarlo a manejar el banco de pruebas. La otra función es mucho más trabajo: esa me llevó todo el día de ayer, maldita sea.
/*
*
* unescape_perl_string()
*
* Tom Christiansen <[email protected]>
* 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 charclasses,
* 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 cualquiera puede ver claramente desde el código de Java anterior, soy realmente un programador de C: Java es todo menos mi idioma favorito. Me temo que realmente tengo que ponerme de parte de Rob Pike en su famosa charla vacía sobre este tema.
''Dijo Nuff.
De todos modos, es solo una hacker de la mañana rápida, pero si ayuda a otros, eres bienvenido, sin condiciones. Si lo mejora, me encantaría que me envíe sus mejoras, pero ciertamente no es necesario.