Каков наилучший способ проанализировать эту строку в С#?
У меня есть строка, которую я читаю из другой системы. Это в основном длинная строка, представляющая список пар значений ключа, которые разделены пробелом между ними. Это выглядит так:
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 не будет пробелов, поэтому мне нужно только беспокоиться о значениях. Извинения за путаницу.
Ответы
Ответ 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));
}