J'ai trouvé un brillant RegEx pour extraire la partie d'une expression camelCase ou TitleCase.
(?<!^)(?=[A-Z])
Cela fonctionne comme prévu:
Par exemple avec Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
//words equals words = new String[]{"lorem","Ipsum"}
Mon problème est que cela ne fonctionne pas dans certains cas:
À mon avis, le résultat devrait être:
En d'autres termes, n caractères majuscules étant donnés:
Une idée sur la façon d'améliorer cette regex?
La regex suivante fonctionne pour tous les exemples ci-dessus:
public static void main(String[] args)
{
for (String w : "camelValue".split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) {
System.out.println(w);
}
}
Cela fonctionne en forçant le regard négatif à ignorer non seulement les correspondances au début de la chaîne, mais également les correspondances dans lesquelles une majuscule est précédée d'une autre majuscule. Cela gère les cas comme "valeur".
La première partie de l'expression rationnelle échoue seule sur "eclipseRCPExt" en ne se séparant pas entre "RPC" et "Ext". C'est l'objet de la deuxième clause: (?<!^)(?=[A-Z][a-z]
. Cette clause autorise une division avant chaque lettre majuscule suivie d'une lettre minuscule, sauf au début de la chaîne.
Il semble que vous rendiez cela plus compliqué que nécessaire. Pour camelCase, l'emplacement de division est simplement n'importe où une lettre majuscule suit immédiatement une lettre minuscule:
(?<=[a-z])(?=[A-Z])
Voici comment cette regex divise vos données d'exemple:
value -> value
camelValue -> camel / Value
TitleValue -> Title / Value
VALUE -> VALUE
eclipseRCPExt -> Eclipse / RCPExt
La seule différence par rapport à la sortie désirée concerne la eclipseRCPExt
, qui, à mon avis, est correctement divisée ici.
Remarque: cette réponse a récemment suscité un vote positif et je me suis rendu compte qu'il existe un meilleur moyen ...
En ajoutant une seconde alternative à l'expression rationnelle ci-dessus, tous les tests élémentaires de l'OP sont correctement divisés.
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])
Voici comment la regex améliorée divise les données d'exemple:
value -> value
camelValue -> camel / Value
TitleValue -> Title / Value
VALUE -> VALUE
eclipseRCPExt -> Eclipse / RCP / Ext
Edit: 20130824 Ajout d'une version améliorée pour gérer le cas RCPExt -> RCP / Ext
.
Une autre solution consisterait à utiliser une méthode dédiée dans commons-lang : StringUtils # splitByCharacterTypeCamelCase
Je ne pouvais pas obtenir la solution aix au travail (et cela ne marche pas non plus avec RegExr), alors je me suis inventé avec le mien que j'ai testé et qui semble faire exactement ce que vous cherchez:
((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))
et voici un exemple d'utilisation:
; Regex Breakdown: This will match against each Word in Camel and Pascal case strings, while properly handling acrynoms.
; (^[a-z]+) Match against any lower-case letters at the start of the string.
; ([A-Z]{1}[a-z]+) Match against Title case words (one upper case followed by lower case letters).
; ([A-Z]+(?=([A-Z][a-z])|($))) Match against multiple consecutive upper-case letters, leaving the last upper case letter out the match if it is followed by lower case letters, and including it if it's followed by the end of the string.
newString := RegExReplace(oldCamelOrPascalString, "((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))", "$1 ")
newString := Trim(newString)
Ici, je sépare chaque mot par un espace, voici donc quelques exemples de la transformation de la chaîne:
Cette solution ci-dessus fait ce que l'article original demande, mais j'avais également besoin d'une expression rationnelle pour trouver les chaînes camel et Pascal qui incluaient des nombres. J'ai donc également proposé cette variante pour inclure des nombres:
((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))
et un exemple d'utilisation:
; Regex Breakdown: This will match against each Word in Camel and Pascal case strings, while properly handling acrynoms and including numbers.
; (^[a-z]+) Match against any lower-case letters at the start of the command.
; ([0-9]+) Match against one or more consecutive numbers (anywhere in the string, including at the start).
; ([A-Z]{1}[a-z]+) Match against Title case words (one upper case followed by lower case letters).
; ([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))) Match against multiple consecutive upper-case letters, leaving the last upper case letter out the match if it is followed by lower case letters, and including it if it's followed by the end of the string or a number.
newString := RegExReplace(oldCamelOrPascalString, "((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))", "$1 ")
newString := Trim(newString)
Et voici quelques exemples de la transformation d'une chaîne de chiffres avec cette expression rationnelle:
A-Z
:s.split("(?<=\\p{Ll})(?=\\p{Lu})|(?<=\\p{L})(?=\\p{Lu}\\p{Ll})");
Non plus:
E.g parseXML
-> parse
, XML
.
ou
Par exemple. XMLParser
-> XML
, Parser
.
public class SplitCamelCaseTest {
static String BETWEEN_LOWER_AND_UPPER = "(?<=\\p{Ll})(?=\\p{Lu})";
static String BEFORE_UPPER_AND_LOWER = "(?<=\\p{L})(?=\\p{Lu}\\p{Ll})";
static Pattern SPLIT_CAMEL_CASE = Pattern.compile(
BETWEEN_LOWER_AND_UPPER +"|"+ BEFORE_UPPER_AND_LOWER
);
public static String splitCamelCase(String s) {
return SPLIT_CAMEL_CASE.splitAsStream(s)
.collect(joining(" "));
}
@Test
public void testSplitCamelCase() {
assertEquals("Camel Case", splitCamelCase("CamelCase"));
assertEquals("lorem Ipsum", splitCamelCase("loremIpsum"));
assertEquals("XML Parser", splitCamelCase("XMLParser"));
assertEquals("Eclipse RCP Ext", splitCamelCase("eclipseRCPExt"));
assertEquals("VALUE", splitCamelCase("VALUE"));
}
}
Les deux réponses principales ici fournissent un code utilisant des recherches positives, qui ne sont pas prises en charge par toutes les variantes de regex. Les expressions rationnelles ci-dessous captureront à la fois PascalCase
et camelCase
et peuvent être utilisées dans plusieurs langues.
Remarque: Je me rends bien compte que cette question concerne Java, cependant, je vois également de nombreuses mentions de ce message dans d'autres questions marquées pour différentes langues, ainsi que des commentaires sur cette question pour les mêmes.
Voir cette expression rationnelle utilisée ici
([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)
eclipseRCPExt
SomethingIsWrittenHere
TEXTIsWrittenHERE
VALUE
loremIpsum
Eclipse
RCP
Ext
Something
Is
Written
Here
TEXT
Is
Written
HERE
VALUE
lorem
Ipsum
[A-Z]+
[A-Z]?
, suivi d'un ou de plusieurs caractères alpha minuscules [a-z]+
[A-Z]
ou un caractère de limite Word \b
Vous pouvez utiliser l'expression ci-dessous pour Java:
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?=[A-Z][a-z])|(?<=\\d)(?=\\D)|(?=\\d)(?<=\\D)
Je peux confirmer que la chaîne regex ([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)
donnée par ctwheels, ci-dessus, fonctionne avec la version Microsoft de regex.
Je voudrais également suggérer l’alternative suivante, basée sur l’expression rationnelle de ctwheels, qui gère les caractères numériques: ([A-Z0-9]+|[A-Z]?[a-z]+)(?=[A-Z0-9]|\b)
.
Cela permet de scinder des chaînes telles que:
DrivingB2BTradeIn2019Onwards
à
Stimuler le commerce B2B à partir de 2019
Au lieu de rechercher des séparateurs qui ne sont pas là vous pourriez aussi envisager de trouver les composants de nom (ceux-ci sont certainement là):
String test = "_Eclipse福福RCPExt";
Pattern componentPattern = Pattern.compile("_? (\\p{Upper}?\\p{Lower}+ | (?:\\p{Upper}(?!\\p{Lower}))+ \\p{Digit}*)", Pattern.COMMENTS);
Matcher componentMatcher = componentPattern.matcher(test);
List<String> components = new LinkedList<>();
int endOfLastMatch = 0;
while (componentMatcher.find()) {
// matches should be consecutive
if (componentMatcher.start() != endOfLastMatch) {
// do something horrible if you don't want garbage in between
// we're lenient though, any Chinese characters are lucky and get through as group
String startOrInBetween = test.substring(endOfLastMatch, componentMatcher.start());
components.add(startOrInBetween);
}
components.add(componentMatcher.group(1));
endOfLastMatch = componentMatcher.end();
}
if (endOfLastMatch != test.length()) {
String end = test.substring(endOfLastMatch, componentMatcher.start());
components.add(end);
}
System.out.println(components);
Ceci génère [Eclipse, 福福, RCP, Ext]
. La conversion en tableau est bien sûr simple.