如何在 Java 中取消转义 Java 字符串文字?

     2023-02-26     192

关键词:

【中文标题】如何在 Java 中取消转义 Java 字符串文字?【英文标题】:How to unescape a Java string literal in Java? 【发布时间】:2011-04-02 01:11:48 【问题描述】:

我正在使用 Java 处理一些 Java 源代码。我正在提取字符串文字并将它们提供给采用字符串的函数。问题是我需要将字符串的未转义版本传递给函数(即,这意味着将\n 转换为换行符,将\\ 转换为单个\,等等)。

Java API 中是否有执行此操作的函数?如果没有,我可以从某个库中获得这样的功能吗?显然,Java 编译器必须进行这种转换。

【问题讨论】:

【参考方案1】:

Java 13 添加了一个执行此操作的方法:String#translateEscapes

它是 Java 13 和 14 中的预览功能,但在 Java 15 中被提升为完整功能。

【讨论】:

Nooo,但至少它会出现在 Java 17 LTS 中。 这是正确答案。投票更高,伙计们!【参考方案2】:

问题

这里作为另一个答案给出的org.apache.commons.lang.StringEscapeUtils.unescapeJava() 真的没什么帮助。

它忘记了 \0 为 null。 它根本不处理八进制。 它无法处理java.util.regex.Pattern.compile() 和使用它的所有东西所承认的各种转义,包括\a\e,尤其是\cX。 它不支持按数字排列的逻辑 Unicode 代码点,仅支持 UTF-16。 这看起来像 UCS-2 代码,而不是 UTF-16 代码:它们使用已弃用的 charAt 接口而不是 codePoint 接口,因此传播了 Java 的 char 保证包含 Unicode 字符的错觉.它不是。他们只是侥幸逃脱,因为没有 UTF-16 代理最终会寻找他们正在寻找的任何东西。

解决方案

我写了一个字符串 unescaper,它解决了 OP 的问题,而没有 Apache 代码的所有烦恼。

/*
 *
 * unescape_perl_string()
 *
 *      Tom Christiansen <tchrist@perl.com>
 *      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 \x8, \u0008, \U00000008
 *
 * Octal escapes: \0 \0N \0NN \N \NN \NNN
 *    Can range up to !\777 not \377
 *    
 *      TODO: add !\oNNNNN
 *          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: !\xXXX 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);

如果它对其他人有所帮助,欢迎您使用它——不附加任何条件。如果你改进它,我希望你把你的改进寄给我,但你当然不必这样做。

【讨论】:

为什么你的例程叫unescape_perl_string?此外,由于java本身不会以这种方式解释文字,因此对规范未定义的事物进行所有额外的转义不是一个错误吗?只要确保我在这里没有遗漏任何东西 - 代码足够复杂,我有点担心所有额外的位。 @tchrist 你知道你用 Apache 的方法描述的问题是否仍然有效,或者他们是否修复了它? @tchrist 谢谢。我刚刚使用包含foo\\bar 的文本文件尝试了您的简洁方法,它返回了foo\\bar。我本来希望它是foo\bar。这是一个错误还是我误解了该方法背后的想法? @sjngm 正如所写的那样,它确实打算对无法识别的反斜杠转义进行传递,而不是剥离一层,因为我发现越来越多的双、三、四等反斜线趋势是难以阅读且容易搞砸。 Apache Commons Lang3 修复了其中的一些问题(至少是八进制位,这让我很着迷)issues.apache.org/jira/browse/LANG-646【参考方案3】:

来自 commons-lang3 的org.apache.commons.lang3.StringEscapeUtils 现在被标记为已弃用。您可以改用org.apache.commons.text.StringEscapeUtils#unescapeJava(String)。它需要一个额外的Maven dependency:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.4</version>
        </dependency>

并且似乎可以处理一些更特殊的情况,例如转义:

转义的反斜杠、单引号和双引号 转义的八进制和 Unicode 值 \\b, \\n, \\t, \\f, \\r

【讨论】:

【参考方案4】:

为了记录,如果你使用 Scala,你可以这样做:

StringContext.treatEscapes(escaped)

【讨论】:

它只处理` [\b, \t, \n, \f, \r, \\, \", \']`,它不能处理unicode转义。【参考方案5】:

从http://commons.apache.org/lang/看到这个:

StringEscapeUtils

StringEscapeUtils.unescapeJava(String str)

【讨论】:

【参考方案6】:

你可以从Apache Commons Lang使用StringEscapeUtilsString unescapeJava(String)方法。

这是一个示例 sn-p:

    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"

实用程序类具有转义和取消转义 Java、Java Script、HTML、XML 和 SQL 字符串的方法。它还具有直接写入java.io.Writer 的重载。


注意事项

看起来StringEscapeUtils 使用一个u 处理Unicode 转义,但不是八进制转义,或带有无关us 的Unicode 转义。

    /* 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

来自 JLS 的引述:

提供八进制转义是为了与 C 兼容,但只能表示 Unicode 值 \u0000\u00FF,因此通常首选 Unicode 转义。

如果您的字符串可以包含八进制转义,您可能需要先将它们转换为 Unicode 转义,或者使用其他方法。

无关的u也记录如下:

Java 编程语言指定了一种将用 Unicode 编写的程序转换为 ASCII 的标准方法,该方法将程序更改为可以由基于 ASCII 的工具处理的形式。转换涉及通过添加额外的u 将程序源文本中的任何 Unicode 转义符转换为 ASCII - 例如,\uxxxx 变为 \uuxxxx - 同时将源文本中的非 ASCII 字符转换为包含以下内容的 Unicode 转义符每个人一个。

这个转换后的版本同样可以被 Java 编程语言的编译器接受,并且代表完全相同的程序。稍后可以通过将存在多个u 的每个转义序列转换为具有少一个u 的Unicode 字符序列,同时使用单个@987654343 转换每个转义序列,可以从此ASCII 形式恢复确切的Unicode 源@ 对应的单个 Unicode 字符。

如果您的字符串可以包含带有无关 u 的 Unicode 转义,那么您可能还需要在使用 StringEscapeUtils 之前对其进行预处理。

或者,您可以尝试从头开始编写自己的 Java 字符串文字转义符,确保遵循确切的 JLS 规范。

参考文献

JLS 3.3 Unicode Escapes JLS 3.10.6 Escape Sequences for Character and String Literals

【讨论】:

我也找到了这个库(发布后)。我遇到了八进制值的问题。我目前正在尝试手动转换它们。 呃,将八进制转换为 unicode 并非易事。只有 0-127 可以轻松映射。是这样吗? @ziggystar:看起来 JLS 改用 0-255(见引用)。最大的八进制转义是\377 啊,你是对的。规范说八进制是 unicode 值。我怀疑它们是 ASCII 值。 我有这个\xf3,它是西班牙语的重音。如何取消转义?【参考方案7】:

遇到了类似的问题,对提出的解决方案也不满意,自己实现了这个。

也可在Github 上作为 Gist 获得:

/**
 * Unescapes a string that contains standard Java escape sequences.
 * <ul>
 * <li><strong>&#92;b &#92;f &#92;n &#92;r &#92;t &#92;" &#92;'</strong> :
 * BS, FF, NL, CR, TAB, double and single quote.</li>
 * <li><strong>&#92;X &#92;XX &#92;XXX</strong> : Octal character
 * specification (0 - 377, 0x00 - 0xFF).</li>
 * <li><strong>&#92;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();

【讨论】:

为了格外小心,首先进行空检查,在这种情况下返回空。不过谢谢!【参考方案8】:

我知道这个问题很老,但我想要一个不涉及包含 JRE6 之外的库的解决方案(即 Apache Commons 是不可接受的),我想出了一个使用内置 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);

输出:

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

【讨论】:

@UdoKlimaschewski - 你是对的。您可以查看 at the source 以了解它实际支持哪些转义。【参考方案9】:

我遇到了同样的问题,但我对这里找到的任何解决方案都不感兴趣。所以,我写了一个使用匹配器迭代字符串的字符来查找和替换转义序列的方法。此解决方案假定输入格式正确。也就是说,它会愉快地跳过无意义的转义,并为换行和回车解码 Unicode 转义(否则,由于此类文字的定义和 Java 的翻译阶段顺序,它们不能出现在字符文字或字符串文字中资源)。抱歉,为简洁起见,代码有点压缩。

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+(\\pXDigit4))");

    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]));
    

我应该注意到 Apache Commons Lang3 似乎没有受到公认解决方案中指出的弱点。也就是说,StringEscapeUtils 似乎可以处理八进制转义和多个u Unicode 转义字符。这意味着除非你有一些迫切的理由避免使用 Apache Commons,否则你应该使用它而不是我的解决方案(或此处的任何其他解决方案)。

【讨论】:

【参考方案10】:

我在这方面有点晚了,但我想我会提供我的解决方案,因为我需要相同的功能。我决定使用 Java Compiler API,这会使其速度变慢,但可以使结果准确。基本上我生活创建一个类然后返回结果。方法如下:

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);
    

它需要一个数组,因此您可以批量取消转义。所以下面的简单测试成功了:

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");
    

【讨论】:

【参考方案11】:

如果您正在从文件中读取 unicode 转义字符,那么您将很难做到这一点,因为字符串将与反斜杠的转义一起被逐字读取:

我的文件.txt

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

.. more stuff

在这里,当您从文件中读取第 3 行时,字符串/行将具有:

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

字符串中的char[]会显示:

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

Commons StringUnescape 不会为你取消转义(我试过 unescapeXml())。您必须以 described here 的身份手动完成。

所以,子字符串 "\u0020" 应该变成 1 个单字符 '\u0020'

但是如果你使用这个 "\u0020" 来做String.split("... ..... ..", columnDelimiterReadFromFile) 这实际上是在内部使用正则表达式,它会直接工作,因为从文件中读取的字符串被转义并且非常适合在正则表达式模式中使用! (困惑?)

【讨论】:

如何使用 Java 在 PostgreSQL 中安全地转义 SQL 的任意字符串

】如何使用Java在PostgreSQL中安全地转义SQL的任意字符串【英文标题】:HowtosafelyescapearbitrarystringsforSQLinPostgreSQLusingJava【发布时间】:2012-04-0205:29:18【问题描述】:我有一个特殊情况,要求我从用户提供的输入值生成SQLWHERE子句的... 查看详情

java示例代码_使用Java中的MySQL以字符串文字形式转义单引号

java示例代码_使用Java中的MySQL以字符串文字形式转义单引号 查看详情

如何取消转义html字符串中的引号

】如何取消转义html字符串中的引号【英文标题】:Howtounescapethequotesinhtmlstring【发布时间】:2019-05-0819:30:16【问题描述】:我在Go中有一个字符串如下:Helloworld!<ahref=\\"www.google.com\\">Google</a>引号被转义了,我想得到没有... 查看详情

如何在 Java 中替换不区分大小写的文字子字符串

】如何在Java中替换不区分大小写的文字子字符串【英文标题】:Howtoreplacecase-insensitiveliteralsubstringsinJava【发布时间】:2011-06-3014:41:22【问题描述】:使用String中的replace(CharSequencetarget,CharSequencereplacement)方法,如何让目标不区分... 查看详情

BigQuery:如何取消转义额外转义/序列化的 JSON 字符串

】BigQuery:如何取消转义额外转义/序列化的JSON字符串【英文标题】:BigQuery:Howtounescapeanextra-escaped/serializedJSONstring【发布时间】:2021-12-1518:36:48【问题描述】:我正在处理一个数据集,其中从服务中提取JSON字符串,并经过额外的... 查看详情

取消转义字符串中的 unicode

】取消转义字符串中的unicode【英文标题】:Unescapeunicodeincharacterstring【发布时间】:2014-09-1702:21:35【问题描述】:RJSONIO中有一个长期存在的bug用于解析包含unicode转义序列的json字符串。似乎需要在libjson中修复该错误,这可能不... 查看详情

如何使用 Gradle 自动转义 Java 属性文件中的 unicode 字符?

】如何使用Gradle自动转义Java属性文件中的unicode字符?【英文标题】:HowtoautomaticallyescapeunicodecharactersinJavapropertyfilesusingGradle?【发布时间】:2016-10-1008:03:48【问题描述】:我正在翻译Java应用程序,方法是使用ResourceBundle和各种*.pr... 查看详情

Java是不是有'@'字符来转义字符串引号?

】Java是不是有\\\'@\\\'字符来转义字符串引号?【英文标题】:DoesJavahavethe\'@\'charactertoescapestringquotes?Java是否有\'@\'字符来转义字符串引号?【发布时间】:2011-01-0209:04:03【问题描述】:我的字符串中有双引号,C#我会这样做:stri... 查看详情

如何在 Java 中读取 % 字符

】如何在Java中读取%字符【英文标题】:Howtoread%CharacterinJava【发布时间】:2014-07-1504:25:58【问题描述】:我必须编写一个小解析应用程序,它正在重写带有一些附加信息的postscriptfile。为了达到这个目标,我只是将BufferedReader与Fi... 查看详情

java示例代码_在java中读取文件时,转义以特殊字符开头的行

java示例代码_在java中读取文件时,转义以特殊字符开头的行 查看详情

java转换html字符实体,java特殊字符转义字符串

为什么要用转义字符串?HTML中<,>,&等有特殊含义(<,>,用于链接签,&用于转义),不能直接使用。这些符号是不显示在我们最终看到的网页里的,那如果我们希望在网页中显示这些符号,该怎么办呢?这就要... 查看详情

如何在 Java 字符串中输入引号?

】如何在Java字符串中输入引号?【英文标题】:HowtoenterquotesinaJavastring?【发布时间】:2011-04-0307:27:09【问题描述】:我想在Java中初始化一个字符串,但该字符串需要包含引号;例如:"ROM"。我试着做:Stringvalue=""ROM"";但... 查看详情

java如何将特殊字符转义

...字符!!如果是正则表达式中请看如下例子:$匹配输入字符串的结尾位置。如果设置了RegExp对象的Multiline属性,则$也匹配‘\n'或‘\r'。要匹配$字符本身,请使用\$。()标记一个子表达式的开始和结束位置。子表达... 查看详情

在 .NET 中使用 XmlReader 取消转义 XML 实体?

...:2011-07-1507:53:14【问题描述】:我正在尝试在.NET(C#)中对字符串中的XML实体进行转义,但我似乎无法使其正常工作。例如,如果我有字符串AT&amp;amp;T,它应该被翻译成AT&amp;T。一种方法是使用HttpUtility.H 查看详情

在空手道 DSL 中,如何在 java 参数调用中传递变量时转义单引号

】在空手道DSL中,如何在java参数调用中传递变量时转义单引号【英文标题】:inKarateDSL,howdoyouescapesinglequoteswhenpassingavariablewithinajavaargumentcall【发布时间】:2017-11-0121:21:03【问题描述】:我正在尝试添加对数据库的调用,作为API... 查看详情

java学习prat2转义字符注释与路径(代码片段)

转义字符\\t:一个制表位,实现对其功能\\n:换行符\\\\:一个\\\\":一个"\\\':一个\'\\r:一个回车在控制台按下tab键可以实现自动补全。回车和换行是两个概念:回车为回到当前行的第一个字符;换行:跳到新一行开头。注释(comment)用... 查看详情

java中字符串如何去掉转义字符

...中unescapeJava(Strings)方法是来处理java转义字符的,可以将字符串中的“\\”转换为“\\”,“\'”转换为“\'”等。通过这个方法处理以上字符串public class Test()   public static void main(String[] args)        String s = "\\\\u79fb\\\\u52... 查看详情

如何理解嵌入在 Java Script 中的 SQL 查询中的转义

】如何理解嵌入在JavaScript中的SQL查询中的转义【英文标题】:HowtounderstandescapinginthisSQLqueryembeddedinJavaScript【发布时间】:2014-10-2722:09:16【问题描述】:varqueryString="selectreplace(B.entity,\'\'\'\',\'\\\\\\\'\'\')fromB"我假设下一行将在Oracle... 查看详情