Подтвердить что ты не робот

Каков наилучший способ проанализировать эту строку в С#?

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

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

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

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

4b9b3361

Ответ 1

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

\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 будет предшествовать пробелу, хотя... Я подумаю о решении этого вопроса.

Ответ 2

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

Рассмотрим это:

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

  • Является ли этот ключ "Альфред Бестер" и значение Альфреда "или ключом" Альфред "и значение" Бестер Альфред "?

Ответ 3

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

Ответ 4

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

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

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

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

Ответ 5

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().

Ответ 6

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

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
    }
}

Ответ 7

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

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

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, чтобы разделить его) и т.д. Это, очевидно, будет очень грязным, если значения могут включать двоеточия, или ключи могут содержать пробелы, но в этом случае вам будет очень не повезло...

Ответ 8

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

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;

        }

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

  • Нет пробелов в значениях
  • Никаких двоеточий в клавишах
  • Никаких двоеточий в значениях

Ответ 9

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

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