54

物事を簡単にするために:

string streamR = sr.ReadLine();  // sr.Readline results in:
                                 //                         one "two two"

それらを2つの異なる文字列として保存できるようにしたいのですが、引用符の間にあるスペースを除いてすべてのスペースを削除してください。したがって、私が必要とするのは次のとおりです。

string 1 = one
string 2 = two two

これまでのところ、機能することがわかっているのは次のコードですが、引用符内のスペースが削除されています。

//streamR.ReadLine only has two strings
  string[] splitter = streamR.Split(' ');
    str1 = splitter[0];
    // Only set str2 if the length is >1
    str2 = splitter.Length > 1 ? splitter[1] : string.Empty;

これの出力は次のようになります

one
two

引用符で囲まれていない限り、正規表現をスペースで分割するように調べましたが、正規表現を機能させたり、コードを理解したりすることができないようです。特に、それらを分割して2つの異なる文字列にする方法はありません。そこにあるすべてのコードでコンパイルエラーが発生します(使用していますSystem.Text.RegularExpressions

4

7 に答える 7

59
string input = "one \"two two\" three \"four four\" five six";
var parts = Regex.Matches(input, @"[\""].+?[\""]|[^ ]+")
                .Cast<Match>()
                .Select(m => m.Value)
                .ToList();
于 2013-02-01T21:15:17.670 に答える
39

正規表現なしでそれを行うこともできます。LINQ式を使用するとString.Split、その仕事を行うことができます。

前に文字列を分割"してから、結果の配列内のインデックスが偶数の要素のみをで分割 できます

var result = myString.Split('"')
                     .Select((element, index) => index % 2 == 0  // If even index
                                           ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                           : new string[] { element })  // Keep the entire item
                     .SelectMany(element => element).ToList();

文字列の場合:

This is a test for "Splitting a string" that has white spaces, unless they are "enclosed within quotes"

結果は次のようになります。

This
is
a
test
for
Splitting a string
that
has
white
spaces,
unless
they
are
enclosed within quotes

アップデート

string myString = "WordOne \"Word Two\"";
var result = myString.Split('"')
                     .Select((element, index) => index % 2 == 0  // If even index
                                           ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                           : new string[] { element })  // Keep the entire item
                     .SelectMany(element => element).ToList();

Console.WriteLine(result[0]);
Console.WriteLine(result[1]);
Console.ReadKey();

更新2

文字列の引用部分をどのように定義しますか?

"最初の文字列の前の文字列は引用符で囲まれていないと想定します。

次に、最初の文字列"と2番目の文字列の前に配置された文字列"が引用されます。"2番目と3番目の間の文字列は"引用符で囲まれていません。3番目と4番目の間の文字列は引用符で囲まれています...

一般的な規則は次のとおりです。(2 * n-1)番目(奇数)"と(2 * n)番目(偶数)の間の各文字列"は引用符で囲まれます。(1)

との関係は何String.Splitですか?

String.SplitとデフォルトのStringSplitOption(StringSplitOption.Noneとして定義)は、1つの文字列のリストを作成し、見つかった分割文字ごとにリストに新しい文字列を追加します。

したがって、最初のの前に"、文字列は分割された配列のインデックス0にあり、1番目と2番目の間にあり"、文字列は配列のインデックス1にあり、3番目と4番目のインデックス2の間にあります。

一般的な規則は次のとおりです。n番目と(n + 1)番目の間の文字列は"、配列のインデックスnにあります。(2)

与えられた(1)(2)、次のように結論付けることができます。引用符で囲まれた部分は分割された配列の奇数インデックスにあります。

于 2013-02-01T21:18:55.703 に答える
14

カスタムパーサーがこれにより適している可能性があるためです。

これは、括弧とスペースを含む特定の (そして非常に奇妙な) 解析要件があったときに私が書いたものですが、実質的にすべての区切り記号とテキスト修飾子で動作するように十分に一般的です。

public static IEnumerable<String> ParseText(String line, Char delimiter, Char textQualifier)
{

    if (line == null)
        yield break;

    else
    {
        Char prevChar = '\0';
        Char nextChar = '\0';
        Char currentChar = '\0';

        Boolean inString = false;

        StringBuilder token = new StringBuilder();

        for (int i = 0; i < line.Length; i++)
        {
            currentChar = line[i];

            if (i > 0)
                prevChar = line[i - 1];
            else
                prevChar = '\0';

            if (i + 1 < line.Length)
                nextChar = line[i + 1];
            else
                nextChar = '\0';

            if (currentChar == textQualifier && (prevChar == '\0' || prevChar == delimiter) && !inString)
            {
                inString = true;
                continue;
            }

            if (currentChar == textQualifier && (nextChar == '\0' || nextChar == delimiter) && inString)
            {
                inString = false;
                continue;
            }

            if (currentChar == delimiter && !inString)
            {
                yield return token.ToString();
                token = token.Remove(0, token.Length);
                continue;
            }

            token = token.Append(currentChar);

        }

        yield return token.ToString();

    } 
}

使用法は次のとおりです。

var parsedText = ParseText(streamR, ' ', '"');
于 2013-02-01T21:17:36.097 に答える
12

名前空間の一部であるTextFieldParserクラスを使用できます。Microsoft.VisualBasic.FileIO(プロジェクトへの参照を追加する必要がありMicrosoft.VisualBasicます。):

string inputString = "This is \"a test\" of the parser.";

using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(inputString)))
{
    using (Microsoft.VisualBasic.FileIO.TextFieldParser tfp = new TextFieldParser(ms))
    {
        tfp.Delimiters = new string[] { " " };
        tfp.HasFieldsEnclosedInQuotes = true;
        string[] output = tfp.ReadFields();

        for (int i = 0; i < output.Length; i++)
        {
            Console.WriteLine("{0}:{1}", i, output[i]);
        }
    }
}

これは出力を生成します:

0:This
1:is
2:a test
3:of
4:the
5:parser.
于 2013-02-01T21:18:58.807 に答える
0

OPはやりたかった

...引用符の間にあるスペースを除くすべてのスペースを削除します

Cédric Bignon の解決策はほぼこれを行いましたが、引用符の数が不均等になる可能性があることを考慮していませんでした。これをチェックしてから余分なものを削除することから始めて、要素が実際に引用符でカプセル化されている場合にのみ分割を停止するようにします。

string myString = "WordOne \"Word Two";
int placement = myString.LastIndexOf("\"", StringComparison.Ordinal);
if (placement >= 0)
myString = myString.Remove(placement, 1);

var result = myString.Split('"')
                     .Select((element, index) => index % 2 == 0  // If even index
                                           ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)  // Split the item
                                           : new string[] { element })  // Keep the entire item
                     .SelectMany(element => element).ToList();

Console.WriteLine(result[0]);
Console.WriteLine(result[1]);
Console.ReadKey();

ロジックの功績は Cédric Bignon のおかげです。私はセーフガードを追加しただけです。

于 2016-02-16T09:25:20.013 に答える