在 C# 中解析此字符串的最佳方法是什么

本文关键字:最佳 方法 是什么 字符串 | 更新日期: 2023-09-27 17:58:15

我有一个字符串,我正在从另一个系统读取。它基本上是一个长字符串,表示由中间空格分隔的键值对列表。它看起来像这样:

 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 中没有空格,所以我只需要担心值。对混乱表示歉意。

在 C# 中解析此字符串的最佳方法是什么

使用以下正则表达式:

'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();

正如 Wonko the Sane 指出的那样,此正则表达式将在具有 : 的值上失败。如果预测这种情况,请使用 'w+:['w: ]+?(?!['w+:]) 作为正则表达式。不过,当 value 中的冒号前面有空格时,这仍然会失败......我会考虑解决这个问题。

如果不将拆分从空格更改为其他内容(例如"|"(,则无法正常工作。

考虑一下:

阿尔弗雷德·贝斯特:阿尔弗雷德·贝斯特 阿尔弗雷德

阿尔弗雷德·贝斯特

    这是钥匙"阿尔弗雷德·贝斯特"和价值阿尔弗雷德"还是钥匙"阿尔弗雷德">
  • 和价值"贝斯特·阿尔弗雷德"?
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

您可以尝试对空格之间的内容进行 Url 编码(键和值而不是 : 符号(,但这需要您控制输入法。

或者你可以简单地使用另一种格式(如XML或JSON(,但同样你需要控制输入格式。

如果您无法控制输入格式,则始终可以使用正则表达式,并搜索单词加号:后面的单个空格。

更新(感谢乔恩格兰特(看起来您可以在键和值中包含空格。如果是这种情况,您将需要认真重新考虑您的策略,因为即使是正则表达式也无济于事。

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

使用正则表达式可以解决您的问题:

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

此代码将执行此操作(给定以下规则(。 它分析键和值,并以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;
        }

注意:此算法假定以下规则:

  1. 值中没有空格
  2. 键中没有冒号
  3. 值中没有冒号

没有任何正则表达式或字符串连接,并且作为可枚举对象(它假设键没有空格,但值可以(:

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

我想你可以采用你的方法并稍微扩展它来处理这些东西......

伪代码种类:

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 将其拆分出来(,依此类推。如果值可以包含冒号,或者键可以包含空格,这显然会变得非常混乱,但在这种情况下,您将几乎不走运......