Regex für die Konvertierung von CamelCase in camel_case in Java

85

Ich verstehe, warum die gewünschte Ausgabe nicht für die Konvertierung mit Regex einer Zeichenfolge angegeben wird, wie FooBarsie Foo_Barstattdessen angegeben wird Foo_Bar_. Ich hätte etwas mit String.substring machen substring(0, string.length() - 2)oder einfach das letzte Zeichen ersetzen können, aber ich denke, es gibt eine bessere Lösung für ein solches Szenario.

Hier ist der Code:

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

Frage: Suchen Sie nach einem besseren Weg, um die gewünschte Ausgabe zu erzielen?

Ajmartin
quelle
Diese Frage ist ähnlich zu stackoverflow.com/questions/4886091/…
Paul Vargas

Antworten:

165

Siehe diese Frage und CaseFormatvon Guave

in Ihrem Fall so etwas wie:

CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "SomeInput");
mkobit
quelle
@eliocs die Frage wurde nicht getaggt android und "ordentlicher Weg" .. Danke für die Abwertung trotzdem;)
2
Der CaseFormat-Link ist offline. Ersatz ist hier
Anticom
66

Binden Sie die Klein- und Großbuchstaben als zwei Gruppen, es wird in Ordnung sein

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());
    }
}
Klugheit
quelle
2
Hinweis: Wenn einzelne Buchstaben in der Eingabezeichenfolge zulässig sind, z. B. "thisIsATest", wird im obigen Code "this_is_atest" ausgegeben. Guave führt in der akzeptierten Antwort zu "this_is_a_test".
DtotheK
Dieser funktioniert nicht bei einem Namen, der mit Großbuchstaben beginnt, z IBMIsMyCompany. B.: .
User3301
37

Sie können das folgende Code-Snippet verwenden:

String replaceAll = key.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();
Sandeep Vaid
quelle
Was ist, wenn meine Zeichenfolge eine Zahl enthält - mode3 endet als mode3, während ich mode_3 möchte.
Mike Stoddart
Es konvertiert Kamel Fall nicht gerne MyUUIDrichtig zu unterstreichen, ich habe my_uu_id.
User3301
5

Ich kann RegEx nicht bereitstellen, es wäre sowieso wahnsinnig komplex.

Probieren Sie diese Funktion mit automatischer Erkennung von Akronymen aus.

Leider erkennt Guava lib Akronyme in Großbuchstaben nicht automatisch, sodass "bigCAT" in "BIG_C_A_T" konvertiert wird.

/**
 * 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();
}
Radzimir
quelle
4

Warum nicht einfach das vorherige Zeichen als nicht Zeilenanfang $abgleichen?

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

Beachten Sie, dass diese Version sicher für etwas ausgeführt werden kann, das bereits in Kamelhülle ist.

Brett Ryan
quelle
Versuchen Sie, ^und $als Anker zu verwenden? Weil sich ihre Bedeutung ändert, wenn Sie sie in eine Charakterklasse einordnen. [^$_A-Z]passt zu jedem Zeichen, das nicht $ist _, oder zu einem Großbuchstaben, und ich glaube nicht, dass Sie das gemeint haben.
Alan Moore
Ich habe versucht, nicht mit dem oberen Zeichen übereinzustimmen. Dies $wurde fälschlicherweise hinzugefügt, da es sich um eine Technik handelt, die ich für Klassennamen verwende.
Brett Ryan
3

Fügen Sie eine Lookahead-Zusicherung mit einer Breite von Null hinzu.

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

Lesen Sie die Dokumentation für (?=X)etc.

Persönlich würde ich die Zeichenfolge tatsächlich teilen und dann neu kombinieren. Dies kann sogar schneller sein, wenn es richtig gemacht wird, und es macht den Code viel einfacher zu verstehen als Magie mit regulären Ausdrücken. Versteh mich nicht falsch: Ich liebe reguläre Ausdrücke. Aber das ist nicht wirklich ein ordentlicher regulären Ausdruck, noch ist diese Transformation eine klassische regexp Aufgabe. Immerhin scheint es, dass Sie auch Kleinbuchstaben machen wollen?

Ein hässlicher , aber schneller Hack wäre zu ersetzen (.)([A-Z]+)mit $1_$2und dann danach die gesamte Zeichenfolge in Kleinbuchstabe (es sei denn , Sie Perl-Stil verlängerten regexps tun können, in dem Sie den Ersatz direkt in Kleinbuchstabe können!). Trotzdem denke ich darüber nach, beim Übergang von unten nach oben zu teilen, dann zu transformieren und dann zu verbinden, um dies richtig und am besten lesbar zu machen.

Hat aufgehört - Anony-Mousse
quelle
Ja, irgendwann möchte ich, dass es auch in Kleinbuchstaben geschrieben wird.
Ajmartin
Also würde ich es in passende Stücke aufteilen [A-Z][a-z]*, den ersten Buchstaben in Kleinbuchstaben schreiben und sie wieder zusammenfügen. Oder der Ersatz + Kleinbuchstaben-Trick, den ich gerade zur Hauptantwort hinzugefügt habe.
Hat aufgehört - 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);  
    }
}
Abinash Sahu
quelle
$ 1-wird verwendet, um Gruppe zu machen
Abinash Sahu
2

Ich bin mir nicht sicher, ob es möglich ist, mit reinem Regex etwas wirklich Solides zu haben. Besonders um Akronyme zu unterstützen.

Ich habe eine kleine Funktion erstellt, die von der Antwort von @radzimir inspiriert ist und Akronyme und kein alphabetisches Zeichen unterstützt:

Von 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();
}
Thomas Decaux
quelle
1
Dies ist eine qualitativ hochwertige Antwort, die die meisten Randfälle behandelt.
User3301
1
([A-Z][a-z\d]+)(?=([A-Z][a-z\d]+))

Sollte nach einem Großbuchstaben suchen, gefolgt von Kleinbuchstaben. Der positive Lookahead sucht nach einem anderen Wort, das mit einem Großbuchstaben gefolgt von Kleinbuchstaben beginnt, schließt es jedoch NICHT in das Match ein.

Schauen Sie hier: http://regexr.com?30ooo

Jack
quelle
0

Ich musste dies implementieren, um einige Schlüssel im Kamelbuchstabenformat in Kleinbuchstaben mit Unterstrichen umzuwandeln. Der reguläre Ausdruck, den ich mir ausgedacht habe, ist:

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

Im Englischen steht es für Großbuchstaben, denen nicht der Anfang der Zeichenfolge, ein Unterstrich oder ein anderer Großbuchstabe vorangestellt ist .

In den folgenden Beispielen sind die fett gedruckten Zeichen diejenigen, die mit dem oben genannten regulären Ausdruck eine Übereinstimmung erzeugen sollen:

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

Beachten Sie, dass der Ausdruck keine Zeichenfolge beeinflusst, die bereits in Kleinbuchstaben + Unterstrich vorliegt.

Das Ersatzmuster wäre:

_l$1

Dies bedeutet Kleinbuchstaben der ersten Erfassungsgruppe , wobei die erste Erfassungsgruppe der Großbuchstabe ist. Sie können anschließend auch die gesamte Zeichenfolge in Kleinbuchstaben schreiben, um die letzten beiden Samples aus der obigen Liste zu normalisieren.

argenkiwi
quelle