Regex per convertire CamelCase in camel_case in java


86

Capisco perché l'output desiderato non viene fornito per la conversione utilizzando regex una stringa come FooBarquella Foo_Barche invece dà Foo_Bar_. Avrei potuto fare qualcosa con String.substring substring(0, string.length() - 2)o semplicemente sostituire l'ultimo carattere, ma penso che ci sia una soluzione migliore a tale scenario.

Ecco il codice:

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

Domanda: Cerchi un modo più ordinato per ottenere l'output desiderato?


Risposte:


171

Vedi questa domanda e CaseFormatda guava

nel tuo caso, qualcosa come:

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

@eliocs la domanda non è stata contrassegnata come Android e "modo più ordinato" .. Grazie comunque per il

2
Il collegamento CaseFormat è offline. La sostituzione è qui
Anticom

66

legare le lettere minuscole e maiuscole come due gruppi, sarà ok

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
Nota: se sono consentite parole con una sola lettera nella stringa di input, ad esempio "thisIsATest", il codice precedente stamperà "this_is_atest". Guava, nella risposta accettata, risulta in "this_is_a_test".
DtotheK

Questo non funziona su un nome iniziare con le protezioni, per esempio: IBMIsMyCompany.
Utente3301

37

Puoi utilizzare lo snippet di codice seguente:

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

E se la mia stringa contiene un numero - mode3 finisce come mode3, mentre io vorrei mode_3.
Mike Stoddart

Non converte il caso del cammello come MyUUIDsottolineatura correttamente, ho capito my_uu_id.
Utente3301

6

Non posso fornire RegEx, sarebbe comunque incredibilmente complesso.

Prova questa funzione con il riconoscimento automatico degli acronimi.

Sfortunatamente Guava lib non rileva automaticamente gli acronimi maiuscoli, quindi "bigCAT" verrebbe convertito in "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();
}

5

Perché non abbinare semplicemente il carattere precedente come un non inizio di riga $?

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

Nota che questa versione è sicura da eseguire su qualcosa che è già rivestito di cammello.


Stai cercando di usare ^e $come ancore? Perché i loro significati cambiano quando li inserisci in una classe di personaggi. [^$_A-Z]corrisponde a qualsiasi carattere che non sia $, _o una lettera maiuscola, e non credo che sia quello che intendevi.
Alan Moore

Non intendo come ancore, sto cercando di non abbinare il carattere superiore, è $stato erroneamente aggiunto in quanto è una tecnica che uso sui nomi delle classi.
Brett Ryan

3

Aggiungi un'asserzione lookahead a larghezza zero.

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

Leggi la documentazione per (?=X)ecc.

Personalmente, vorrei effettivamente dividere la stringa, quindi ricombinarla. Questo può anche essere più veloce se fatto bene e rende il codice molto più facile da capire rispetto alla magia delle espressioni regolari. Non fraintendermi: amo le espressioni regolari. Ma questa non è davvero un'espressione regolare ordinata, né questa trasformazione è un classico compito regexp. Dopotutto sembra che tu voglia fare anche le minuscole?

Un trucco brutto ma veloce sarebbe sostituire (.)([A-Z]+)con $1_$2e poi minuscolo l'intera stringa in seguito (a meno che tu non possa fare espressioni regolari estrese in stile Perl, dove puoi scrivere direttamente in minuscolo la sostituzione!). Tuttavia, considero la divisione nella transizione dal basso verso l'alto, quindi la trasformazione, quindi l'unione come il modo corretto e più leggibile per farlo.


Sì, alla fine vorrei che fosse anche in minuscolo.
Ajmartin

Quindi lo dividerei in blocchi corrispondenti [A-Z][a-z]*, minuscolo la prima lettera e li ricongiungerei. O il trucco di sostituzione + minuscolo che ho appena aggiunto alla risposta principale.
È CHIUSO - 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 - viene utilizzato per creare un gruppo
abinash sahu

2

Non sono sicuro che sia possibile avere qualcosa di veramente solido con pura regex. Soprattutto per supportare gli acronimi.

Ho creato una piccola funzione, ispirata alla risposta di @radzimir, che supporta acronimi e nessun carattere alfabetico:

Da 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
Questa è una risposta di qualità, gestisce la maggior parte dei casi limite.
Utente3301

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

Dovrebbe cercare una lettera maiuscola seguita da lettere minuscole. Il lookahead positivo cercherà un'altra parola che inizia con una lettera maiuscola seguita da lettere minuscole ma NON la includerà nella corrispondenza.

Guarda qui: http://regexr.com?30ooo


0

Ho dovuto implementarlo per convertire alcune chiavi in ​​formato cammello in minuscolo con trattini bassi. L'espressione regolare che ho trovato è:

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

In inglese sta per lettera maiuscola che non è preceduta dall'inizio della stringa, da un trattino basso o da un'altra lettera maiuscola .

Negli esempi seguenti, i caratteri in grassetto sono quelli che dovrebbero produrre una corrispondenza utilizzando la suddetta espressione regolare:

  • Cammello C ase T o S omething E lse
  • cammello C ase T o S omething E lse
  • camel_case_to_something_else
  • Camel_Case_To_Something_Else
  • CAMEL_CASE_TO_SOMETHING_ELSE

Si noti che l'espressione non influisce sulle stringhe che sono già in formato minuscolo + trattino basso.

Il modello di sostituzione sarebbe:

_l$1

Il che significa minuscolo del primo gruppo di acquisizione , il primo gruppo di acquisizione è la lettera maiuscola. In seguito potresti anche scrivere in minuscolo l'intera stringa per normalizzare gli ultimi due campioni dall'elenco sopra.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.