Regex do konwersji CamelCase na camel_case w java


86

Rozumiem, dlaczego żądane dane wyjściowe nie są podawane do konwersji za pomocą wyrażenia regularnego na ciąg podobny FooBardo Foo_Barktóry zamiast tego daje Foo_Bar_. Mogłem coś zrobić z String.substring substring(0, string.length() - 2)lub po prostu zamienić ostatni znak, ale myślę, że jest lepsze rozwiązanie takiego scenariusza.

Oto kod:

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
*/

Pytanie: Szukasz lepszego sposobu na uzyskanie pożądanego wyniku?


Odpowiedzi:


168

Zobacz to pytanie i CaseFormatod guawy

w twoim przypadku coś takiego:

CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "SomeInput");

@eliocs pytanie nie zostało oznaczone jako android i „schludniej” .. Mimo wszystko dzięki za głos przeciw;)

2
Link CaseFormat jest niedostępny. Zastąpienie jest tutaj
Anticom

66

połącz małe i duże litery jako dwie grupy, wszystko będzie dobrze

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
Uwaga: Jeśli w wejściowym ciągu znaków dozwolone są słowa składające się z jednej litery, np. „ThisIsATest”, powyższy kod wypisze „this_is_atest”. Guawa, w zaakceptowanej odpowiedzi, daje wynik „this_is_a_test”.
DtotheK

Ten nie będzie działał z nazwami zaczynającymi się od wielkich liter, np IBMIsMyCompany. : .
User3301

37

Możesz użyć poniższego fragmentu kodu:

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

Co jeśli mój ciąg zawiera liczbę - mode3 kończy się jako mode3, podczas gdy chciałbym, aby mode_3.
Mike Stoddart

Nie konwertuje obudowy wielbłąda, jak MyUUIDpoprawnie podkreślał, mam my_uu_id.
User3301

6

Nie mogę podać RegEx, i tak byłoby to szalenie skomplikowane.

Wypróbuj tę funkcję z automatycznym rozpoznawaniem akronimów.

Niestety biblioteka Guava nie wykrywa automatycznie akronimów wielkich liter, więc „bigCAT” zostanie przekonwertowany na „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

Dlaczego po prostu nie dopasować poprzedniego znaku jako nie początku wiersza $?

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

Zauważ, że ta wersja jest bezpieczna do wykonania na czymś, co już jest okryte wielbłądem.


Czy próbujesz użyć ^i $jako kotwic? Ponieważ ich znaczenie zmienia się, gdy umieścisz je w klasie postaci. [^$_A-Z]dopasowuje dowolny znak, który nie jest $, _lub wielkiej litery, a nie sądzę, że to, co masz na myśli.
Alan Moore

Nie zamierzając jako kotwice, próbuję nie dopasować wyższego znaku, $został omyłkowo dodany, ponieważ jest to technika, której używam do nazw klas.
Brett Ryan

3

Dodaj asercję wyprzedzającą o zerowej szerokości.

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

Przeczytaj dokumentację (?=X)itp.

Osobiście rozdzieliłbym strunę, a następnie ponownie ją połączył . Może to być nawet szybsze, jeśli zostanie wykonane poprawnie, i sprawia, że ​​kod jest znacznie łatwiejszy do zrozumienia niż magia wyrażeń regularnych. Nie zrozum mnie źle: uwielbiam wyrażenia regularne. Ale to naprawdę nie jest zgrabne wyrażenie regularne, ani ta transformacja nie jest klasycznym zadaniem wyrażenia regularnego . W końcu wydaje się, że chcesz też pisać małymi literami?

Brzydka ale szybko siekać byłoby wymienić (.)([A-Z]+)z $1_$2czym małe litery cały ciąg potem (o ile można to zrobić perl-styl extrended wyrażeń regularnych, w których można małe litery wymiany bezpośrednio!). Nadal uważam, że rozdzielenie przy przejściu z dołu do góry, następnie przekształcenie, a następnie połączenie się, jest właściwym i najbardziej czytelnym sposobem zrobienia tego.


Tak, ostatecznie chciałbym, żeby było to również małymi literami.
ajmartin

Więc podzieliłbym go na pasujące fragmenty [A-Z][a-z]*, małą pierwszą literą i ponownie połączyłem. Lub sztuczka zastępująca + małe litery, którą właśnie dodałem do głównej odpowiedzi.
WYJŚCIE - 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 $ - służy do tworzenia grupy
abinash sahu

2

Nie jestem pewien, czy można mieć coś naprawdę samotnego przy użyciu czystego wyrażenia regularnego. Szczególnie do obsługi akronimów.

Zrobiłem małą funkcję, zainspirowaną odpowiedzią @radzimir, która obsługuje akronimy i nie ma znaków alfabetycznych:

Z 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
To jest dobra odpowiedź, radzi sobie z większością skrajnych przypadków.
User3301

1
([A-Z][a-z\d]+)(?=([A-Z][a-z\d]+))

Powinien szukać dużej litery, po której następują małe litery. Pozytywne spojrzenie w przód będzie szukać innego słowa zaczynającego się wielką literą, po której następują małe litery, ale NIE uwzględni go w dopasowaniu.

Zajrzyj tutaj: http://regexr.com?30ooo


0

Musiałem to zaimplementować, aby przekonwertować niektóre klucze w formacie wielbłąda na małe litery z podkreśleniami. Wyrażenie regularne, które wymyśliłem, to:

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

W języku angielskim oznacza wielką literę, która nie jest poprzedzona początkiem ciągu, podkreśleniem lub inną dużą literą .

W poniższych przykładach pogrubione znaki to te, które powinny dać dopasowanie przy użyciu wyżej wymienionego wyrażenia regularnego:

  • Camel C ase T o S omething E lse
  • camel C ase T o S omething E lse
  • camel_case_to_something_else
  • Camel_Case_To_Something_Else
  • CAMEL_CASE_TO_SOMETHING_ELSE

Zwróć uwagę, że wyrażenie nie wpływa na ciągi już zapisane małymi literami + podkreślenie.

Wzorzec zastępczy wyglądałby tak:

_l$1

Oznacza to małe litery pierwszej grupy przechwytywania , przy czym pierwsza grupa przechwytywania jest wielką literą. Możesz później również małe litery w całym ciągu, aby znormalizować ostatnie dwie próbki z powyższej listy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.