web-dev-qa-db-ja.com

一重引用符または二重引用符で囲まれていないときにスペースを使用して文字列を分割するための正規表現

私は正規表現に慣れていないので、ご協力をお願いします。一重引用符または二重引用符で囲まれていないすべてのスペースを使用して、サンプル文字列を分割する式をまとめようとしています。私の最後の試みは次のようになります:(?!")そして、まったく機能していません。引用符の前のスペースで分割されます。

入力例:

This is a string that "will be" highlighted when your 'regular expression' matches something.

望ましい出力:

This
is
a
string
that
will be
highlighted
when
your
regular expression
matches
something.

ご了承ください "will be"および'regular expression'単語間のスペースを保持します。

104
carlsz

他のすべてがこのような複雑な正規表現や長いコードを提案している理由がわかりません。基本的に、文字列から2種類の情報を取得する必要があります。2種類の引用符については、スペースでも引用符でもない文字列と、引用符で始まり、引用符なしで終わる文字列です。次の正規表現を使用して、これらのものを簡単に一致させることができます。

[^\s"']+|"([^"]*)"|'([^']*)'

リストに引用符が必要ないため、キャプチャグループを追加しました。

このJavaコードはリストを作成し、引用符を除外するために一致した場合はキャプチャグループを追加し、キャプチャグループが一致しなかった場合(引用されていないWordが一致した場合)全体の正規表現一致を追加します。

List<String> matchList = new ArrayList<String>();
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    if (regexMatcher.group(1) != null) {
        // Add double-quoted string without the quotes
        matchList.add(regexMatcher.group(1));
    } else if (regexMatcher.group(2) != null) {
        // Add single-quoted string without the quotes
        matchList.add(regexMatcher.group(2));
    } else {
        // Add unquoted Word
        matchList.add(regexMatcher.group());
    }
} 

返されるリストに引用符を入れてもかまわない場合は、もっと簡単なコードを使用できます。

List<String> matchList = new ArrayList<String>();
Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'");
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    matchList.add(regexMatcher.group());
} 
227
Jan Goyvaerts

StackOverflowには、正規表現を使用したさまざまなコンテキストでこの同じ質問をカバーするいくつかの質問があります。例えば:

[〜#〜] update [〜#〜]:一重引用符と二重引用符で囲まれた文字列を処理する正規表現のサンプル。参照: 引用符内にある場合を除き、文字列で分割するにはどうすればよいですか?

m/('.*?'|".*?"|\S+)/g 

これを簡単なPerlスニペットでテストし、出力は以下のようになりました。空の文字列または空白のみの文字列が引用符の間にある場合にも機能します(それが望ましいかどうかはわかりません)。

This
is
a
string
that
"will be"
highlighted
when
your
'regular expression'
matches
something.

これには一致した値に引用文字自体が含まれますが、文字列の置換で引用文字を削除するか、正規表現を含めないように正規表現を変更できます。午前2時は正規表現をいじるには遅すぎるので、読者または別のポスターの演習としてこれを残しておきます;)

13
Jay

文字列内でエスケープされた引用符を許可する場合は、次のようなものを使用できます。

(?:(['"])(.*?)(?<!\\)(?>\\\\)*\1|([^\s]+))

引用符で囲まれた文字列はグループ2になり、引用されていない単一の単語はグループ3になります。

ここでさまざまな文字列で試すことができます: http://www.fileformat.info/tool/regex.htm または http://gskinner.com/RegExr/

5
mcrumley

Jan Goyvaertsの正規表現は、私がこれまでに見つけた最適なソリューションですが、空の(null)マッチも作成しますが、彼はこれを彼のプログラムから除外しています。これらの空の一致は、正規表現テスターからも表示されます(例:rubular.com)。検索を方向転換する場合(最初に引用符で囲まれた部分を検索し、スペースで区切られた単語よりも先に検索する場合)、次のように1回で実行できます。

("[^"]*"|'[^']*'|[\S]+)+
3
iRon
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

これは、二重引用符で囲まれていないスペースと一致します。 Javaは後読みで*と+をサポートしていないため、min、max {0,99999}を使用する必要があります。

2

おそらく、文字列を検索し、各部分をつかんで、分割するよりも簡単でしょう。

理由は、"will be"の前後のスペースで分割することができます。しかし、スプリット内のスペースを無視することを指定する方法は考えられません。

(実際のJavaではない)

string = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";

regex = "\"(\\\"|(?!\\\").)+\"|[^ ]+"; // search for a quoted or non-spaced group
final = new Array();

while (string.length > 0) {
    string = string.trim();
    if (Regex(regex).test(string)) {
        final.Push(Regex(regex).match(string)[0]);
        string = string.replace(regex, ""); // progress to next "Word"
    }
}

また、一重引用符をキャプチャすると問題が発生する可能性があります。

"Foo's Bar 'n Grill"

//=>

"Foo"
"s Bar "
"n"
"Grill"
1

String.split()は、引用符内のスペース(分割しない)と外部のスペース(分割する)を区別する方法がないため、ここでは役に立ちません。 Matcher.lookingAt()はおそらく必要なものです:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
str = str + " "; // add trailing space
int len = str.length();
Matcher m = Pattern.compile("((\"[^\"]+?\")|('[^']+?')|([^\\s]+?))\\s++").matcher(str);

for (int i = 0; i < len; i++)
{
    m.region(i, len);

    if (m.lookingAt())
    {
        String s = m.group(1);

        if ((s.startsWith("\"") && s.endsWith("\"")) ||
            (s.startsWith("'") && s.endsWith("'")))
        {
            s = s.substring(1, s.length() - 1);
        }

        System.out.println(i + ": \"" + s + "\"");
        i += (m.group(0).length() - 1);
    }
}

次の出力が生成されます。

0: "This"
5: "is"
8: "a"
10: "string"
17: "that"
22: "will be"
32: "highlighted"
44: "when"
49: "your"
54: "regular expression"
75: "matches"
83: "something."
1
Zach Scrivena

C#を使用している場合は、次を使用できます。

string input= "This is a string that \"will be\" highlighted when your 'regular expression' matches <something random>";

List<string> list1 = 
                Regex.Matches(input, @"(?<match>\w+)|\""(?<match>[\w\s]*)""|'(?<match>[\w\s]*)'|<(?<match>[\w\s]*)>").Cast<Match>().Select(m => m.Groups["match"].Value).ToList();

foreach(var v in list1)
   Console.WriteLine(v);

具体的に「<(?[\ w\s] *)>」を追加して、フレーズをグループ化するために任意の文字を指定できることを強調しています。 (この場合、グループに<>を使用しています。

出力は次のとおりです。

This
is
a
string
that
will be
highlighted
when
your
regular expression 
matches
something random
1
Praveen Singh

Janのアプローチは素晴らしいですが、ここに記録のための別のアプローチがあります。

"will be"'regular expression'に引用符を入れたまま、タイトルに記載されているように実際に分割したい場合は、 パターンを照合(または置換)する)をそのまま使用できます。状況s1、s2、s3などを除く

正規表現:

'[^']*'|\"[^\"]*\"|( )

左の2つの代替は、完全な'quoted strings'"double-quoted strings"に一致します。これらの一致は無視されます。右側はスペースをグループ1に一致させてキャプチャしますが、左側の表現と一致しなかったため、スペースが正しいスペースであることがわかります。それらをSplitHereに置き換えてから、SplitHereで分割します。繰り返しますが、これは"will be"ではなくwill beが必要な真の分割の場合です。

完全に機能する実装を次に示します( オンラインデモ の結果を参照)。

import Java.util.*;
import Java.io.*;
import Java.util.regex.*;
import Java.util.List;

class Program {
public static void main (String[] args) throws Java.lang.Exception  {

String subject = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
Pattern regex = Pattern.compile("\'[^']*'|\"[^\"]*\"|( )");
Matcher m = regex.matcher(subject);
StringBuffer b= new StringBuffer();
while (m.find()) {
    if(m.group(1) != null) m.appendReplacement(b, "SplitHere");
    else m.appendReplacement(b, m.group(0));
}
m.appendTail(b);
String replaced = b.toString();
String[] splits = replaced.split("SplitHere");
for (String split : splits) System.out.println(split);
} // end main
} // end Program
1
zx81

Marcusのアプローチが好きでしたが、引用符の近くのテキストを許可し、 "と 'の両方の引用符文字をサポートできるように修正しました。たとえば、a =" some value "を[a =、"何らかの値」]。

(?<!\\G\\S{0,99999}[\"'].{0,99999})\\s|(?<=\\G\\S{0,99999}\".{0,99999}\"\\S{0,99999})\\s|(?<=\\G\\S{0,99999}'.{0,99999}'\\S{0,99999})\\s"
1
Eric Woodruff

次は、引数の配列を返します。引数は、単一引用符または二重引用符で囲まない限り、スペースで分割された変数「コマンド」です。次に、一致を修正して、一重引用符と二重引用符を削除します。

using System.Text.RegularExpressions;

var args = Regex.Matches(command, "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'").Cast<Match>
().Select(iMatch => iMatch.Value.Replace("\"", "").Replace("'", "")).ToArray();

正規表現だけではこれが不可能だと確信しています。他のタグ内に何かが含まれているかどうかを確認することは、解析操作です。これは、正規表現を使用してXMLを解析しようとするのと同じ問題のように思えます。正しく実行できません。引用符で囲まれた文字列に一致する貪欲でない、非グローバルな正規表現を繰り返し適用することで、目的の結果を得ることができる場合があります。すべての部分文字列の元の順序を追跡するなどの問題。あなたの最善の策は、文字列を反復処理し、必要なトークンを引き出す本当に簡単な関数を書くことです。

0
rmeador

Janの受け入れられた回答について、うまくいけば役立つ微調整がいくつかあります。

(['"])((?:\\\1|.)+?)\1|([^\s"']+)
  • 引用符付き文字列内のエスケープされた引用符を許可します
  • 一重引用符と二重引用符に対してパターンを繰り返すことを避けます。これにより、必要に応じて引用符を追加することも簡単になります(もう1つのキャプチャグループが犠牲になります)
0
pascals

これを試すこともできます:

    String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something";
    String ss[] = str.split("\"|\'");
    for (int i = 0; i < ss.length; i++) {
        if ((i % 2) == 0) {//even
            String[] part1 = ss[i].split(" ");
            for (String pp1 : part1) {
                System.out.println("" + pp1);
            }
        } else {//odd
            System.out.println("" + ss[i]);
        }
    }
0
Rakesh Sosa