Разбить строки по пробелам в C # - PullRequest
3 голосов
/ 29 декабря 2011

Я хочу разбить строку на пробелы, за исключением случаев, когда текст внутри строки содержит двойные кавычки ("текст") или одинарные кавычки ('текст').

Я делаю это с помощью этой функции:

public static string[] ParseKeywordExpression(string keywordExpressionValue, bool isUniqueKeywordReq)
{
    keywordExpressionValue = keywordExpressionValue.Trim();
    if (keywordExpressionValue == null || !(keywordExpressionValue.Length > 0))
        return new string[0];
    int idx = keywordExpressionValue.Trim().IndexOf(" ");
    if (idx == -1)
        return new string[] { keywordExpressionValue };
    //idx = idx + 1;
    int count = keywordExpressionValue.Length;
    ArrayList extractedList = new ArrayList();
    while (count > 0)
    {
        if (keywordExpressionValue[0] == '"')
        {
            int temp = keywordExpressionValue.IndexOf(BACKSLASH, 1, keywordExpressionValue.Length - 1);
            while (keywordExpressionValue[temp - 1] == '\\')
            {
                temp = keywordExpressionValue.IndexOf(BACKSLASH, temp + 1, keywordExpressionValue.Length - temp - 1);
            }
            idx = temp + 1;
        }
        if (keywordExpressionValue[0] == '\'')
        {
            int temp = keywordExpressionValue.IndexOf(BACKSHASH_QUOTE, 1, keywordExpressionValue.Length - 1);
            while (keywordExpressionValue[temp - 1] == '\\')
            {
                temp = keywordExpressionValue.IndexOf(BACKSHASH_QUOTE, temp + 1, keywordExpressionValue.Length - temp - 1);
            }
            idx = temp + 1;
        }
        string s = keywordExpressionValue.Substring(0, idx);
        int left = count - idx;
        keywordExpressionValue = keywordExpressionValue.Substring(idx, left).Trim();
        if (isUniqueKeywordReq)                    
        {
            if (!extractedList.Contains(s.Trim('"')))
            {
                extractedList.Add(s.Trim('"'));
            }
        }
        else
        {
            extractedList.Add(s.Trim('"'));
        }
        count = keywordExpressionValue.Length;
        idx = keywordExpressionValue.IndexOf(SPACE);
        if (idx == -1)
        {
            string add = keywordExpressionValue.Trim('"', ' ');
            if (add.Length > 0)
            {
                if (isUniqueKeywordReq )
                {
                    if (!extractedList.Contains(add))
                    {
                        extractedList.Add(add);
                    }
                }
                else
                {
                    extractedList.Add(add);
                }
            }                   
            break;
        }
    }
    return (string[])extractedList.ToArray(typeof(string));
}

Есть ли другой способ сделать это или эту функцию можно оптимизировать?

Например, я хочу разбить строку

% ABC%% aasdf% aalasdjjfas "c: \ Document and Setting \ Program Files \ abc.exe"

до

% ABC% * * тысяча двадцать-одна % Aasdf%
aalasdjjfas
"c: \ Document and Setting \ Program Files \ abc.exe"

Ответы [ 3 ]

6 голосов
/ 29 декабря 2011

Самое простое регулярное выражение для этого, обработка одинарных и двойных кавычек:

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

var regex = new Regex(@"(""((\\"")|([^""]))*"")|('((\\')|([^']))*')|(\S+)");
var matches = regex.Matches(inputstring);
foreach (Match match in matches) {
    extractedList.Add(match.Value);
}

Таким образом, достаточно от четырех до пяти строк кода.

Выражение, объяснил:

Main structure:
("((\\")|([^"]))*")    Double-quoted token
|                      , or
('((\\')|([^']))*')    single-quoted token
|                      , or
(\S+)                  any group of non-space characters

Double-quoted token:
(                      Group starts
    "                  Initial double-quote
    (                  Inner group starts
        (\\")          Either a backslash followed by a double-quote
        |              , or  
        ([^"])         any non-double-quote character
    )*                 The inner group repeats any number of times (or zero)
    "                  Ending double-quote
)

Single-quoted token:
(                      Group starts
    '                  Initial single-quote
    (                  Inner group starts
        (\\')          Either a backslash followed by a single-quote
        |              , or  
        ([^'])         any non-single-quote character
    )*                 The inner group repeats any number of times (or zero)
    '                  Ending single-quote
)

Non-space characters:
(                      Group starts
    \S                 Non-white-space character
    +                  , repeated at least once
)                      Group ends
2 голосов
/ 30 декабря 2011

Я избегаю одинарных и двойных кавычек, используя шестнадцатеричные значения \x27 и \x22 в строке.Это облегчает чтение и манипулирование буквальным текстом шаблона C #.

Также используется IgnorePatternWhitespace, поскольку он позволяет комментировать шаблон для лучшей читаемости;не влияет на обработку регулярных выражений.

string data = @"'single' %ABC% %aasdf% aalasdjjfas ""c:\Document and Setting\Program Files\abc.exe""";

string pattern = @"(?xm)     # Tell the regex compiler we are commenting (x = IgnorePatternWhitespace)
                             # and tell the compiler this is multiline (m),
                             # In Multiline the ^ matches each start line and $ is each EOL
                             # -Pattern Start-
^(                           # Start at the beginning of the line always
 (?![\r\n]|$)                # Stop the match if EOL or EOF found.
 (?([\x27\x22])              # Regex If to check for single/double quotes
      (?:[\x27\x22])         # \\x27\\x22 are single/double quotes
      (?<Token>[^\x27\x22]+) # Match this in the quotes and place in Named match Token
      (?:[\x27\x22])

  |                          # or (else) part of If when Not within quotes

     (?<Token>[^\s\r\n]+)    # Not within quotes, but put it in the Token match group
  )                          # End of Pattern OR

(?:\s?)                       # Either a space or EOL/EOF
)+                            # 1 or more tokens of data.
";

Console.WriteLine( string.Join(" | ", 

Regex.Match(data, pattern)
        .Groups["Token"]
        .Captures
        .OfType<Capture>()
        .Select( cp => cp.Value )
                )
                );
/* Output
single | %ABC% | %aasdf% | aalasdjjfas | c:\Document and Setting\Program Files\abc.exe
 */

Выше написано на основе следующих двух записей моего блога:

2 голосов
/ 29 декабря 2011

Если вам не нравится RegEx, этот метод должен иметь возможность разбивать строки в кавычках и игнорировать последовательные пробелы:

public IEnumerable<string> SplitString(string input)
{
    var isInDoubleQuote = false;
    var isInSingleQuote = false;
    var sb = new StringBuilder();
    foreach (var c in input)
    {
        if (!isInDoubleQuote && c == '"')
        {
            isInDoubleQuote = true;
            sb.Append(c);
        }
        else if (isInDoubleQuote)
        {
            sb.Append(c);
            if (c != '"')
                continue;
            if (sb.Length > 2)
                yield return sb.ToString();
            sb = sb.Clear();
            isInDoubleQuote = false;
        }
        else if (!isInSingleQuote && c == '\'')
        {
            isInSingleQuote = true;
            sb.Append(c);
        }
        else if (isInSingleQuote)
        {
            sb.Append(c);
            if (c != '\'')
                continue;
            if (sb.Length > 2)
                yield return sb.ToString();
            sb = sb.Clear();
            isInSingleQuote = false;
        }
        else if (c == ' ')
        {
            if (sb.Length == 0)
                continue;
            yield return sb.ToString();
            sb.Clear();
        }
        else
            sb.Append(c);
    }
    if (sb.Length > 0)
        yield return sb.ToString();
}

Редактировать: Изменен тип возвращаемого значения на IEnumerable, используя yield и StringBuilder.

...