当Key本身是分隔符时,如何从字符串中提取键值对?

时间:2014-06-10 08:14:07

标签: c# regex string key-value

假设松散的"格式",

中有一个字符串
string str = "V1,B=V1,C=V1,V2,V3,D=V1,V2,A=V1,=V2,V3";

和一组已知的密钥

List<string> lst = new List<string>() { "A", "B", "C", "D" };

如何提取下面显示的键值对? (第一个键之前的任何文本都应被视为空键的值。此外,下面显示的值还删除了任何尾随逗号。)

Key     Value
(null)  V1
A       V1,=V2,V3     (The = here is, unfortunately, part of the value)
B       V1
C       V1,V2,V3 
D       V1,V2

这个问题很难解决,因为 可能会立即在=,上拆分。

4 个答案:

答案 0 :(得分:4)

忽略已知的密钥集,并假设每个密钥只出现一次:

string str = "V1,B=V1,C=V1,V2,V3,D=V1,V2,A=V1,=V2,V3";

var splitByEqual = new[] {'='};

var values = Regex.Split(str, @",(?=\w+=)")
    .Select(token => token.Split(splitByEqual, 2))
    .ToDictionary(pair => pair.Length == 1 ? "" : pair.First(),
                  pair => pair.Last());
  • 正则表达式为pretty simple:用逗号分隔后跟一个键(任何字母数字)和一个等号。 (如果我们允许A=V1,V2=V3这不起作用)
  • 现在我们收集了集{V1B=V1C=V1,V2,V3D=V1,V2A=V1,=V2,V3}。我们将其分为=,但不超过一次。
  • 接下来我们创建一个字典。这条线有点难看,但不太重要 - 我们已经拥有了我们需要的数据。我也使用空字符串而不是null。

如果我们确实想要使用已知的密钥列表,我们可以将模式更改为:

var splitPattern = @",(?=(?:" + String.Join("|", keys.Select(Regex.Escape))) + ")=)";

并使用Regex.Split(str, splitPattern)

答案 1 :(得分:1)

假设键中没有出现键:

  • 对于每个键,
    • 搜索正则表达式",|^" + KEY + "="
  • 在找到的位置拆分字符串
  • 然后单独处理每个拆分字符串。第一个=之前的任何东西都是关键,之后的任何东西都是值

答案 2 :(得分:1)

在拆分之前,你不能删除前导=吗?这是使用String.Split和LINQ:

的方法
var pairs = str.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(x => new { KeyVals = x.TrimStart('=').Split('=') })
    .Select(x => new
    {
        Key = x.KeyVals.Length == 1 ? null : x.KeyVals[0].Trim(),
        Value = x.KeyVals.Last().Trim()
    })
    .GroupBy(x => x.Key)
    .Select(g => new { g.Key, Values=g.Select(x => x.Value) });

输出:

foreach (var keyVal in pairs)
    Console.WriteLine("Key:{0} Values:{1}", keyVal.Key, string.Join(",", keyVal.Values)); 

Key: Values:V1,V2,V3,V2,V2,V3
Key:B Values:V1
Key:C Values:V1
Key:D Values:V1
Key:A Values:V1

结果与您想要的结果不同,所以也许我走错了路。还不清楚为什么你需要“已知的一组键”。如果您想按照他们进行过滤,请在Where之前添加GroupBy

答案 3 :(得分:1)

我讨厌自己去所有的老同学,但是在分裂之前尝试用另一个角色替换领先的=然后把它放回去:

结果的调试视图:

enter image description here

    private static List<KeyValuePair<string, string>> ExtractData(string dataString, List<string> keys)
    {
        // Convert any leading "=" to another character avoid losing it :)
        dataString = dataString.Replace(",=", ",+");

        List<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();

        // Split on equals and comma
        var entries = dataString.Split(new char[] { '=', ',' }, StringSplitOptions.RemoveEmptyEntries);

        // Start with null key
        string key = null;

        // Start with blank value for each key
        string value = "";
        foreach (string entry in entries)
        {
            // Put back any removed '='
            string text = entry.Replace('+', '=');
            if (keys.Contains(entry))
            {
                // Save previous key value
                if (!string.IsNullOrEmpty(value))
                {
                    result.Add(new KeyValuePair<string, string>(key, value.TrimEnd(new char[] { ',' })));
                }
                key = entry;
                value = "";
            }
            else
            {
                value += text + ",";
            }
        }
        // save last result
        result.Add(new KeyValuePair<string,string>(key, value.TrimEnd(new char[]{','})));
        return result;
    }

我知道这可以通过LINQ等缩短,但没有时间使它漂亮:)