如何解析字符串以查找其中的键值对

本文关键字:键值对 查找 何解析 字符串 | 更新日期: 2023-09-27 18:07:36

在Google中搜索邮件时,我们使用如下语法

from:devcoder hasattachments:true mySearchString on:11-aug

mySearchString from:devcoder on:11-aug anotherSearchKeyword

解析后,我应该得到键值对,如(from, devcoder), (on, 11-aug)。在c#中实现这种解析的最佳方法是什么?

如何解析字符串以查找其中的键值对

Linq-ify Jason的回答:

string s = "from:devcoder hasattachments:true mySearchString on:11-aug";
var keyValuePairs = s.Split(' ')
    .Select(x => x.Split(':'))
    .Where(x => x.Length == 2)
    .ToDictionary(x => x.First(), x => x.Last());

按空格分割,然后对分割的每个组件按:分割。然后进行相应的操作。约:

string s = "from:devcoder hasattachments:true mySearchString on:11-aug";
var components = s.Split(' ');
var blocks = components.Select(component => component.Split(':'));
foreach(var block in blocks) {
    if(block.Length == 1) {
        Console.WriteLine("Found {0}", block[0]);
    }
    else {
        Console.WriteLine(
            "Found key-value pair key = {0}, value = {1}",
            block[0],
            block[1]
        );
    }
}
输出:

Found key-value pair key = from, value = devcoder
Found key-value pair key = hasattachments, value = true
Found mySearchString
Found key-value pair key = on, value = 11-aug

第二个字符串的输出:

Found mySearchString
Found key-value pair key = from, value = devcoder
Found key-value pair key = on, value = 11-aug
Found anotherSearchKeyword

这是我过去使用过的一种基于正则表达式的方法;它支持前缀与带引号的字符串的组合。

更正确/健壮/高性能的方法应该包括编写一个简单的解析器,但是在大多数使用场景中,与实现和测试解析器相关的时间和精力与所获得的收益是非常不成比例的。

private static readonly Regex searchTermRegex = new Regex(
        @"^(
            's*
            (?<term>
                ((?<prefix>[a-zA-Z][a-zA-Z0-9-_]*):)?
                (?<termString>
                    (?<quotedTerm>
                        (?<quote>['""])
                        (('''k<quote>)|((?!'k<quote>).))*
                        'k<quote>?
                    )
                    |(?<simpleTerm>[^'s]+)
                )
            )
            's*
        )*$",
        RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture
    );

private static void FindTerms(string s) {
    Console.WriteLine("[" + s + "]");
    Match match = searchTermRegex.Match(s);
    foreach(Capture term in match.Groups["term"].Captures) {
        Console.WriteLine("term: " + term.Value);
        Capture prefix = null;
        foreach(Capture prefixMatch in match.Groups["prefix"].Captures)
            if(prefixMatch.Index >= term.Index && prefixMatch.Index <= term.Index + term.Length) {
                prefix = prefixMatch;
                break;
            }
        if(null != prefix)
            Console.WriteLine("prefix: " + prefix.Value);
        Capture termString = null;
        foreach(Capture termStringMatch in match.Groups["termString"].Captures)
            if(termStringMatch.Index >= term.Index && termStringMatch.Index <= term.Index + term.Length) {
                termString = termStringMatch;
                break;
            }
        Console.WriteLine("termString: " + termString.Value);
    }
    Console.WriteLine();
}
public static void Main (string[] args)
{           
    FindTerms(@"two terms");
    FindTerms(@"prefix:value");
    FindTerms(@"some:""quoted term""");
    FindTerms(@"firstname:Jack ""the Ripper""");
    FindTerms(@"'quoted term''s escaped quotes'");
    FindTerms(@"""unterminated quoted string");
}
输出:

[two terms]
term: two
termString: two
term: terms
termString: terms
[prefix:value]
term: prefix:value
prefix: prefix
termString: value
[some:"quoted term"]
term: some:"quoted term"
prefix: some
termString: "quoted term"
[firstname:Jack "the Ripper"]
term: firstname:Jack
prefix: firstname
termString: Jack
term: "the Ripper"
termString: "the Ripper"
['quoted term''s escaped quotes']
term: 'quoted term''s escaped quotes'
termString: 'quoted term''s escaped quotes'
["unterminated quoted string]
term: "unterminated quoted string
termString: "unterminated quoted string

首先是空格上的Split(),然后是包含所有搜索项的数组。然后循环遍历它们以找到Contains() a冒号(:)和Split()再次在冒号上的那些。