2011-12-29 3 views
3

Я хочу, чтобы разбить строку пробелами, за исключением, если текст внутри строки в двойных кавычках («текст») или одинарные кавычки («текст»).Сплит строки пробелами в C#

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

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 и настройки \ Program Files \ abc.exe"

в

% ABC%
% aasdf%
aalasdjjfas
«C: \ Document и Настройка \ Program Files \ abc.exe»

+0

Так найти регулярное выражение CSV и приспособить его для использования в '\ s' вместо запятой? –

+0

@BradChristie Я отредактировал свой quiestion о том, как я хочу выход.Я не разбавляю CSV REGEX, помог бы – Ankesh

ответ

6

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

("((\\")|([^"]))*")|('((\\')|([^']))*')|(\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 
+0

Да, его работа над двойными кавычками, но не по ОДНОМУ ЦИТАТАМ ex-% ABC%% aasdf% aalasdjjfas "c: \ Doctment and Setting \ Program Files \ abc.exe" 'c: \ Doctment and Setting \ Program Files \ abc.exe ' – Ankesh

+0

Обновленный мой ответ также включает одинарные кавычки. –

+0

Ваше Regex работает хорошо ... :). Спасибо :) – Ankesh

2

Если вам не нравится 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(); 
} 

Изменить: Изменен return type to IEnumerable, используя yield и StringBuilder

+0

Это будет производить довольно много GC'able временных строк, не так ли? –

+1

Если вы не собираетесь ударять по результатам более одного раза, а просто пропустите через них, а затем измените возвращаемый тип на 'IEumerable ' и замените вызовы' output.Add' 'yield return curentString;' это хорошая идея. Это также случай, когда использование 'StringBuilder', а не множество конкатенаций имеет смысл. –

+0

Я полностью согласен, @JonHanna. 'yield return' - недоиспользуемая функция C#. Аргумент 'StringBuilder' является допустимым, но поскольку он, вероятно, используется только для синтаксического анализа последовательности аргументов командной строки, удар производительности не огромен. Но, тем не менее, нет никакого оправдания для неаккуратного кода. –

2

Я избегаю одиночных и двойных кавычек, используя шестнадцатеричные значения \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 
*/ 

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

+1

Я рад, что вы нашли свой ответ. Я твердо верю в регулярное выражение, и если люди будут тратить время на его изучение, это мощный инструмент, который независимо от языка (C#/Java/php) может использовать его для одного и того же эффекта во всем. :-) – OmegaMan

Смежные вопросы