在C#.Net中提取和操作字符串

时间:2017-07-13 15:59:17

标签: c# .net

我们需要在C#中提取和操作字符串。净。要求是 - 我们有一个字符串

  

($ name $ :('George')AND $ phonenumer $ :('456456')AND   $ $ EMAILADDRESS :( “test@test.com”))

我们需要在字符 - $

之间提取字符串

因此,最后,我们需要获取一个包含 - name,phonenumber,emailaddress的字符串列表。

理想的做法是什么?有没有现成的开箱即用功能?

此致

约翰

2 个答案:

答案 0 :(得分:1)

最简单的方法是使用正则表达式匹配$之间的所有非空格字符:

var regex=new Regex(@"\$\w+\$");
var input = "($name$:('George') AND $phonenumer$:('456456') AND $emailaddress$:(\"test@test.com\"))";

var matches=regex.Matches(input);

这将返回一系列匹配项。每个匹配的.Value属性包含匹配的字符串。使用\$是因为$在正则表达式中具有特殊含义 - 它匹配字符串的结尾。 \w表示非空白字符。 +表示一个或多个。

由于这是一个集合,您可以在其上使用LINQ来获取具有值的数组:

var values=matches.OfType<Match>().Select(m=>m.Value).ToArray();

该数组将包含值$name$$phonenumer$$emailaddress$

按名称捕获

您可以在模式中指定组并为其附加名称。例如,您可以对字段名称值进行分组:

var regex=new Regex(@"\$(?<name>\w+)\$");
var names=regex.Matches(input)
                .OfType<Match>()
                .Select(m=>m.Groups["name"].Value);

这将返回name,phonenumer,emailaddress。括号用于分组。 (?<somename>pattern)用于将名称附加到群组

提取名称和值

您还可以捕获字段值并将其作为单独的字段提取。获得字段名称和值后,您可以返回它们,例如作为对象或匿名类型。

这种情况下的模式更加复杂:

@"\$(?<name>\w+)\$:\(['""](?<value>.+?)['""]\)"

括号被转义,因为我们希望它们与值匹配。 '"个字符都在值中使用,因此['"]用于指定字符的选择。模式是一个文字字符串(即以@开头),因此双引号必须被转义:['""]。任何字符都必须匹配.+,但只能匹配模式.+?中的下一个字符。没有?模式.+将匹配字符串末尾的所有内容。

把它们放在一起:

var regex =  new Regex(@"\$(?<name>\w+)\$:\(['""](?<value>.+?)['""]\)");
var myValues = regex.Matches(input)
          .OfType<Match>()
          .Select(m=>new {  Name=m.Groups["name"].Value, 
                            Value=m.Groups["value"].Value
            })
          .ToArray()

将它们变成字典

您可以使用ToArray()将对象转换为字典,而不是ToDictionary(),而不是.ToDictionary(it=>it.Name,it=>it.Value)。您可以省略选择步骤并从匹配项中生成字典:

var myDict = regex.Matches(input)
          .OfType<Match>()
          .ToDictionary(m=>m.Groups["name"].Value, 
                        m=>m.Groups["value"].Value);

正则表达式通常很快,因为它们不会拆分字符串。模式转换为有效的代码,解析输入并立即跳过非匹配的输入。每个匹配和组仅包含索引到输入字符串中的起始和结束字符。只有在调用.Value时才会生成字符串。

正则表达式是线程安全的,这意味着单个Regex对象可以存储在静态字段中并从多个线程重用。这有助于Web应用程序,因为不需要为每个请求创建一个新的Regex对象

由于这两个优点,正则表达式被广泛用于解析日志文件和提取特定字段。与分割相比,性能可以提高10倍或更多,而内存使用率仍然很低。分割很容易导致内存使用量比原始输入文件大多倍倍。

可以更快吗?

是。正则表达式产生的解析代码可能不尽如人意。手写解析器可能更快。在这种特殊情况下,我们希望在第一个$之前检测到$时开始捕获文本。这可以通过以下方法完成:

IEnumerable<string> GetNames(string input)
{
    var builder=new StringBuilder(20);
    bool started=false;
    foreach(var c in input)
    {        
        if (started)
        {
            if (c!='$')
            {
                builder.Append(c);
            }
            else
            {
                started=false;
                var value=builder.ToString();
                yield return value;
                builder.Clear();
            }
        }
        else if (c=='$')
        {
            started=true;
        }        
    }
}

字符串是IEnumerable<char>,因此我们可以一次检查一个字符,而无需复制它们。通过使用具有预定容量的单个StringBuilder,我们避免重新分配,至少在我们找到大于20个字符的密钥之前。

修改此代码以提取值并不是那么容易。

答案 1 :(得分:0)

这是一种方法,但肯定不是很优雅。基本上将字符串拆分为'$'并取其他所有项目将会得到结果(在对一些不需要的字符进行额外修剪之后)。

在这个例子中,我也抓住了每个项目的值,然后将它们放在一个字典中:

var input = "($name$:('George') AND $phonenumer$:('456456') AND $emailaddress$:(\"test@test.com\"))";
var inputParts = input.Replace(" AND ", "")
    .Trim(')', '(')
    .Split(new[] {'$'}, StringSplitOptions.RemoveEmptyEntries);

var keyValuePairs = new Dictionary<string, string>();

for (int i = 0; i < inputParts.Length - 1; i += 2)
{
    var key = inputParts[i];
    var value = inputParts[i + 1].Trim('(', ':', ')', '"', '\'', ' ');

    keyValuePairs[key] = value;
}

foreach (var kvp in keyValuePairs)
{
    Console.WriteLine($"{kvp.Key} = {kvp.Value}");
}

// Wait for input before closing
Console.WriteLine("\nDone!\nPress any key to exit...");
Console.ReadKey();

<强>输出

enter image description here