¿Cuál es la forma más sencilla de convertir una cadena Java de mayúsculas (palabras separadas por guiones bajos) a CamelCase (sin separadores de palabras)?

152

El título lo dice todo. ¿Cuál es la forma más simple / elegante de convertir, en Java, una cadena del formato "THIS_IS_AN_EXAMPLE_STRING"al formato " ThisIsAnExampleString"? Me imagino que debe haber al menos una forma de hacerlo usando String.replaceAll()y una expresión regular.

Mis pensamientos iniciales son: anteponer la cadena con un guión bajo ( _), convertir toda la cadena a minúscula y luego usar replaceAll para convertir cada carácter precedido por un guión bajo con su versión en mayúscula.

Matt Ball
fuente
12
Nota del editor, 2015-03: los "pensamientos iniciales" anteriores son súper tontos. Aprende mucho sobre la creación de software en seis años.
Matt Ball
44
Ese momento cuando preguntas 'qué idiota escribió esto' y miras en el control de la fuente para encontrar a ese joven y estúpido que hiciste. He estado allí, hecho eso.
pierus
@MattBall: Me gusta la versión de pensamientos iniciales, no requiere una biblioteca y solo necesita una concatenación de cadenas y dos reemplazos de expresiones regulares.
Konrad Höffner

Respuestas:

192

Otra opción es usar Google Guava's com.google.common.base.CaseFormat

George Hawkins dejó un comentario con este ejemplo de uso:

CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "THIS_IS_AN_EXAMPLE_STRING");
Arnout Engelen
fuente
3
Consulte el comentario de George Hawkins [usuario: 245602] para ver un ejemplo. stackoverflow.com/questions/1143951/…
Michael Scheper
55
Echo de menos las respuestas de Java puro mientras estoy desarrollando para Android.
eliocs
1
Esta respuesta es la más útil para mí. Podría escribir mi propio código, pero si alguien más ya lo ha hecho, ciertamente no quiero reinventar la rueda.
James Dunn
Consulte stackoverflow.com/questions/10310321/… también
Hartmut P.
1
@ CléssioMendes, ¿ha considerado mencionar esto en github.com/google/guava/issues ?
Arnout Engelen
128

Echa un vistazo a WordUtils en el idioma de Apache Commons biblioteca :

Específicamente, el método capitalizeFully (String str, char [] delimiters) debería hacer el trabajo:

String blah = "LORD_OF_THE_RINGS";
assertEquals("LordOfTheRings", WordUtils.capitalizeFully(blah, new char[]{'_'}).replaceAll("_", ""));

Barra verde!

Dan Gravell
fuente
55
¡No señor! Deberíamos reescribir estas utilidades existentes, que ya funcionan, nosotros mismos, ¡porque somos programadores adecuados!
skaffman
24
Son las 16:42 del viernes por la tarde. Dejaré que todos lo reescriban, saldré a tomar una cerveza \ / /;)
Dan Gravell
1
Más aún, ni siquiera tengo acceso a ese paquete en particular con mi configuración actual, y dado que realmente (todavía) no necesito nada más allá del método capitalizeFully, no pierdo nada escribiéndolo yo mismo.
Matt Ball
77
Respeto tu decisión Matt, probablemente sea lo correcto en tu posición. Sin embargo, considere lo siguiente: * Alguien más en su equipo decide que necesita una rutina para cambiar el caso de las letras. Lo implementan. Ahora tiene ~ 20 líneas para mantener. Tendría ~ 2 si usara la biblioteca. ¡Y no olvide las pruebas unitarias! * La respuesta aceptada tiene una desventaja en que el nombre del método no describe lo que hace el código. Una API bien reutilizada como las cosas comunes rara vez tiene esos inconvenientes. El punto es que el mantenimiento es el mayor costo del software. En general, la reutilización es una buena idea.
Dan Gravell
2
Para "acceder a este paquete en particular", coloque repo1.maven.org/maven2/commons-lang/commons-lang/2.5/… en su classpath. El artefacto de Maven es commons-lang: commons-lang: 2.5 y está disponible en Maven Central.
Hendy Irawan
90
static String toCamelCase(String s){
   String[] parts = s.split("_");
   String camelCaseString = "";
   for (String part : parts){
      camelCaseString = camelCaseString + toProperCase(part);
   }
   return camelCaseString;
}

static String toProperCase(String s) {
    return s.substring(0, 1).toUpperCase() +
               s.substring(1).toLowerCase();
}

Nota : debe agregar la validación de argumentos.

Cruzar
fuente
1
Buena respuesta, pero sería un poco mejor si el nombre del método describe el hecho de que la cadena se dividió o que la lógica se externalizó y las llamadas al método se alinearon como una tubería, por ejemplo, "THIS_IS_AN_EXAMPLE_STRING" .removeUnderscores (). ToCamelCase () This Es más reutilizable.
Dan Gravell
1
Eso no es necesariamente mejor (aunque sí, es más reutilizable). Cuando se trata de convenciones de formato de nombres, camelcase puede / implica no usar guiones bajos; en el reverso de la moneda, hay convenciones que especifican el uso de guiones bajos. En mi opinión, este es solo un método para convertir de un formato a otro.
Matt Ball
58
La biblioteca de guayaba de Google tiene una enumeración de utilidad más general para la conversión entre las convenciones comunes. Para este caso lo harías String result = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "THIS_IS_AN_EXAMPLE_STRING");. Ver com.google.common.base.CaseFormat javadoc .
George Hawkins el
1
Esta respuesta tendrá problemas cuando se use en configuraciones regionales como el turco ... Si su código se va a usar en varias configuraciones regionales, use toUpperCase (Locale) y toLowercase (Locale) ... no las que dependen de la configuración regional predeterminada.
vkraemer
2
@DanGravell: una vez que eliminas los guiones bajos, ya no es posible distinguir las palabras.
njzk2
18

Con Apache Commons Lang3 lib es muy fácil.

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;

public String getName(String text) {
  return StringUtils.remove(WordUtils.capitalizeFully(text, '_'), "_");
}

Ejemplo:

getName("SOME_CONSTANT");

Da:

"SomeConstant"
librucha
fuente
2
En el caso del nombre de la variable, esto no es válido porque el nombre debe comenzar con minúsculas.
Seby
9
public static void main(String[] args) {
    String start = "THIS_IS_A_TEST";
    StringBuffer sb = new StringBuffer();
    for (String s : start.split("_")) {
        sb.append(Character.toUpperCase(s.charAt(0)));
        if (s.length() > 1) {
            sb.append(s.substring(1, s.length()).toLowerCase());
        }
    }
    System.out.println(sb);
}
Yishai
fuente
3
la prueba de longitud no es necesaria
njzk2
9

Aquí hay un fragmento de código que podría ayudar:

String input = "ABC_DEF";
StringBuilder sb = new StringBuilder();
for( String oneString : input.toLowerCase().split("_") )
{
    sb.append( oneString.substring(0,1).toUpperCase() );
    sb.append( oneString.substring(1) );
}

// sb now holds your desired String
Alex B
fuente
Esta solución es apropiada para ALL_UPPER para el caso Camel. Pero un ligero cambio en el programa también puede manejar MixED_case o lower_case (caso de serpiente). He sugerido una edición si está permitido.
sud007
6

Ejemplo de Java 1.8 usando Streams

String text = "THIS_IS_SOME_TEXT";

String bactrianCamel = Stream.of(text.split("[^a-zA-Z0-9]"))
        .map(v -> v.substring(0, 1).toUpperCase() + v.substring(1).toLowerCase())
        .collect(Collectors.joining());
String dromedaryCamel = bactrianCamel.toLowerCase().substring(0, 1) + bactrianCamel.substring(1); 

System.out.printf("%s is now %s%n", text, dromedaryCamel); 

THIS_IS_SOME_TEXT ahora es thisIsSomeText

Miguel
fuente
Me gusta esta respuesta, pero tiene un defecto si la cadena de entrada ya está en el caso de camello, en cuyo caso disminuye la entrada completa. por ejemplo, abcDef se convierte en abcdef.
mrswadge
Una prueba que use text.matches( "([a-z]+[a-zA-Z0-9]+)+" )antes de la carcasa de camello es probablemente una solución razonable para el problema de la carcasa inferior.
mrswadge
2

No estoy seguro, pero creo que puedo usar menos memoria y obtener un rendimiento confiable al hacerlo char-by-char. Estaba haciendo algo similar, pero en bucles en subprocesos de fondo, así que estoy intentando esto por ahora. He tenido cierta experiencia con String.split, que es más caro de lo esperado. Y estoy trabajando en Android y espero que el hipo de GC sea más un problema que el uso de la CPU.

  public static String toCamelCase(String value) {
    StringBuilder sb = new StringBuilder();

    final char delimChar = '_';
    boolean lower = false;
    for (int charInd = 0; charInd < value.length(); ++charInd) {
      final char valueChar = value.charAt(charInd);
      if (valueChar == delimChar) {
        lower = false;
      } else if (lower) {
        sb.append(Character.toLowerCase(valueChar));
      } else {
        sb.append(Character.toUpperCase(valueChar));
        lower = true;
      }
    }

    return sb.toString();
  }

Una pista de que String.split es costoso es que su entrada es una expresión regular (no un carácter como String.indexOf) y devuelve una matriz (en lugar de decir un iterador porque el bucle solo usa una cosa a la vez). Además, casos como "AB_AB_AB_AB_AB_AB ..." rompen la eficiencia de cualquier copia masiva, y para cadenas largas, use un orden de magnitud más memoria que la cadena de entrada.

Mientras que recorrer los caracteres no tiene un caso canónico. Entonces, para mí, la sobrecarga de una expresión regular y una matriz innecesarias generalmente parece menos preferible (luego renunciar a la posible eficiencia de copia masiva). Interesado en escuchar opiniones / correcciones, gracias.

leorleor
fuente
2
public String withChars(String inputa) {
    String input = inputa.toLowerCase();
    StringBuilder sb = new StringBuilder();
    final char delim = '_';
    char value;
    boolean capitalize = false;
    for (int i=0; i<input.length(); ++i) {
        value = input.charAt(i);
        if (value == delim) {
            capitalize = true;
        }
        else if (capitalize) {
            sb.append(Character.toUpperCase(value));
            capitalize = false;
        }
        else {
            sb.append(value);
        }
    }

    return sb.toString();
}

public String withRegex(String inputa) {
    String input = inputa.toLowerCase();
    String[] parts = input.split("_");
    StringBuilder sb = new StringBuilder();
    sb.append(parts[0]);
    for (int i=1; i<parts.length; ++i) {
        sb.append(parts[i].substring(0,1).toUpperCase());
        sb.append(parts[i].substring(1));
    }

    return sb.toString();
}

Tiempos: en milisegundos.

Iterations = 1000
WithChars: start = 1379685214671 end = 1379685214683 diff = 12
WithRegex: start = 1379685214683 end = 1379685214712 diff = 29

Iterations = 1000
WithChars: start = 1379685217033 end = 1379685217045 diff = 12
WithRegex: start = 1379685217045 end = 1379685217077 diff = 32

Iterations = 1000
WithChars: start = 1379685218643 end = 1379685218654 diff = 11
WithRegex: start = 1379685218655 end = 1379685218684 diff = 29

Iterations = 1000000
WithChars: start = 1379685232767 end = 1379685232968 diff = 201
WithRegex: start = 1379685232968 end = 1379685233649 diff = 681

Iterations = 1000000
WithChars: start = 1379685237220 end = 1379685237419 diff = 199
WithRegex: start = 1379685237419 end = 1379685238088 diff = 669

Iterations = 1000000
WithChars: start = 1379685239690 end = 1379685239889 diff = 199
WithRegex: start = 1379685239890 end = 1379685240585 diff = 695

Iterations = 1000000000
WithChars: start = 1379685267523 end = 1379685397604 diff = 130081
WithRegex: start = 1379685397605 end = 1379685850582 diff = 452977
Srisa
fuente
Genial, ¿está iterando con la entrada "THIS_IS_AN_EXAMPLE_STRING"?
leorleor
@leorleor Iteration = 1000000000 WithChars: start = 1387547394726 end = 1387547889896 diff = 495170 WithRegex: start = 1387547889897 end = 1387548944739 diff = 1054842
Srisa
1

Puede usar org.modeshape.common.text.Inflector .

Específicamente:

String camelCase(String lowerCaseAndUnderscoredWord,
    boolean uppercaseFirstLetter, char... delimiterChars) 

Por defecto, este método convierte cadenas a UpperCamelCase.

El artefacto de Maven es: org.modeshape: modeshape-common: 2.3.0.Final

en el repositorio JBoss: https://repository.jboss.org/nexus/content/repositories/releases

Aquí está el archivo JAR: https://repository.jboss.org/nexus/content/repositories/releases/org/modeshape/modeshape-common/2.3.0.Final/modeshape-common-2.3.0.Final.jar

Hendy Irawan
fuente
1

Puedes probar esto también:

 public static String convertToNameCase(String s)
    {
        if (s != null)
        {
            StringBuilder b = new StringBuilder();
            String[] split = s.split(" ");
            for (String srt : split)
            {
                if (srt.length() > 0)
                {
                    b.append(srt.substring(0, 1).toUpperCase()).append(srt.substring(1).toLowerCase()).append(" ");
                }
            }
            return b.toString().trim();
        }
        return s;
    }
Ashish
fuente
1
protected String toCamelCase(String input) {
    if (input == null) {
        return null;
    }

    if (input.length() == 0) {
        return "";
    }

    // lowercase the first character
    String camelCaseStr = input.substring(0, 1).toLowerCase();

    if (input.length() > 1) {
        boolean isStartOfWord = false;

        for (int i = 1; i < input.length(); i++) {
            char currChar = input.charAt(i);
            if (currChar == '_') {
                // new word. ignore underscore
                isStartOfWord = true;
            } else if (Character.isUpperCase(currChar)) {
                // capital letter. if start of word, keep it
                if (isStartOfWord) {
                    camelCaseStr += currChar;
                } else {
                    camelCaseStr += Character.toLowerCase(currChar);
                }
                isStartOfWord = false;
            } else {
                camelCaseStr += currChar;
                isStartOfWord = false;
            }
        }
    }

    return camelCaseStr;
}
Muzikant
fuente
1
public String CamelCase(String str)
{
    String CamelCase="";
    String parts[] = str.split("_");
    for(String part:parts)
    {
        String as=part.toLowerCase();
        int a=as.length();
        CamelCase = CamelCase + as.substring(0, 1).toUpperCase()+ as.substring(1,a);    
    }
    return CamelCase;
}

Este es el programa más simple para convertir en CamelCase. Espero que te ayude..

XORG_99
fuente
0

Se convertirá Enum Constanten Camel Case. Sería útil para cualquiera que esté buscando tal funcionalidad.

public enum TRANSLATE_LANGUAGES {
        ARABIC("ar"), BULGARIAN("bg"), CATALAN("ca"), CHINESE_SIMPLIFIED("zh-CN"), CHINESE_TRADITIONAL("zh-TW"), CZECH("cs"), DANISH("da"), DUTCH("nl"), ENGLISH("en"), ESTONIAN("et"), FINNISH("fi"), FRENCH(
                "fr"), GERMAN("de"), GREEK("el"), HAITIAN_CREOLE("ht"), HEBREW("he"), HINDI("hi"), HMONG_DAW("mww"), HUNGARIAN("hu"), INDONESIAN("id"), ITALIAN("it"), JAPANESE("ja"), KOREAN("ko"), LATVIAN(
                "lv"), LITHUANIAN("lt"), MALAY("ms"), NORWEGIAN("no"), PERSIAN("fa"), POLISH("pl"), PORTUGUESE("pt"), ROMANIAN("ro"), RUSSIAN("ru"), SLOVAK("sk"), SLOVENIAN("sl"), SPANISH("es"), SWEDISH(
                "sv"), THAI("th"), TURKISH("tr"), UKRAINIAN("uk"), URDU("ur"), VIETNAMESE("vi");

        private String code;

        TRANSLATE_LANGUAGES(String language) {
            this.code = language;
        }

        public String langCode() {
            return this.code;
        }

        public String toCamelCase(TRANSLATE_LANGUAGES lang) {
            String toString = lang.toString();
            if (toString.contains("_")) {
                String st = toUpperLowerCase(toString.split("_"));
            }

            return "";
        }

        private String toUpperLowerCase(String[] tempString) {
            StringBuilder builder = new StringBuilder();

            for (String temp : tempString) {

                String char1 = temp.substring(0, 1);
                String restString = temp.substring(1, temp.length()).toLowerCase();
                builder.append(char1).append(restString).append(" ");

            }

            return builder.toString();
        }
    }
ARIZONA_
fuente
0

Una solución más a esto puede ser la siguiente.

public static String toCamelCase(String str, String... separators) {
    String separatorsRegex = "\\".concat(org.apache.commons.lang3.StringUtils.join(separators, "|\\"));
    List splits = Arrays.asList(str.toLowerCase().split(separatorsRegex));
    String capitalizedString = (String)splits.stream().map(WordUtils::capitalize).reduce("", String::concat);
    return capitalizedString.substring(0, 1).toLowerCase() + capitalizedString.substring(1);
}
Sajani
fuente
0
public static final String  UPPER_CAMEL = "initUp";
public static final String  LOWER_CAMEL = "initLow";

public String toCamel(String src, String separator, String format) {
    StringBuilder builder = new StringBuilder(src.toLowerCase());
    int len = builder.length();

    for (int idx = builder.indexOf(separator); idx > 0 && idx < len; idx = builder.indexOf(separator, idx)) {
        builder = builder.replace(idx, idx + 2, (String.valueOf(builder.charAt(idx + 1)).toUpperCase()));
    }

    switch (format) {
    case LOWER_CAMEL:
        builder.setCharAt(0, Character.toLowerCase(builder.charAt(0)));
        break;
    default:
        builder.setCharAt(0, Character.toUpperCase(builder.charAt(0)));
        break;
    }

    return builder.toString();

}

Invocación como

toCamel("THIS_IS_AN_EXAMPLE_STRING", "_", UPPER_CAMEL)

Tiempo de ejecución: 14 ms

Arindam
fuente
0

Un simple snnipet:

 public static String camelCase(String in) {
    if (in == null || in.length() < 1) { return ""; } //validate in
    String out = "";
    for (String part : in.toLowerCase().split("_")) {
        if (part.length() < 1) { //validate length
            continue;
        }
        out += part.substring(0, 1).toUpperCase();
        if (part.length() > 1) { //validate length
            out += part.substring(1);
        }
    }
    return out;
}
fitorec
fuente
-2

Java 8 para cadenas múltiples:

import com.google.common.base.CaseFormat;



String camelStrings = "YOUR_UPPER, YOUR_TURN, ALT_TAB";

List<String> camelList = Arrays.asList(camelStrings.split(","));
camelList.stream().forEach(i -> System.out.println(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, i) + ", "));
DET66
fuente
1
Respuesta duplicada
Mark Jeronimus
-2
    protected String toCamelCase(CaseFormat caseFormat, String... words){
        if (words.length  == 0){
          throw new IllegalArgumentException("Word list is empty!");
        }

        String firstWord = words[0];
        String [] restOfWords = Arrays.copyOfRange(words, 1, words.length);

        StringBuffer buffer = new StringBuffer();
        buffer.append(firstWord);
        Arrays.asList(restOfWords).stream().forEach(w->buffer.append("_"+ w.toUpperCase()));

        return CaseFormat.UPPER_UNDERSCORE.to(caseFormat, buffer.toString());

    }
Vladimir
fuente
1
CaseFormatNo es API estándar. Respuesta duplicada si es guayaba.
Mark Jeronimus