В интернете в основном встретились узкоспециализированные методы, которые либо переводят только имена констант (по соглашениям Java, SOME_JAVA_NAMING_CONVENTION_CONST), либо только фразы, разделенные пробелами.
Мне этого категорически не хватало, нужна была бОльшая универсальность.
Как и подобает любому уважающему себя велосипедисту, я начал писать свой алгоритм, и немного увлёкся. Очнувшись от кода, я обнаружил, что функция переводит любые мною скормленные строки в нормальный camelCase или CamelCase.
Единственное, что она не делает — не запрещает цифры в начале получившейся строки (для соглашений JNC), но мне это и не нужно было (при необходимости дописывается одной строкой кода — пополнением ко второму, вложенному, условию).
Что получилось можете увидеть под катом.
Функция принимает два аргумента — собственно строку, и флаг, указывающий писать результат с большой буквы (недо-camelCase).
Делает она всё это за один проход. Код присыпан комментариями для новичков (кому и пишется эта шпаргалка).
При жалении второй аргумент можно безболезненно откусить.
Так же практически без изменений алгоритм портируется на javascript и C#.
/**
* Возвращает отформатированную в виде camelCase (или CamelCase) строку.
*
* @param string Исходная строка
* @param firstWordToLowerCase Начинать ли искомую строку с маленького символа (lowercase).
*/
public static String toCamelCase(String string, boolean firstWordToLowerCase) {
char currentChar, previousChar = '\u0000'; // Текущий и предыдущий символ прохода
StringBuilder result = new StringBuilder(); // Результат функции в виде строкового билдера
boolean firstLetterArrived = !firstWordToLowerCase; // Флаг, отвечающий за написание первого символа результата в lowercase
boolean nextLetterInUpperCase = true; // Флаг, приказывающий следующий добавляемый символ писать в UPPERCASE
// Проходимся по всем символам полученной строки
for (int i = 0; i < string.length(); i++) {
currentChar = string.charAt(i);
/* Если текущий символ не цифробуква -
приказываем следующий символ писать Большим (начать новое слово) и идем на следующую итерацию.
Если предыдущий символ это маленькая буква или цифра, а текущий это большая буква -
приказываем текущий символ писать Большим (начать новое слово).
*/
if (!Character.isLetterOrDigit(currentChar) || (
((Character.isLetter(previousChar) && Character.isLowerCase(previousChar)) || Character.isDigit(previousChar)) &&
Character.isLetter(currentChar) && Character.isUpperCase(currentChar))
) {
nextLetterInUpperCase = true;
if (!Character.isLetterOrDigit(currentChar)) {
previousChar = currentChar;
continue;
}
}
// Если приказано писать Большую букву, и первая буква уже написана.
if (nextLetterInUpperCase && firstLetterArrived) {
result.append(Character.toUpperCase(currentChar));
}
else {
result.append(Character.toLowerCase(currentChar));
}
// Устанавливаем флаги.
firstLetterArrived = true;
nextLetterInUpperCase = false;
previousChar = currentChar;
}
// Возвращаем полученный результат.
return result.toString();
}
Source string: 'normalCamelCaseName'
Result string: 'normalCamelCaseName'
Result string: 'NormalCamelCaseName' (firstWordToLowerCase = false)
===========================
Source string: 'NotCamelCaseName'
Result string: 'notCamelCaseName'
Result string: 'NotCamelCaseName' (firstWordToLowerCase = false)
===========================
Source string: 'CONSTANT_TO_CAMEL_CASE'
Result string: 'constantToCamelCase'
Result string: 'ConstantToCamelCase' (firstWordToLowerCase = false)
===========================
Source string: 'Text To Camel Case'
Result string: 'textToCamelCase'
Result string: 'TextToCamelCase' (firstWordToLowerCase = false)
===========================
Source string: 'Text to camel case'
Result string: 'textToCamelCase'
Result string: 'TextToCamelCase' (firstWordToLowerCase = false)
===========================
Source string: 'ОтЖиМаЕмСя На ШиФфТе, ДрУзЯфФкИ!:)'
Result string: 'отЖиМаЕмСяНаШиФфТеДрУзЯфФкИ'
Result string: 'ОтЖиМаЕмСяНаШиФфТеДрУзЯфФкИ' (firstWordToLowerCase = false)
===========================
Source string: '-(*&*&%&%$^&^*()Знаков*&^%*(&$препинания… и.нечитаемых-----------знаков^ (Может*90Быть&(*?*?: СКОЛЬКО*?%?:%угодно!'
Result string: 'знаковПрепинанияИНечитаемыхЗнаковМожет90БытьСколькоУгодно'
Result string: 'ЗнаковПрепинанияИНечитаемыхЗнаковМожет90БытьСколькоУгодно' (firstWordToLowerCase = false)
===========================
Source string: 'И, напоследок, русская строка со знаками препинания (локализация!).'
Result string: 'иНапоследокРусскаяСтрокаСоЗнакамиПрепинанияЛокализация'
Result string: 'ИНапоследокРусскаяСтрокаСоЗнакамиПрепинанияЛокализация' (firstWordToLowerCase = false)
Комментарии (10)
js605451
08.11.2015 20:04+6Меня всегда удивляло как в одном человеке может одновременно уживаться усердие написать комментарии типа вот этих:
// Проходимся по всем символам полученной строки
// Устанавливаем флаги.
// Возвращаем полученный результат.
и потом взять и нагородить многоэтажное совершенно нечитабельное условие типа такого:
!Character.isLetterOrDigit(currentChar) || ( ( ( Character.isLetter(previousChar) && Character.isLowerCase(previousChar) ) || Character.isDigit(previousChar) ) && Character.isLetter(currentChar) && Character.isUpperCase(currentChar) )
Duster
08.11.2015 20:25-5Подробные комменты я пишу редко, в основном комментами отделяю смысловые «блоки» кода, как тут. Это довольно популярная практика…
А условие вполне себе читабельное. Скобочки вынес, на строки разбил, части условия понятны и по названию.
PS:js605451
08.11.2015 22:04+4Вы путаете «читаемость кода» и «читаемость намерений». Код обычно читают с одной единственной целью — понять намерения автора. Форматирование и выделение блоков безусловно помогает, но это далеко не самое важное. Я легко могу прочитать условие, которое вы написали. Даже если бы вы его не отформатировали, мне ничего не стоило бы отформатировать его за вас — это тупая механическая работа. Что намного сложнее — понять ваше намерение: почему условие именно такое, что именно будет означать если условие вычисляется в true. Субъективно, хороший код как минимум даёт ответ на этот вопрос без вмешательства автора, а очень хороший — исключает возникновение такого вопроса.
Lure_of_Chaos
09.11.2015 01:35+2Не могу понять, зачем было писать целую статью, будто про теорему Ферма. Где здесь проблематичность, которую хабравчане могли обсуждать?
andrewnester
ну вот честное слово, ну что здесь такого, почему Вы решили написать статью?
можно ведь просто опубликовать на github или написать в блоге, или как вариант есть сайты со сниппетами, могли бы добавить туда
vedenin1980
Согласен, не говоря уже о том что туже функцию можно переписать раза в два короче и проще, как-то так:
ИМХО, таким вещам лучше на сайтах с сниппетами, потому что если каждый будет постить на хабре каждую свою удачную на его взгляд функцию…
Duster
Поиск по гуглу выдавал говнокод, заточенный под конкретный формат исходной строки. А запросов таких достаточно много.
Искать нечто подобное по гитхабу считаю извращением, и даже мысль не приходила искать подобное там.
Сайты со сниппетами… Не пользовался как-то раньше, можете показать пример?
PS: И, да, мнения разделились. Минусов ровно столько же, сколько и звезд… Значит кому-то да пригодится.
stranger777
->тысячи их. Гуглите на английском и будет Вам счастье. Достаточно было запроса snippet sites.
Borz
А вы не думали пополнить класс org.apache.commons.lang3.StringUtils вашим методом?