Каков наилучший способ разобрать эту строку в C #? - PullRequest
24 голосов
/ 31 мая 2011

У меня есть строка, которую я читаю из другой системы. В основном это длинная строка, представляющая список пар ключ-значение, разделенных пробелом между ними. Это выглядит так:

 key:value[space]key:value[space]key:value[space]

Итак, я написал этот код для его анализа:

string myString = ReadinString();
string[] tokens = myString.split(' ');
foreach (string token in tokens) {
     string key = token.split(':')[0];
     string value = token.split(':')[1];
     .  . . . 
}

Проблема в том, что в некоторых значениях есть пробелы, поэтому мое «упрощенное» разбиение вверху больше не работает. Я хотел посмотреть, как я все еще могу разобрать список пар ключ-значение (заданный пробел в качестве символа разделителя), теперь я знаю, что в поле значения также могут быть пробелы, так как разделение не похоже на то, что оно сможет работать больше.

ПРИМЕЧАНИЕ. Теперь я подтвердил, что в КЛЮЧАХ НЕ будет пробелов, поэтому мне нужно беспокоиться только о значениях. Извиняюсь за путаницу.

Ответы [ 9 ]

22 голосов
/ 31 мая 2011

Используйте это регулярное выражение:

\w+:[\w\s]+(?![\w+:])

Я проверял его на

test:testvalue test2:test value test3:testvalue3

Возвращает три совпадения:

test:testvalue
test2:test value
test3:testvalue3

Вы можете изменить \wк любому набору символов, который может встречаться в вашем вводе.

Код для проверки этого:

var regex = new Regex(@"\w+:[\w\s]+(?![\w+:])");
var test = "test:testvalue test2:test value test3:testvalue3";

foreach (Match match in regex.Matches(test))
{
    var key = match.Value.Split(':')[0];
    var value = match.Value.Split(':')[1];

    Console.WriteLine("{0}:{1}", key, value);
}
Console.ReadLine();

Как указал Вонко Сан, это регулярное выражение не будет работать со значениями с :.Если вы прогнозируете такую ​​ситуацию, используйте \w+:[\w: ]+?(?![\w+:]) в качестве регулярного выражения.Это все равно не получится, если двоеточию в value предшествует пробел, хотя ... Я подумаю над решением этой проблемы.

5 голосов
/ 31 мая 2011

Это не может работать без изменения вашего разбиения с пробела на что-то другое, например, "|".

Учтите это:

Альфред Бестер: Альфред Бестер Альфред : Альфред Бестер

  • Является ли этот ключ «Альфред Бестер» и значением «Альфред» или ключ «Альфред» и значением «Бестер Альфред»?
4 голосов
/ 31 мая 2011
string input = "foo:Foobarius Maximus Tiberius Kirk bar:Barforama zap:Zip Brannigan";

foreach (Match match in Regex.Matches(input, @"(\w+):([^:]+)(?![\w+:])"))
{
   Console.WriteLine("{0} = {1}", 
       match.Groups[1].Value, 
       match.Groups[2].Value
      );
}

Дает вам:

foo = Foobarius Maximus Tiberius Kirk
bar = Barforama
zap = Zip Brannigan
2 голосов
/ 31 мая 2011

Вы можете попытаться Url-кодировать содержимое между пробелами ( Ключи и значения не символ: ), но для этого вам потребуется контроль над методом ввода.

Или вы можете просто использовать другой формат (например, XML или JSON), но вам снова потребуется контроль над форматом ввода.

Если вы не можете контролировать формат ввода, вы всегда можете использоватьa Регулярное выражение, которое ищет единичные пробелы, за которыми следует слово плюс:

Обновление (спасибо Джону Гранту) Похоже, у вас могут быть пробелы в ключе и значении.Если это так, вам нужно серьезно пересмотреть свою стратегию, поскольку даже Regex не поможет.

1 голос
/ 31 мая 2011

Использование регулярного выражения может решить вашу проблему:

private void DoSplit(string str)
{
    str += str.Trim() + " ";
    string patterns = @"\w+:([\w+\s*])+[^!\w+:]";
    var r = new System.Text.RegularExpressions.Regex(patterns);
    var ms = r.Matches(str);
    foreach (System.Text.RegularExpressions.Match item in ms)
    {
        string[] s = item.Value.Split(new char[] { ':' });
        //Do something
    }
}
1 голос
/ 31 мая 2011
string input = "key1:value key2:value key3:value";
Dictionary<string, string> dic = input.Split(' ').Select(x => x.Split(':')).ToDictionary(x => x[0], x => x[1]);

Первый создаст массив:

"key:value", "key:value"

Затем массив массивов:

{ "key", "value" }, { "key", "value" }

А затем словарь:

"key" => "value", "key" => "value"

Обратите внимание, что Dictionary<K,V> не допускает дублирования ключей, в таком случае это вызовет исключение.Если такой сценарий возможен, используйте ToLookup().

0 голосов
/ 31 мая 2011

Без каких-либо регулярных выражений и конкатов строк и как перечислимое (предполагается, что ключи не имеют пробелов, но значения могут):

    public static IEnumerable<KeyValuePair<string, string>> Split(string text)
    {
        if (text == null)
            yield break;

        int keyStart = 0;
        int keyEnd = -1;
        int lastSpace = -1;
        for(int i = 0; i < text.Length; i++)
        {
            if (text[i] == ' ')
            {
                lastSpace = i;
                continue;
            }

            if (text[i] == ':')
            {
                if (lastSpace >= 0)
                {
                    yield return new KeyValuePair<string, string>(text.Substring(keyStart, keyEnd - keyStart), text.Substring(keyEnd + 1, lastSpace - keyEnd - 1));
                    keyStart = lastSpace + 1;
                }
                keyEnd = i;
                continue;
            }
        }
        if (keyEnd >= 0)
            yield return new KeyValuePair<string, string>(text.Substring(keyStart, keyEnd - keyStart), text.Substring(keyEnd + 1));
    }
0 голосов
/ 31 мая 2011

Этот код сделает это (учитывая правила ниже). Он анализирует ключи и значения и возвращает их в Dictonary<string, string> структуре данных. В конце я добавил некоторый код, который предполагает, что в вашем примере к последнему значению всей строки / потока добавляется [пробел]: * ​​1002 *

private Dictionary<string, string> ParseKeyValues(string input)
        {
            Dictionary<string, string> items = new Dictionary<string, string>();

            string[] parts = input.Split(':');

            string key = parts[0];
            string value;

            int currentIndex = 1;

            while (currentIndex < parts.Length-1)
            {
                int indexOfLastSpace=parts[currentIndex].LastIndexOf(' ');
                value = parts[currentIndex].Substring(0, indexOfLastSpace);
                items.Add(key, value);
                key = parts[currentIndex].Substring(indexOfLastSpace + 1);
                currentIndex++;
            }
            value = parts[parts.Length - 1].Substring(0,parts[parts.Length - 1].Length-1);


            items.Add(key, parts[parts.Length-1]);

            return items;

        }

Примечание: этот алгоритм предполагает следующие правила:

  1. Нет пробелов в значениях
  2. Нет ключей в ключах
  3. Нет двоеточий в значениях
0 голосов
/ 31 мая 2011

Полагаю, вы могли бы взять свой метод и немного расширить его, чтобы разобраться с этим материалом ...

Вид псевдокода:

List<string> parsedTokens = new List<String>();
string[] tokens = myString.split(' ');
for(int i = 0; i < tokens.Length; i++)
{
    // We need to deal with the special case of the last item, 
    // or if the following item does not contain a colon.
    if(i == tokens.Length - 1 || tokens[i+1].IndexOf(':' > -1)
    {
        parsedTokens.Add(tokens[i]);
    }
    else
    {
        // This bit needs to be refined to deal with values with multiple spaces...
        parsedTokens.Add(tokens[i] + " " + tokens[i+1]);
    }
}

Другой подход - разделить двоеточие ... Таким образом, вашим первым элементом массива будет имя первого ключа, вторым элементом будет значение первого ключа, а затем имя второго ключа (может используйте LastIndexOf, чтобы разделить его) и так далее. Это, очевидно, будет очень запутанным, если значения могут включать двоеточия или ключи могут содержать пробелы, но в этом случае вам в значительной степени не повезет ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...