Quelle est la manière la plus simple de convertir une chaîne Java en majuscules (mots séparés par des traits de soulignement) en CamelCase (sans séparateurs de mots)?

152

Le titre dit à peu près tout. Quelle est la manière la plus simple / la plus élégante de convertir, en Java, une chaîne du format "THIS_IS_AN_EXAMPLE_STRING"au format " ThisIsAnExampleString"? Je pense qu'il doit y avoir au moins une façon de le faire en utilisant String.replaceAll()et une regex.

Mes premières pensées sont les suivantes: ajoutez un trait de soulignement à la chaîne _, convertissez la chaîne entière en minuscules, puis utilisez replaceAll pour convertir chaque caractère précédé d'un trait de soulignement avec sa version en majuscule.

Matt Ball
la source
12
Note de l'éditeur, 2015-03: les "premières pensées" ci-dessus sont super stupides. Vous en apprenez beaucoup sur la création de logiciels en six ans.
Matt Ball
4
Ce moment où vous demandez «quel idiot a écrit ceci» et regardez dans le contrôle de source pour trouver ce jeune, stupide que vous avez fait. J'y suis allé, j'ai fait ça.
pierus
@MattBall: J'aime la version initiale de pensées, elle ne nécessite pas de bibliothèque et a juste besoin d'une concaténation de chaînes et de deux remplacements de regex.
Konrad Höffner

Réponses:

192

Une autre option consiste à utiliser Google Guava com.google.common.base.CaseFormat

George Hawkins a laissé un commentaire avec cet exemple d'utilisation:

CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "THIS_IS_AN_EXAMPLE_STRING");
Arnout Engelen
la source
3
Reportez-vous au commentaire de George Hawkins [utilisateur: 245602] pour un exemple. stackoverflow.com/questions/1143951/…
Michael Scheper
5
Les réponses java pures me manquent lorsque je développe pour Android.
eliocs
1
Cette réponse est la plus utile pour moi. Je pourrais très bien écrire mon propre code, mais si quelqu'un d'autre l'a déjà fait, je ne veux certainement pas réinventer la roue.
James Dunn
Reportez-vous à stackoverflow.com/questions/10310321/… également
Hartmut P.
1
@ CléssioMendes avez-vous envisagé d'en parler sur github.com/google/guava/issues ?
Arnout Engelen
128

Jetez un œil à WordUtils dans la bibliothèque de langues Apache Commons :

Plus précisément, la méthode capitalizeFully (String str, char [] delimiters) devrait faire le travail:

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

Barre verte!

Dan Gravell
la source
55
Non monsieur! Nous devrions réécrire nous-mêmes ces utilitaires existants et qui fonctionnent déjà, car nous sommes de vrais programmeurs!
skaffman
24
Il est 16h42 un vendredi après-midi. Je vais laisser tout le monde le réécrire, je sors prendre une bière \ o /;)
Dan Gravell
1
Plus précisément, je n'ai même pas accès à ce paquet avec ma configuration actuelle, et comme je n'ai vraiment (encore) besoin de rien au-delà de la méthode capitalizeFully, je ne perds rien en l'écrivant moi-même.
Matt Ball
7
Je respecte ta décision Matt, c'est probablement la bonne chose à faire à ta place. Cependant, tenez compte des éléments suivants: * Un autre membre de votre équipe décide qu'il a besoin d'une routine pour échanger la casse des lettres. Ils le mettent en œuvre. Vous avez maintenant ~ 20 lignes à maintenir. Vous auriez ~ 2 si vous utilisiez la bibliothèque. Et n'oubliez pas les tests unitaires! * La réponse acceptée a un inconvénient en ce que le nom de la méthode ne décrit pas ce que fait le code. Une API bien réutilisée comme le truc commun a rarement ces inconvénients. Le fait est que la maintenance est le plus gros coût des logiciels. En général, la réutilisation est une bonne idée.
Dan Gravell
2
Pour "accéder à ce paquet particulier", déposez repo1.maven.org/maven2/commons-lang/commons-lang/2.5/... dans votre chemin de classe . L'artefact Maven est commons-lang: commons-lang: 2.5 et il est facilement disponible à partir de 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();
}

Remarque : vous devez ajouter une validation d'argument.

Traverser
la source
1
Bonne réponse, mais ce serait un peu mieux si le nom de la méthode décrivait le fait que la chaîne était divisée ou que la logique était externalisée et les appels de méthode alignés comme un tube, par exemple "THIS_IS_AN_EXAMPLE_STRING" .removeUnderscores (). ToCamelCase () Ceci est plus réutilisable.
Dan Gravell
1
Ce n'est pas forcément mieux (même si oui, c'est plus réutilisable). En ce qui concerne les conventions de formatage des noms, camelcase peut / n'implique pas l'utilisation de traits de soulignement; au verso de la pièce, il existe des conventions qui spécifient l'utilisation de traits de soulignement. Donc, dans mon esprit, ce n'est qu'une méthode pour convertir d'un format à un autre.
Matt Ball
58
La bibliothèque de goyave Google a une énumération utilitaire plus générale pour la conversion entre les conventions courantes. Pour ce cas, vous feriez String result = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "THIS_IS_AN_EXAMPLE_STRING");. Voir com.google.common.base.CaseFormat javadoc .
George Hawkins
1
Cette réponse rencontrera des problèmes lorsqu'elle est utilisée dans des paramètres régionaux comme le turc ... Si votre code doit être utilisé dans plusieurs paramètres régionaux, utilisez toUpperCase (Locale) et toLowercase (Locale) .. pas ceux qui dépendent de la locale par défaut.
vkraemer
2
@DanGravell: une fois que vous supprimez les traits de soulignement, il n'est plus possible de distinguer les mots.
njzk2
18

Avec Apache Commons Lang3 lib, c'est très simple.

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

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

Exemple:

getName("SOME_CONSTANT");

Donne:

"SomeConstant"
librucha
la source
2
Dans le cas d'un nom de variable, ce n'est pas valide car le nom doit commencer par des minuscules.
Seby du
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
la source
3
le test de longueur n'est pas nécessaire
njzk2
9

Voici un extrait de code qui pourrait vous aider:

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
la source
Cette solution est appropriée pour ALL_UPPER to Camel case. Mais un léger changement dans le programme peut également gérer MixED_case ou lower_case (cas de serpent). J'ai suggéré une modification si cela est autorisé.
sud007
6

Exemple Java 1.8 utilisant 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 est maintenant thisIsSomeText

Mike
la source
J'aime cette réponse, mais elle a un défaut si la chaîne d'entrée est déjà dans le cas du chameau, auquel cas elle minuscule toute l'entrée. par exemple abcDef devient abcdef.
mrswadge
Un test utilisant text.matches( "([a-z]+[a-zA-Z0-9]+)+" )avant le boîtier de chameau est probablement une solution de contournement raisonnable pour le problème du boîtier inférieur.
mrswadge
2

Pas sûr, mais je pense que je peux utiliser moins de mémoire et obtenir des performances fiables en le faisant char-par-caractère. Je faisais quelque chose de similaire, mais en boucles dans les threads d'arrière-plan, donc j'essaye ceci pour l'instant. J'ai eu une certaine expérience avec String.split étant plus cher que prévu. Et je travaille sur Android et je m'attends à ce que le hoquet du GC soit plus un problème que l'utilisation du processeur.

  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();
  }

Un indice que String.split est cher est que son entrée est une expression régulière (pas un caractère comme String.indexOf) et il renvoie un tableau (au lieu de dire un itérateur car la boucle n'utilise qu'une seule chose à la fois). De plus, des cas comme "AB_AB_AB_AB_AB_AB ..." cassent l'efficacité de toute copie en bloc, et pour les chaînes longues, utilisez un ordre de grandeur plus de mémoire que la chaîne d'entrée.

Alors que la boucle à travers les caractères n'a pas de cas canonique. Donc, pour moi, la surcharge d'un regex et d'un tableau inutiles me semble généralement moins préférable (abandonnant alors une éventuelle efficacité de copie en masse). Intéressé d'entendre des opinions / corrections, merci.

Léorleor
la source
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();
}

Temps: en milli secondes.

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
la source
Cool, est-ce une itération avec l'entrée "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

Vous pouvez utiliser org.modeshape.common.text.Inflector .

Plus précisément:

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

Par défaut, cette méthode convertit les chaînes en UpperCamelCase.

L'artefact Maven est: org.modeshape: modeshape-common: 2.3.0.Final

sur le référentiel JBoss: https://repository.jboss.org/nexus/content/repositories/releases

Voici le fichier 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
la source
1

Vous pouvez également essayer ceci:

 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
la source
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
la source
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;
}

C'est le programme le plus simple à convertir en CamelCase. j'espère que cela vous aidera.

XORG_99
la source
0

Il se convertira Enum Constanten Camel Case. Ce serait utile pour tous ceux qui recherchent une telle fonctionnalité.

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();
        }
    }
AZ_
la source
0

Une autre solution à cela peut être la suivante.

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
la source
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();

}

Invocation comme

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

Temps d'exécution: 14 ms

Arindam
la source
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
la source
-2

Java 8 pour plusieurs chaînes:

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
la source
1
Réponse en double
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
la source
1
CaseFormatn'est pas une API standard. Réponse en double s'il s'agit de goyave.
Mark Jeronimus