Ich habe ein brilliant RegEx gefunden, um den Teil eines camelCase- oder TitleCase-Ausdrucks zu extrahieren.
(?<!^)(?=[A-Z])
Es funktioniert wie erwartet:
Zum Beispiel mit Java:
String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
//words equals words = new String[]{"lorem","Ipsum"}
Mein Problem ist, dass es in einigen Fällen nicht funktioniert:
Meiner Meinung nach sollte das Ergebnis sein:
Mit anderen Worten, gegebene n Großbuchstaben:
Irgendeine Idee, wie man diese Regex verbessern kann?
Die folgende Regex funktioniert für alle obigen Beispiele:
public static void main(String[] args)
{
for (String w : "camelValue".split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) {
System.out.println(w);
}
}
Es bewirkt, dass der negative Look nicht nur Übereinstimmungen am Anfang der Zeichenfolge ignoriert, sondern auch Übereinstimmungen ignoriert, bei denen einem Großbuchstaben ein weiterer Großbuchstabe vorangestellt ist. Dies behandelt Fälle wie "VALUE".
Der erste Teil des regulären Ausdrucks allein schlägt bei "eclipseRCPExt" fehl, da er nicht zwischen "RPC" und "Ext" aufteilen kann. Dies ist der Zweck der zweiten Klausel: (?<!^)(?=[A-Z][a-z]
. Diese Klausel erlaubt eine Aufteilung vor jedem Großbuchstaben, auf den ein Kleinbuchstabe folgt, außer am Anfang der Zeichenfolge.
Es scheint, dass Sie das komplizierter machen, als es sein muss. Bei camelCase ist der Split-Speicherort einfach überall, wo ein Großbuchstabe unmittelbar auf einen Kleinbuchstaben folgt:
(?<=[a-z])(?=[A-Z])
Diese Regex teilt Ihre Beispieldaten folgendermaßen auf:
value -> value
camelValue -> camel / Value
TitleValue -> Title / Value
VALUE -> VALUE
eclipseRCPExt -> Eclipse / RCPExt
Der einzige Unterschied zu Ihrer gewünschten Ausgabe besteht in der eclipseRCPExt
, von der ich behaupte, dass sie hier richtig aufgeteilt ist.
Hinweis: Diese Antwort wurde kürzlich positiv bewertet und mir wurde klar, dass es einen besseren Weg gibt ...
Durch Hinzufügen einer zweiten Alternative zum obigen Regex werden alle Testfälle des OP korrekt aufgeteilt.
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])
So teilt der verbesserte reguläre Ausdruck die Beispieldaten auf:
value -> value
camelValue -> camel / Value
TitleValue -> Title / Value
VALUE -> VALUE
eclipseRCPExt -> Eclipse / RCP / Ext
Edit: 20130824 Verbesserte Version für RCPExt -> RCP / Ext
Fall hinzugefügt.
Eine andere Lösung wäre die Verwendung einer speziellen Methode in commons-lang : StringUtils # splitByCharacterTypeCamelCase
Ich konnte die Lösung von aix nicht zum Laufen bringen (und auch nicht mit RegExr), also habe ich mir meine eigene Lösung ausgedacht, die ich getestet habe und genau das zu tun, wonach Sie suchen:
((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))
und hier ist ein Beispiel für die Verwendung:
; 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)
Hier trenne ich jedes Wort durch ein Leerzeichen, daher sind hier einige Beispiele, wie der String transformiert wird:
Diese Lösung oben macht das, was der ursprüngliche Beitrag verlangt, aber ich brauchte auch einen Regex, um Kamel- und Pascal-Strings zu finden, die Zahlen enthielten. Daher kam ich auch zu dieser Variation mit Zahlen:
((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))
und ein Beispiel für die Verwendung:
; 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)
Und hier sind einige Beispiele, wie eine Zeichenfolge mit Zahlen mit dieser Regex umgewandelt wird:
A-Z
zu behandeln:s.split("(?<=\\p{Ll})(?=\\p{Lu})|(?<=\\p{L})(?=\\p{Lu}\\p{Ll})");
Entweder:
ZB parseXML
-> parse
, XML
.
oder
Z.B. 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"));
}
}
Beide Top-Antworten bieten Code mit positiven LookHinds, die nicht von allen Regex-Varianten unterstützt werden. Der folgende reguläre Ausdruck erfasst sowohl PascalCase
als auch camelCase
und kann in mehreren Sprachen verwendet werden.
Anmerkung: Ich weiß, dass diese Frage Java betrifft, ich sehe jedoch mehrere Erwähnungen dieses Beitrags in anderen Fragen, die für verschiedene Sprachen getaggt sind, sowie einige Kommentare zu dieser Frage.
Sehen Sie diese Regex hier in Verwendung
([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]?
, gefolgt von einem oder mehreren Kleinbuchstaben [a-z]+
[A-Z]
oder ein Word-Begrenzungszeichen \b
ist.Sie können den folgenden Ausdruck für Java verwenden:
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?=[A-Z][a-z])|(?<=\\d)(?=\\D)|(?=\\d)(?<=\\D)
Ich kann bestätigen, dass der von ctwheels oben angegebene Regex-String ([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)
mit der Microsoft-Variante von Regex funktioniert.
Ich möchte auch die folgende Alternative vorschlagen, basierend auf dem Regex von ctwheels, der numerische Zeichen verarbeitet: ([A-Z0-9]+|[A-Z]?[a-z]+)(?=[A-Z0-9]|\b)
.
Dies ermöglicht das Aufteilen von Strings wie:
DrivingB2BTradeIn2019Onwards
zu
B2B-Handel ab 2019 vorantreiben
Anstatt nach Trennzeichen zu suchen, die nicht vorhanden sind könnten Sie auch die Namenskomponenten suchen (die sicherlich vorhanden sind)
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);
Dies gibt [Eclipse, 福福, RCP, Ext]
aus. Die Konvertierung in ein Array ist natürlich einfach.