How to parse a string to find key-value pairs in i

2020-02-14 04:37发布

While searching mail in Google, we use the sytax like

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

or

mySearchString from:devcoder on:11-aug anotherSearchKeyword

After parsing, I should get the keyvalue pair such as (from, devcoder), (on, 11-aug). What is the best way to implement this parsing in c#.

4条回答
Animai°情兽
2楼-- · 2020-02-14 04:53

First Split() on space, then you have an array containing all search terms. Then you loop over them to find the ones that Contains() a colon (:) and Split() those again on the colon.

查看更多
够拽才男人
3楼-- · 2020-02-14 05:08

To Linq-ify Jason's answer:

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());
查看更多
做自己的国王
4楼-- · 2020-02-14 05:11

Split by space, then for each component of the split, split it by :. Then proceed accordingly. Roughly:

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

Output:

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

Output from your second string:

Found mySearchString
Found key-value pair key = from, value = devcoder
Found key-value pair key = on, value = 11-aug
Found anotherSearchKeyword
查看更多
可以哭但决不认输i
5楼-- · 2020-02-14 05:13

Here is one regular expression-based approach I have used in the past; it supports prefixes in combination with quoted strings.

A more correct/robust/performant approach would involve writing a simple parser, however in most usage scenarios the time and effort associated with implementing and testing the parser would be vastly disproportionate to the gains.

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

Output:

[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
查看更多
登录 后发表回答