Regex para converter CamelCase em camel_case em java

86

Eu entendo por que a saída desejada não é fornecida para converter usando regex uma string como FooBara Foo_Barque em vez disso fornece Foo_Bar_. Eu poderia ter feito algo com String.substring substring(0, string.length() - 2)ou apenas substituído o último caractere, mas acho que há uma solução melhor para tal cenário.

Aqui está o código:

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

Pergunta: Procurando uma maneira mais organizada de obter a saída desejada?

Ajmartin
fonte
Esta pergunta é semelhante a stackoverflow.com/questions/4886091/…
Paul Vargas

Respostas:

168

Veja esta pergunta e CaseFormatda goiaba

no seu caso, algo como:

CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "SomeInput");
mkobit
fonte
@eliocs a pergunta não foi marcada como android e "maneira mais limpa" .. Obrigado pelo downvote de qualquer maneira;)
2
O link CaseFormat está offline. A substituição é aqui
Anticom
66

vincule a caixa baixa e a caixa alta como dois grupos, vai ficar tudo bem

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());
    }
}
tensão de inteligência
fonte
2
Nota: Se palavras de uma única letra são permitidas na string de entrada, por exemplo, "thisIsATest", o código acima imprimirá "this_is_atest". Goiaba, na resposta aceita, resulta em "this_is_a_test".
DtotheK 01 de
Este não vai funcionar em um nome começar com bonés, por exemplo: IBMIsMyCompany.
User3301
37

Você pode usar o snippet de código abaixo:

String replaceAll = key.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();
Sandeep Vaid
fonte
E se minha string contiver um número - mode3 termina como mode3, enquanto eu gostaria de mode_3.
Mike Stoddart
Não converte a caixa de camelo como MyUUIDsublinhado corretamente, entendi my_uu_id.
User3301
6

Não posso fornecer RegEx, seria insanamente complexo de qualquer maneira.

Experimente esta função com reconhecimento automático de siglas.

Infelizmente, Guava lib não detecta automaticamente acrônimos em maiúsculas, então "bigCAT" seria convertido para "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();
}
Radzimir
fonte
4

Por que não simplesmente combinar o caractere anterior como um não início de linha $?

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

Observe que esta versão é segura para ser executada em algo que já está envolto em camelo.

Brett Ryan
fonte
Você está tentando usar ^e $como âncoras? Porque seus significados mudam quando você os coloca em uma classe de personagem. [^$_A-Z]corresponde a qualquer caractere que não seja $, _ou uma letra maiúscula, e não acho que seja isso que você quis dizer.
Alan Moore
Não pretendendo ser âncoras, estou tentando não corresponder ao caractere superior, o $foi adicionado por engano, pois é uma técnica que uso em nomes de classes.
Brett Ryan
3

Adicione uma afirmação antecipada de largura zero.

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

Leia a documentação para (?=X)etc.

Pessoalmente, eu iria realmente dividir a string e, em seguida, recombinar. Isso pode até ser mais rápido quando feito da maneira certa e torna o código muito mais fácil de entender do que a mágica de expressão regular. Não me entenda mal: eu adoro expressões regulares. Mas isso não é realmente uma expressão regular puro, nem é esta transformação uma tarefa clássica regexp. Afinal parece que você também quer fazer minúsculas?

Um hack feio, mas rápido, seria substituir (.)([A-Z]+)por $1_$2e depois colocar toda a string em minúsculas (a menos que você possa fazer expressões regulares extrendidas no estilo perl, onde você pode minúsculas a substituição diretamente!). Ainda assim, considero a divisão na transição inferior para superior, a transformação e a união como a maneira adequada e mais legível de fazer isso.

Tem QUIT - Anony-Mousse
fonte
Sim, eventualmente eu gostaria que fosse em minúsculas também.
ajmartin,
Então, eu iria dividi-lo em pedaços correspondentes [A-Z][a-z]*, minúsculas a primeira letra e juntá-los novamente. Ou o truque de substituição + minúsculas que acabei de adicionar à resposta principal.
QUIT - 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
fonte
$ 1-é usado para fazer o grupo
abinash sahu
2

Não tenho certeza se é possível ter algo realmente sólido com regex puro. Especialmente para apoiar siglas.

Fiz uma pequena função, inspirada na resposta @radzimir, que suporta siglas e nenhum caractere alfabético:

De 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
fonte
1
Esta é uma resposta de qualidade, ela lida com a maioria dos casos extremos.
User3301
1
([A-Z][a-z\d]+)(?=([A-Z][a-z\d]+))

Deve pesquisar uma letra maiúscula seguida de letras minúsculas. O lookahead positivo procurará outra palavra que comece com uma letra maiúscula seguida por letras minúsculas, mas NÃO a incluirá na correspondência.

Olhe aqui: http://regexr.com?30ooo

Jack
fonte
0

Eu tive que implementar isso para converter algumas chaves no formato camel case para minúsculas com sublinhados. A expressão regular que criei é:

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

Em inglês, significa letra maiúscula que não é precedida pelo início da string, um sublinhado ou outra letra maiúscula .

Nos exemplos abaixo, os caracteres em negrito são os que devem produzir uma correspondência usando a expressão regular mencionada:

  • Camelo C ase T S S omething E LSE
  • camelo C ase T S S omething E LSE
  • camel_case_to_something_else
  • Camel_Case_To_Something_Else
  • CAMEL_CASE_TO_SOMETHING_ELSE

Observe que a expressão não afeta a string que já está no formato de minúsculas + sublinhado.

O padrão de substituição seria:

_l$1

O que significa letras minúsculas do primeiro grupo de captura , primeiro o grupo de captura sendo a letra maiúscula. Você também pode colocar a string inteira em letras minúsculas para normalizar as duas últimas amostras da lista acima.

argenkiwi
fonte