Ich habe ein brillantes RegEx gefunden , um den Teil eines camelCase- oder TitleCase-Ausdrucks zu extrahieren.
(?<!^)(?=[A-Z])
Es funktioniert wie erwartet:
- Wert -> Wert
- camelValue -> camel / Value
- TitleValue -> Titel / Wert
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:
- Fall 1: WERT -> V / A / L / U / E.
- Fall 2: eclipseRCPExt -> eclipse / R / C / P / Ext
Meiner Meinung nach sollte das Ergebnis sein:
- Fall 1: WERT
- Fall 2: Eclipse / RCP / Ext
Mit anderen Worten, wenn n Großbuchstaben angegeben sind:
- Wenn auf die n Zeichen Zeichen in Kleinbuchstaben folgen, sollten die Gruppen sein: (n-1 Zeichen) / (n-te Zeichen + Kleinzeichen)
- Wenn die n Zeichen am Ende sind, sollte die Gruppe sein: (n Zeichen).
Irgendeine Idee, wie man diesen regulären Ausdruck verbessern kann?
java
regex
camelcasing
title-case
Jmini
quelle
quelle
^
und einen anderen bedingten Fall für Großbuchstaben im negativen Lookbehind benötigen würden. Ich bin mir nicht sicher, aber ich denke, das wäre die beste Wahl, um das Problem zu beheben.Antworten:
Der folgende reguläre Ausdruck funktioniert für alle oben genannten 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 funktioniert, indem das negative Lookbehind gezwungen wird, Übereinstimmungen am Anfang der Zeichenfolge nicht nur zu ignorieren, sondern auch Übereinstimmungen zu ignorieren, bei denen einem Großbuchstaben ein anderer Großbuchstabe vorangestellt ist. Dies behandelt Fälle wie "VALUE".
Der erste Teil des regulären Ausdrucks schlägt bei "eclipseRCPExt" fehl, indem er nicht zwischen "RPC" und "Ext" aufgeteilt werden kann. Dies ist der Zweck der zweiten Klausel :
(?<!^)(?=[A-Z][a-z]
. Diese Klausel ermöglicht eine Aufteilung vor jedem Großbuchstaben, gefolgt von einem Kleinbuchstaben, außer am Anfang der Zeichenfolge.quelle
Es scheint, dass Sie dies komplizierter machen, als es sein muss. Bei camelCase befindet sich die geteilte Position einfach überall dort, wo ein Großbuchstabe unmittelbar auf einen Kleinbuchstaben folgt:
(?<=[a-z])(?=[A-Z])
So teilt dieser Regex Ihre Beispieldaten 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 behaupten würde, dass sie hier korrekt aufgeteilt ist.Nachtrag - Verbesserte Version
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 regulären Ausdruck werden alle Testfälle des OP korrekt aufgeteilt.
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])
So teilt der verbesserte Regex die Beispieldaten auf:
value -> value
camelValue -> camel / Value
TitleValue -> Title / Value
VALUE -> VALUE
eclipseRCPExt -> eclipse / RCP / Ext
Bearbeiten: 20130824 Verbesserte Version hinzugefügt, um
RCPExt -> RCP / Ext
Fall zu behandeln .quelle
(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[0-9])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])
Eine andere Lösung wäre die Verwendung einer dedizierten Methode in commons-lang : StringUtils # splitByCharacterTypeCamelCase
quelle
Ich konnte die Lösung von aix nicht zum Laufen bringen (und sie funktioniert auch nicht mit RegExr), also habe ich mir eine eigene ausgedacht, die ich getestet habe und die genau das zu tun scheint, 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. Hier sind einige Beispiele, wie die Zeichenfolge transformiert wird:
Diese obige Lösung macht das, was der ursprüngliche Beitrag verlangt, aber ich brauchte auch einen regulären Ausdruck, um Kamel- und Pascal-Strings zu finden, die Zahlen enthielten. Deshalb habe ich mir auch diese Variante ausgedacht, um Zahlen einzuschließen:
((^[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 diesem regulären Ausdruck transformiert wird:
quelle
(^[a-z]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|$))
für das erste und(^[a-z]+|[0-9]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|$|[0-9]))
für das zweite. Das äußerste kann auch entfernt werden, aber die Syntax, die sich auf die gesamte Übereinstimmung bezieht, ist nicht zwischen Sprachen portierbar ($0
und es$&
gibt zwei Möglichkeiten).([A-Z]?[a-z]+)|([A-Z]+(?=[A-Z][a-z]))
So verarbeiten Sie mehr Buchstaben als nur
A-Z
:s.split("(?<=\\p{Ll})(?=\\p{Lu})|(?<=\\p{L})(?=\\p{Lu}\\p{Ll})");
Entweder:
ZB
parseXML
->parse
,XML
.oder
ZB
XMLParser
->XML
,Parser
.In besser lesbarer Form:
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")); } }
quelle
Kurz
Beide Top-Antworten hier liefern Code mit positiven Lookbehinds, der nicht von allen Regex-Varianten unterstützt wird. Die Regex unten erfassen wird sowohl
PascalCase
undcamelCase
und kann in mehreren Sprachen verwendet werden.Hinweis: Mir ist klar, dass sich diese Frage auf Java bezieht. Ich sehe jedoch auch mehrere Erwähnungen dieses Beitrags in anderen Fragen, die für verschiedene Sprachen markiert sind, sowie einige Kommentare zu dieser Frage für dieselbe.
Code
Sehen Sie diesen regulären Ausdruck hier
Ergebnisse
Probeneingabe
Beispielausgabe
Erläuterung
[A-Z]+
[A-Z]?
, gefolgt von einem oder mehreren Kleinbuchstaben[a-z]+
[A-Z]
oder ein Wortbegrenzungszeichen ist\b
quelle
Sie können StringUtils verwenden. splitByCharacterTypeCamelCase ("loremIpsum") von Apache Commons Lang.
quelle
Sie können den folgenden Ausdruck für Java verwenden:
quelle
Anstatt nach Trennzeichen zu suchen, die nicht vorhanden sind, können Sie auch die Namenskomponenten suchen (diese sind sicherlich vorhanden):
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 aus
[eclipse, 福福, RCP, Ext]
. Die Konvertierung in ein Array ist natürlich einfach.quelle
Ich kann bestätigen, dass die
([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)
oben von ctwheels angegebene Regex-Zeichenfolge mit der Microsoft-Variante von Regex funktioniert.Ich möchte auch die folgende Alternative vorschlagen, die auf dem regulären Ausdruck von ctwheels basiert und numerische Zeichen verarbeitet :
([A-Z0-9]+|[A-Z]?[a-z]+)(?=[A-Z0-9]|\b)
.Dies kann Zeichenfolgen wie:
zu
quelle
Eine JavaScript-Lösung
/** * howToDoThis ===> ["", "how", "To", "Do", "This"] * @param word word to be split */ export const splitCamelCaseWords = (word: string) => { if (typeof word !== 'string') return []; return word.replace(/([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)/g, '!$&').split('!'); };
quelle