【问题标题】:How to convert arbitrary string to Java identifier?如何将任意字符串转换为 Java 标识符?
【发布时间】:2011-11-18 10:34:53
【问题描述】:

我需要转换任意字符串:

  • 带空格的字符串
  • 100stringsstartswithnumber
  • 字符串€with%special†characters/\!
  • [空字符串]

到一个有效的 Java 标识符:

  • string_with_spaces
  • _100stringsstartswithnumber
  • string_with_special_characters___
  • _

这个任务有现成的工具吗?

有这么多 Java 源代码重构/生成框架,人们会认为这应该是相当普遍的任务。

【问题讨论】:

  • 您是否希望在运行时动态执行此操作?如果是这样,那是行不通的。你需要Map<String,Object> 或其他东西才能做到这一点。
  • @glowcoder 是的,我需要一个实用方法来在运行时执行此操作。请说明为什么我需要使用Map
  • 因为在运行时没有动态映射到变量。你能做的最好的就是使用反射。老实说,如果有,则表明设计决策不佳。你为什么想做这样的事情?通常有更好(更安全!)的方法来做到这一点。
  • @glowcoder 也许我没有足够清楚地描述我想要做的事情。我不做什么“在运行时动态映射到变量”。我要做的就是将字符串转换为另一个保证符合 Java 标识符命名规则的字符串(没有空格,不要以数字开头等)。 Elipse 的 'Extract to constant' 重构选项仅适用于字符串和数字。我正在寻找一种实用方法,我可以在我的应用中使用它来做类似的事情。
  • 我猜hibernate类ImprovedNamingStrategy做了类似的事情。

标签: java string code-generation identifier


【解决方案1】:

这个简单的方法会将任何输入字符串转换成一个有效的java标识符:

public static String getIdentifier(String str) {
    try {
        return Arrays.toString(str.getBytes("UTF-8")).replaceAll("\\D+", "_");
    } catch (UnsupportedEncodingException e) {
        // UTF-8 is always supported, but this catch is required by compiler
        return null;
    }
}

例子:

"%^&*\n()" --> "_37_94_38_42_10_56_94_40_41_"

任何输入字符都可以使用 - 外语字符、换行符等等!
另外,这个算法是:

  • 可重现
  • 独特 - 即如果str1.equals(str2),将总是产生相同的结果
  • 可逆

感谢Joachim SauerUTF-8 建议


如果冲突正常(两个输入字符串可能产生相同的结果),则此代码会产生可读的输出:

public static String getIdentifier(String str) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < str.length(); i++) {
        if ((i == 0 && Character.isJavaIdentifierStart(str.charAt(i))) || (i > 0 && Character.isJavaIdentifierPart(str.charAt(i))))
            sb.append(str.charAt(i));
        else
            sb.append((int)str.charAt(i));
    }
    return sb.toString();
}

它保留作为有效标识符的字符,仅将那些无效的字符转换为其十进制等效值。

【讨论】:

  • 如果这应该是可重现和稳定的,那么getBytes() 应该接受一个论点(我建议"UTF-8")。
  • 虽然它不是来自现有库的代码,但它是提供的代码中最优雅的和平。我接受这个作为答案。
  • 我的源代码文本文件编码是 UTF-8,然后,解决方案不起作用(Java 1.8.0.45):getIdentifier("hallo") 产生“104_97_108_108_111
  • @Hartmut 不可能。 Arrays.toString() 输出 [ 作为第一个字符,它与 \D 匹配,所以你总是会得到一个 _ 作为第一个字符。我也在 Java 1.8.0.45 上运行了 getIdentifier("hallo") 并得到了 _104_97_108_108_111_(带有前导下划线),这是一个有效的标识符。
  • 从 Java 7 开始,您可以使用 nio.charset.StandardCharsets 来避免 try-catch 块。 Arrays.toString(str.getBytes(StandardCharsets.UTF_8)).replaceAll("\\D+", "_");
【解决方案2】:

我不知道用于此目的的工具,但可以使用 Character 类轻松创建它。

您知道 string€with_special_characters___ 是合法的 java 标识符吗?

public class Conv {
    public static void main(String[] args) {
        String[] idents = { "string with spaces", "100stringsstartswithnumber",
                "string€with%special†characters/\\!", "" };
        for (String ident : idents) {
            System.out.println(convert(ident));
        }
    }

    private static String convert(String ident) {
        if (ident.length() == 0) {
            return "_";
        }
        CharacterIterator ci = new StringCharacterIterator(ident);
        StringBuilder sb = new StringBuilder();
        for (char c = ci.first(); c != CharacterIterator.DONE; c = ci.next()) {
            if (c == ' ')
                c = '_';
            if (sb.length() == 0) {
                if (Character.isJavaIdentifierStart(c)) {
                    sb.append(c);
                    continue;
                } else
                    sb.append('_');
            }
            if (Character.isJavaIdentifierPart(c)) {
                sb.append(c);
            } else {
                sb.append('_');
            }
        };
        return sb.toString();
    }
}

打印

string_with_spaces
_100stringsstartswithnumber
string€with_special_characters___
_

【讨论】:

  • 这太棒了。它允许þ 作为标识符。这意味着结合条件运算符,您可以在代码中执行诸如 $? 8:þ 笑脸之类的操作,这总是一件好事,对吧?对吧?!
【解决方案3】:

如果您是为自动生成的代码执行此操作(即不太关心可读性),我最喜欢的一个就是 Base64 它。无需就哪些字符在哪些编码中有效而争论不休,这是“保护”任意字节数据的一种非常常见的方式。

【讨论】:

    【解决方案4】:

    有这么多 Java 源代码重构/生成框架,人们会认为这应该是相当普遍的任务。

    其实不是。

    • 代码重构框架将从现有的有效 java 标识符开始,将能够通过将它们与一些附加字符连接来生成新标识符以用于消除歧义。

    • 一个典型的代码生成框架将从一个受限制的字符集中的“名称”开始。它不必处理任意字符。


    我认为您的转换器的目的是在可能的情况下生成类似于输入字符串的标识符。如果是这种情况,我将通过按原样映射所有合法标识符字符来进行转换,并将非法标识符字符替换为“$xxxx”,其中“xxxx”是 Java 16 位字符的 4 位十六进制编码。

    您的方案也可以,但是用“_”替换所有非法字符更有可能导致标识符冲突;即两个输入字符串映射到相同的标识符。

    这是直截了当的代码,所以我将其留给您完成。

    【讨论】:

    • Elipse 的“提取到常量”重构选项必须处理任意字符,因为它从任意字符串和数字生成常量名称。
    • @parxier - 1) 这是一个不寻常的案例。 2)为什么不看一下Eclipse代码库,看看是否可以重用代码
    • 2) 如果我知道从哪里开始,我会的,这是一个巨大的项目。
    • @parxier - 如果您需要提高理解大型代码库的技能,这将是一个很好的开始示例。 [或者换句话说,我不会为你做你的研究:-)]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-02
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多