Как преобразовать произвольную строку в Java-идентификатор?
Мне нужно преобразовать любую произвольную строку:
- строка с пробелами
- 100stringsstartswithnumber
- строка € с% специальными символами †/\!
- [пустая строка]
для действительного идентификатора Java:
- string_with_spaces
- _100stringsstartswithnumber
- string_with_special_characters ___
- _
Есть ли существующий инструмент для этой задачи?
С таким множеством рефакторинговых/генерирующих фреймов источников Java можно подумать, что это должна быть довольно общая задача.
Ответы
Ответ 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)
- обратимый
Спасибо Йоахиму Зауэру за предложение UTF-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();
}
Он сохраняет символы, которые являются допустимыми идентификаторами, преобразуя только те, которые являются недопустимыми, в их десятичные эквиваленты.
Ответ 2
Я не знаю инструмент для этой цели, но его можно легко создать с помощью класса Character.
Знаете ли вы, что строка € 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___
_
Ответ 3
Если вы делаете это для автогенерированного кода (т.е. не заботятся о читаемости), один из моих фаворитов - это Base64 Это. Не нужно играть на языке юриста по поводу того, какие символы действительны в кодировке, и это довольно распространенный способ "защитить" произвольные байтовые данные.
Ответ 4
С таким множеством рефакторинговых/генерирующих фреймов источников Java можно подумать, что это должна быть довольно общая задача.
На самом деле это не так.
-
Структура рефакторинга кода начнется с существующих действительных java-идентификаторов, сможет генерировать новый идентификатор, объединяя их с некоторыми дополнительными символами для целей устранения неоднозначности.
-
Типичная структура генерации кода начинается с "имен", взятых из ограниченного набора символов. Он не должен иметь дело с произвольными символами.
Я предполагаю, что целью вашего конвертера является создание идентификаторов, которые напоминают входные строки, если это возможно. Если это произойдет, я бы сделал преобразование путем сопоставления всех символов юридического идентификатора как-есть и заменил символы незаконного идентификатора на "$ xxxx", где "xxxx" представляет собой четырехзначное шестнадцатеричное кодирование 16-битного символа Java.
Ваша схема также работает, но замена всех недопустимых символов на "_" скорее приведет к конфликтам идентификаторов; то есть, когда две входные строки сопоставляются с одним и тем же идентификатором.
Это прямолинейно коду, поэтому я оставлю это для вас.