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


114

私は正規表現に不慣れなので、あなたの助けに感謝します。一重引用符または二重引用符で囲まれていないすべてのスペースを使用してサンプル文字列を分割する式をまとめようとしています。私の最後の試みは次のようになります:(?!")うまくいきません。見積もりの​​前のスペースで分割されています。

入力例:

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'単語間のスペースを保持します。


実際に「分割」メソッドを使用していますか、またはMatcherの「検索」メソッドでループするだけで十分ですか?
エリクソン2008

9
「そして今、彼には2つの問題があります」

回答:


251

他のすべての人がそのような複雑な正規表現や長いコードを提案している理由がわかりません。基本的に、文字列から2種類のものを取得する必要があります。スペースや引用符ではない文字のシーケンスと、2種類の引用符の間に引用符を入れずに引用符で開始および終了する文字のシーケンスです。これらのことは、次の正規表現で簡単に照合できます。

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

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

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

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

1
1月、返信ありがとうございます。ところで、私はEditPadの大ファンです。
carlsz 2008

文字列でエスケープされた引用符を許可したい場合はどうなります\"か?
Monstieur 2014年

3
この答えの問題は、比類のない引用です: John's mother結果は[John, s, mother]
分割さ

2
leonbloyアウトラインの問題を修正するには、オペランドを少し並べ替えて、空白スペースグループから引用符を省略します"([^"]*)"|'([^']*)'|[^\s]+
ゴーストキーパー、2014

1
これと他の答えに基づいて、次の正規表現では、引用符内の文字をエスケープできます"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+stackoverflow.com/questions/5695240/…を
Limnic

15

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

UPDATE:単一引用符と二重引用符で囲まれた文字列を処理するサンプル正規表現 参照:引用符の内側を除いて、どうすれば文字列を分割できますか

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

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

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

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


あなたの正規表現では、「will be」と「regular expression」のように、引用符の不一致が許容されると思います。
Zach Scrivena 2008

@ザック-あなたは正しい、それはそうします...念のためそれを修正するために更新しました
ジェイ

6

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

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

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

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


3

Jan Goyvaertsの正規表現は、これまでに見つけた最良の解決策ですが、空(null)の一致も作成するため、プログラムでは除外します。これらの空の一致は、正規表現のテスター(例:rubular.com)からも表示されます。検索を逆転させる場合(最初に引用符で囲まれた部分を検索し、スペースで区切られた単語を検索します)、次のようにして一度に実行できます。

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

2
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

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


1

おそらく、文字列を検索して、各部分を取得するよりも、分割する方が簡単です。

理由は、前と後のスペースで分割することです"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"

このソリューションは、Carlの例の一部である、単一引用符で囲まれた文字列を処理しません。
Jan Goyvaerts、2008

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

私はマーカスのアプローチが好きでしたが、引用符の近くにテキストを許可し、 "と 'の両方の引用文字をサポートできるように変更しました。たとえば、[a =、"に分割しないようにa = "some value"が必要でしたいくつかの値 "]。

(?<!\\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

Janのアプローチは素晴らしいですが、ここにもう1つ記録を示します。

タイトルで述べたように実際に分割して、引用符を"will be"とのままにし'regular expression'たい場合は、このメソッドを使用できます。このメソッドは、状況s1、s2、s3などを除いて、パターンと直接一致(または置換)できます。

正規表現:

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

左の2つの代替は、complete '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

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

0

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


それは正規表現で可能です、私がリンクしたサンプルのいくつかを参照してください。これにはいくつかのバリエーションがあり、正規表現を介してこれに対処するSOに関するいくつかの同様の質問を見てきました。
ジェイ

1
正規表現を使用しない方が、(?:(['"])(。*?)(?<!\)(?> \\\)* \ 1 |([ ^ \ s] +))
ルネ

0

カップルがJanの回答をうまく活用できるように調整してください:

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

以下のような彼らでアポストロフィ、この休憩の言葉you're
エイドリアンデザイン

0

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

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

なぜこれが機能するのかについていくつかの説明を追加する必要があります-コードとコード自体にコメントを追加することもできます-現在の形式では、コミュニティの他のメンバーが何を理解するのに役立つ説明はありませんあなたは質問を解決/答えるためにやった。これは、すでに回答がある質問では特に重要です。
ishmaelMakitla 2016

0

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

using System.Text.RegularExpressions;

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

2
他の人がより簡単に理解できるように、回答に少し説明を追加できますか?理想的には、コードのみの回答は避けたいです。
Jaquez

0

String.split()を使用した最初のワンライナー

String s = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
String[] split = s.split( "(?<!(\"|').{0,255}) | (?!.*\\1.*)" );

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

空白で分割しないでください。空白が単一引用符または二重引用符で囲まれてい
て、空白の左側の255文字と右側のすべての文字が単一引用符でも二重引用符でもない場合、空白で分割されます。

元の投稿 からの変更(二重引用符のみを処理)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.