Регулярное выражение для преобразования CamelCase в camel_case в java

86

Я понимаю , почему желаемый результат не дается для преобразования с использованием регулярных выражений в строку , как FooBarв Foo_Barкоторый вместо дает Foo_Bar_. Я мог бы что-то сделать с String.substring substring(0, string.length() - 2)или просто заменить последний символ, но я думаю, что есть лучшее решение для такого сценария.

Вот код:

String regex = "([A-Z][a-z]+)";
String replacement = "$1_";

"CamelCaseToSomethingElse".replaceAll(regex, replacement); 

/*
outputs: Camel_Case_To_Something_Else_
desired output: Camel_Case_To_Something_Else
*/

Вопрос: Ищете более аккуратный способ получить желаемый результат?

Аджмартин
источник
Этот вопрос похож на stackoverflow.com/questions/4886091/…
Пол Варгас

Ответы:

168

См. Этот вопрос и CaseFormatот гуавы

в вашем случае что-то вроде:

CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "SomeInput");
mkobit
источник
@eliocs вопрос не был помечен тегом android и "аккуратнее" .. В любом случае спасибо за отрицательный голос;)
2
Ссылка CaseFormat недоступна. Замена уже здесь
Anticom
66

свяжите нижний и верхний регистры как две группы, все будет в порядке

public  class Main
{
    public static void main(String args[])
    {
        String regex = "([a-z])([A-Z]+)";
        String replacement = "$1_$2";
        System.out.println("CamelCaseToSomethingElse"
                           .replaceAll(regex, replacement)
                           .toLowerCase());
    }
}
умное напряжение
источник
2
Примечание. Если во входной строке разрешены однобуквенные слова, например «thisIsATest», приведенный выше код напечатает «this_is_atest». Гуава в принятом ответе дает "this_is_a_test".
DtotheK 01
Это один не будет работать на имя начать с колпачками, например: IBMIsMyCompany.
User3301
37

Вы можете использовать нижеприведенный фрагмент кода:

String replaceAll = key.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();
Сандип Вайд
источник
Что, если моя строка содержит число - mode3 заканчивается как mode3, тогда как мне нужно mode_3.
Майк Стоддарт,
MyUUIDЯ понял, что это не преобразование верблюжьего регистра, как правильное подчеркивание my_uu_id.
User3301
6

Я не могу предоставить RegEx, это все равно было бы безумно сложно.

Попробуйте эту функцию с автоматическим распознаванием акронимов.

К сожалению, Guava lib не определяет автоматически аббревиатуры в верхнем регистре, поэтому bigCAT будет преобразован в BIG_C_A_T.

/**
 * Convert to UPPER_UNDERSCORE format detecting upper case acronyms
 */
private String upperUnderscoreWithAcronyms(String name) {
    StringBuffer result = new StringBuffer();
    boolean begin = true;
    boolean lastUppercase = false;
    for( int i=0; i < name.length(); i++ ) {
        char ch = name.charAt(i);
        if( Character.isUpperCase(ch) ) {
            // is start?
            if( begin ) {
                result.append(ch);
            } else {
                if( lastUppercase ) {
                    // test if end of acronym
                    if( i+1<name.length() ) {
                        char next = name.charAt(i+1);
                        if( Character.isUpperCase(next) ) {
                            // acronym continues
                            result.append(ch);
                        } else {
                            // end of acronym
                            result.append('_').append(ch);
                        }
                    } else {
                        // acronym continues
                        result.append(ch);
                    }
                } else {
                    // last was lowercase, insert _
                    result.append('_').append(ch);
                }
            }
            lastUppercase=true;
        } else {
            result.append(Character.toUpperCase(ch));
            lastUppercase=false;
        }
        begin=false;
    }
    return result.toString();
}
Радзимир
источник
4

Почему бы просто не сопоставить предыдущий символ как начало строки $?

String text = "CamelCaseToSomethingElse";
System.out.println(text.replaceAll("([^_A-Z])([A-Z])", "$1_$2"));

Обратите внимание, что эта версия безопасна для выполнения на чем-то, что уже заделано верблюжьей оболочкой.

Бретт Райан
источник
Вы пытаетесь использовать ^и в $качестве якорей? Потому что их значения меняются, когда вы помещаете их в класс персонажей. [^$_A-Z]соответствует любому символу, который не$ , _или прописной буквы, и я не думаю , что это то , что вы имели в виду.
Алан Мур
Не предназначенный в качестве якоря, я пытаюсь не сопоставить верхний символ, $был ошибочно добавлен, поскольку это метод, который я использую для имен классов.
Бретт Райан
3

Добавьте утверждение опережающего просмотра нулевой ширины.

http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html

Прочтите документацию (?=X)и т. Д.

Лично я бы разделил строку, а затем рекомбинировал ее. Это может быть даже быстрее, если все сделано правильно, и это делает код намного проще для понимания, чем магия регулярных выражений. Не поймите меня неправильно: я люблю регулярные выражения. Но на самом деле это не изящное регулярное выражение, и это преобразование не является классической задачей регулярного выражения. В конце концов, кажется, вы тоже хотите писать строчные буквы?

Некрасивый , но быстрый хак бы заменить (.)([A-Z]+)с , $1_$2а затем в нижнем регистре всей строки после этого (если вы не можете сделать Perl-стиль extrended регэксп, где вы можете нижние регистр замены сразу!). Тем не менее, я считаю разделение при переходе снизу вверх, затем преобразование, а затем соединение как правильный и наиболее читаемый способ сделать это.

ВЫЙТИ - Anony-Mousse
источник
Да, со временем я бы тоже хотел, чтобы это было строчными буквами.
ajmartin
Так что я бы разделил его на куски, соответствующие [A-Z][a-z]*первой букве в нижнем регистре, и воссоединил их. Или трюк с заменой и строчными буквами, который я только что добавил к основному ответу.
ВЫЙТИ - Anony-Mousse
2
public class ReplaceFromCameltoSnake {
    public static void main(String args[]){
        String s1=" totalAmountWithoutDiscount";  
        String replaceString=s1.replaceAll("([A-Z]+)","\\_$1").toLowerCase(); 
        System.out.println(replaceString);  
    }
}
абинаш саху
источник
1 доллар используется для создания группы
abinash sahu
2

Не уверен, что с чистым регулярным выражением возможно что-то действительно сплошное. Особенно для поддержки аббревиатур.

Я сделал небольшую функцию, вдохновленную ответом @radzimir, которая поддерживает акронимы и не содержит буквенных символов:

С https://gist.github.com/ebuildy/cf46a09b1ac43eea17c7621b7617ebcd :

private static String snakeCaseFormat(String name) {
    final StringBuilder result = new StringBuilder();

    boolean lastUppercase = false;

    for (int i = 0; i < name.length(); i++) {
        char ch = name.charAt(i);
        char lastEntry = i == 0 ? 'X' : result.charAt(result.length() - 1);
        if (ch == ' ' || ch == '_' || ch == '-' || ch == '.') {
            lastUppercase = false;

            if (lastEntry == '_') {
                continue;
            } else {
                ch = '_';
            }
        } else if (Character.isUpperCase(ch)) {
            ch = Character.toLowerCase(ch);
            // is start?
            if (i > 0) {
                if (lastUppercase) {
                    // test if end of acronym
                    if (i + 1 < name.length()) {
                        char next = name.charAt(i + 1);
                        if (!Character.isUpperCase(next) && Character.isAlphabetic(next)) {
                            // end of acronym
                            if (lastEntry != '_') {
                                result.append('_');
                            }
                        }
                    }
                } else {
                    // last was lowercase, insert _
                    if (lastEntry != '_') {
                        result.append('_');
                    }
                }
            }
            lastUppercase = true;
        } else {
            lastUppercase = false;
        }

        result.append(ch);
    }
    return result.toString();
}
Томас Деко
источник
1
Это качественный ответ, он подходит для большинства крайних случаев.
User3301
1
([A-Z][a-z\d]+)(?=([A-Z][a-z\d]+))

Следует искать заглавную букву, за которой следуют строчные буквы. Положительный просмотр вперед будет искать другое слово, начинающееся с заглавной буквы, за которой следуют строчные буквы, но НЕ будет включать его в соответствие.

Смотрите здесь: http://regexr.com?30ooo

Джек
источник
0

Мне пришлось реализовать это, чтобы преобразовать некоторые ключи в формате верблюжьего регистра в нижний регистр с подчеркиванием. Я придумал регулярное выражение:

(?<!^|_|[A-Z])([A-Z])

На английском это означает заглавную букву, которой не предшествует начало строки, подчеркивание или другая заглавная буква .

В приведенных ниже примерах жирным шрифтом выделены символы, которые должны обеспечивать соответствие с использованием вышеупомянутого регулярного выражения:

  • Верблюд С аза Т о S omething Е ЛСЭ
  • верблюд С аза Т о S omething Е ЛСЭ
  • camel_case_to_something_else
  • Camel_Case_To_Something_Else
  • CAMEL_CASE_TO_SOMETHING_ELSE

Обратите внимание, что выражение не влияет на строку, которая уже находится в формате нижнего регистра + подчеркивание.

Шаблон замены будет таким:

_l$1

Это означает нижний регистр первой группы захвата , первая группа захвата - заглавная буква. Впоследствии вы можете строчить всю строку, чтобы нормализовать последние два образца из списка выше.

Аргенкиви
источник