web-dev-qa-db-ja.com

ラクダケースまたはタイトルケースを分割する正規表現(高度)

brilliant RegEx を見つけて、camelCaseまたはTitleCase式の一部を抽出しました。

 (?<!^)(?=[A-Z])

期待どおりに動作します:

  • 値->値
  • camelValue-> camel/Value
  • TitleValue->タイトル/値

たとえば、Javaの場合:

String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
//words equals words = new String[]{"lorem","Ipsum"}

私の問題は、場合によっては機能しないことです:

  • ケース1:VALUE-> V/A/L/U/E
  • ケース2:eclipseRCPExt-> Eclipse/R/C/P/Ext

私の考えでは、結果は次のようになります。

  • ケース1:VALUE
  • ケース2:Eclipse/RCP/Ext

つまり、n個の大文字が与えられた場合:

  • n文字の後に小文字の文字が続く場合、グループは次のようになります。(n-1文字)/(n番目の文字+小文字)
  • n個の文字が最後にある場合、グループは(n個の文字)である必要があります。

この正規表現を改善する方法についてのアイデアはありますか?

74
Jmini

次の正規表現は、上記のすべての例で機能します。

public static void main(String[] args)
{
    for (String w : "camelValue".split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) {
        System.out.println(w);
    }
}   

文字列の先頭で一致を無視するだけでなく、大文字の前に別の大文字が含まれる一致を無視するように、負の後ろ読みを強制することで機能します。これにより、「VALUE」などのケースが処理されます。

正規表現の最初の部分は、「eclipseRCPExt」で「RPC」と「Ext」に分割できないため、失敗します。これが2番目の句の目的です:(?<!^)(?=[A-Z][a-z]。この句により、文字列の先頭を除いて、小文字が続くすべての大文字の前で分割できます。

101
NPE

これを必要以上に複雑にしているようです。 camelCaseの場合、分割位置は単に、大文字が小文字の直後に続く任意の場所です。

(?<=[a-z])(?=[A-Z])

この正規表現がサンプルデータを分割する方法は次のとおりです。

  • value -> value
  • camelValue -> camel / Value
  • TitleValue -> Title / Value
  • VALUE -> VALUE
  • eclipseRCPExt -> Eclipse / RCPExt

目的の出力との唯一の違いはeclipseRCPExtであり、ここで正しく分割されると主張します。

補遺-改良版

注:この回答は最近賛成票を得たので、もっと良い方法があることに気付きました...

上記の正規表現に2番目の選択肢を追加すると、OPのすべてのテストケースが正しく分割されます。

(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])

改良された正規表現がサンプルデータを分割する方法は次のとおりです。

  • value -> value
  • camelValue -> camel / Value
  • TitleValue -> Title / Value
  • VALUE -> VALUE
  • eclipseRCPExt -> Eclipse / RCP / Ext

編集:20130824RCPExt -> RCP / Extケース。

70
ridgerunner

別の解決策は、 commons-lang で専用メソッドを使用することです。 StringUtils#splitByCharacterTypeCamelCase

28
YMomb

私はaixのソリューションを動作させることができませんでした(そして、それもRegExrで動作しません)。

((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))

そして、それを使用する例を次に示します。

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

ここでは、各Wordをスペースで区切ります。そのため、文字列の変換方法の例をいくつか示します。

  • ThisIsATitleCASEString =>これはタイトルCASE文字列です
  • andThisOneIsCamelCASE =>そして、これはキャメルケースです

上記のこのソリューションは元の投稿が求めることを行いますが、数字を含むラクダとPascalの文字列を見つけるための正規表現も必要だったので、数字を含めるためにこのバリエーションを思い付きました:

((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))

そしてそれを使用する例:

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

そして、この正規表現を使用して数字を含む文字列を変換する方法の例を次に示します。

  • myVariable123 => my Variable 123
  • my2Variables => my 2変数
  • The3rdVariableIsHere => 3 rdVariable Is Here
  • 12345NumsAtTheStartIncludedToo => 12345 Nums at the Start Included Too
10
deadlydog

A-Z以外の文字を処理するには:

s.split("(?<=\\p{Ll})(?=\\p{Lu})|(?<=\\p{L})(?=\\p{Lu}\\p{Ll})");

どちらか:

  • 小文字の後に分割し、その後に大文字を続けます。

例:parseXML-> parseXML

または

  • 任意の文字の後に分割し、その後に大文字と小文字を続けます。

例えば。 XMLParser-> XMLParser


より読みやすい形式で:

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

簡単な

ここでの両方の上位の回答は、すべての正規表現フレーバーでサポートされているわけではない、ポジティブルックビハインドを使用したコードを提供します。以下の正規表現は、PascalCasecamelCaseの両方をキャプチャし、複数の言語で使用できます。

注:この質問はJavaに関するものであることに気づきましたが、さまざまな言語にタグ付けされた他の質問や、この質問に対するいくつかのコメント。

コード

使用中のこの正規表現を参照してください

([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

説明

  • 1つ以上の大文字のアルファベット[A-Z]+に一致
  • または0個または1個の大文字のアルファベット文字[A-Z]?に一致し、その後に1個以上の小文字のアルファベット文字[a-z]+が続く
  • 後続の文字が大文字の[A-Z]またはワード境界文字\bであることを確認してください
2
ctwheels

次の式をJavaに使用できます。

(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?=[A-Z][a-z])|(?<=\\d)(?=\\D)|(?=\\d)(?<=\\D)
0
Maicon Zucco

正規表現文字列([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)上記のctwheelsによって与えられたものは、マイクロソフトの正規表現と連動します。

また、ctwheelsの正規表現に基づいて、数字を処理する次の代替案を提案したいと思います:([A-Z0-9]+|[A-Z]?[a-z]+)(?=[A-Z0-9]|\b)

これにより、次のような文字列を分割できます。

運転B2BTradeIn2019以降

2019年以降のB2Bトレードの推進

0
William Bell

存在しないの区切り文字を探す代わりに、名前コンポーネントの検索を検討することもできます(確かに存在します)。

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

これは[Eclipse, 福福, RCP, Ext]を出力します。配列への変換はもちろん簡単です。

0
Maarten Bodewes