我们需要在C#中提取和操作字符串。净。要求是 - 我们有一个字符串在C#中提取和操纵字符串.net
($名称$ :('乔治')和$ phonenumer $ :('456456')和 $ EMAILADDRESS $ :(“[email protected]”))
我们需要提取的字符之间的字符串 - $
因此,在最后,我们需要得到一个包含字符串列表 - 名,PHONENUMBER,EMAILADDRESS。
这样做的理想方法是什么?是否有任何可用于此的开箱即用功能?
问候,
约翰
我们需要在C#中提取和操作字符串。净。要求是 - 我们有一个字符串在C#中提取和操纵字符串.net
($名称$ :('乔治')和$ phonenumer $ :('456456')和 $ EMAILADDRESS $ :(“[email protected]”))
我们需要提取的字符之间的字符串 - $
因此,在最后,我们需要得到一个包含字符串列表 - 名,PHONENUMBER,EMAILADDRESS。
这样做的理想方法是什么?是否有任何可用于此的开箱即用功能?
问候,
约翰
最简单的方法是使用正则表达式来匹配$
之间的所有非空白字符:
var regex=new Regex(@"\$\w+\$");
var input = "($name$:('George') AND $phonenumer$:('456456') AND $emailaddress$:(\"[email protected]\"))";
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(it=>it.Name,it=>it.Value)
转换成字典,ToDictionary()
,例如。你可以省略选择步骤并且从比赛本身词典:
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个字符的密钥之前。
修改此代码以提取值虽然不是那么容易。
下面是做这件事,但肯定不是很优雅。基本上在'$'上分割字符串,并采取每一个其他项目会给你的结果(经过一些额外的修剪不需要的字符)。无论是在一本字典
在这个例子中,我也抓住每一个项目的值,然后把:
var input = "($name$:('George') AND $phonenumer$:('456456') AND $emailaddress$:(\"[email protected]\"))";
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();
输出
这不是提取,这是*解析*。它很简单,但它可以用正则表达式来执行,例如'@“\ $ \ w + \ $”' –
将字符串拆分为'$'并在结果可枚举中将每个奇数出现(即第1,第五等):) – DavidG
@DavidG比正则表达式更慢更复杂。它也会产生很多临时字符串 –