Regex для перетворення 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");

@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

Це один не буде працювати на ім'я почати з ковпачками, наприклад: IBMIsMyCompany.
User3301

37

Ви можете використовувати нижче фрагмент коду:

String replaceAll = key.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();

Що робити, якщо мій рядок містить число - mode3 закінчується як mode3, тоді як я хотів би mode_3.
Mike Stoddart

Це не перетворює випадок верблюда, як MyUUIDпідкреслення належним чином, я зрозумів my_uu_id.
User3301

6

Я не можу надати RegEx, це все одно було б шалено складно.

Спробуйте цю функцію з автоматичним розпізнаванням скорочень.

На жаль, lib Guava не визначає автоматичні абревіатури верхнього регістру, тому "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]відповідає будь-якому символу, який не є $, _або великій букві, і я не думаю, що це те, що ви мали на увазі.
Alan Moore

Не маючи на меті якорів, я намагаюся не відповідати верхньому символу, $помилково було додано, оскільки це техніка, яку я використовую для імен класів.
Brett Ryan

3

Додайте твердження про перегляд нульової ширини.

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

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

Особисто я б фактично розділив рядок, а потім рекомбінував його. Це може бути навіть швидше, якщо все зробити правильно, і це полегшує розуміння коду, ніж магія регулярних виразів. Не зрозумійте мене неправильно: я люблю регулярні вирази. Але це насправді не акуратний регулярний вираз, і це перетворення не є класичним завданням регулярного виразу. Врешті-решт, здається, ви також хочете робити малі літери?

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


Так, врешті-решт я хотів би, щоб це було також малими літерами.
ajmartin

Тож я б розділив його на шматки, що збігаються [A-Z][a-z]*, малу літеру першої літери та знову приєднався до них. Або трюк із заміною + малими літерами, який я щойно додав до основної відповіді.
Вийшов - Аноні-Мус

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

Що означає нижній регістр першої групи захоплення , перша група захоплення - це велика літера. Ви також можете ввести нижній регістр цілого рядка, щоб нормалізувати останні два зразки зі списку вище.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.