我从一个包含一些HTML的API中接收到一些文本,即<span>和<a>,我将使用这些部分来填充Xamarin.Forms Label的FormattedText属性。
下面的代码工作得很好,但是看起来效率很低,有一个外部正则表达式,每个循环多三个。
我想知道是否有更高级的正则表达式可以用来更好地分块,以获取我需要的class和href属性。
鉴于这一投入:
one<span class=\"a-class\">two</span>three<a href=\"#a-link\">four</a>five正确地产生:
one ->
two -> a-class
three ->
four -> #a-link
five ->代码:
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Program
{
public class StringPart
{
public string Text { get; set; }
public string Class { get; set; }
public string Link { get; set; }
public bool IsClass => !string.IsNullOrEmpty(Class);
public bool IsLink => !string.IsNullOrEmpty(Link);
public string Info
=> IsClass ? Class : IsLink ? Link : string.Empty;
}
public static void Main()
{
var text = "one<span class=\"a-class\">two</span>three<a href=\"#a-link\">four</a>five";
var parts = new List<StringPart>();
var idx = 0;
// Matches '<span class=\"a-class\">two</span>'
// & '<a href=\"#a-link\">four</a>'
foreach (Match match in new Regex($"<(.*?)>(.*?)</(.*?)>").Matches(text))
{
// preceeds match
parts.Add(new StringPart { Text = text.Substring(idx, match.Index - idx) });
// a match, has either span or a props
// 3 more regex, though
parts.Add(new StringPart
{
Text = Regex.Replace(match.Value, "<.*?>", string.Empty),
Link = Regex.Match(match.Value, "(?<=href=\\\")[\\S]+(?=\\\")").Value,
Class = Regex.Match(match.Value, "(?<=class=\\\")[\\S]+(?=\\\")").Value
});
// move idx for next preceeding part
idx = match.Index + match.Length;
}
// remaining after last match
parts.Add(new StringPart { Text = text.Substring(idx) });
// dump
foreach (var p in parts)
Console.WriteLine($"{p.Text} -> {p.Info}");
}
}发布于 2018-08-29 02:26:55
下面的代码将使用单个regex生成所需的输出,尽管regex有点复杂:
public static void Main()
{
var testString = "one<span class=\"a-class\">two</span>three<a href=\"#a-link\">four</a>five";
var matches = new Regex(@"^(?<Text>.+?)<|span class=""(?<Class>.*?)"">(?<Text>.+?)<\/span|a href=""(?<Link>.*?)"">(?<Text>.+?)<\/a|>(?<Text>.+?)<|>(?<Text>.+?)$").Matches(testString);
var parts = from m in matches.Cast<Match>()
select new StringPart
{
Text = m.Groups["Text"].Value,
Class = m.Groups["Class"].Value,
Link = m.Groups["Link"].Value
};
// dump
foreach (var p in parts)
Console.WriteLine($"{p.Text} -> {p.Info}");
}让我们把横梁拆了。下面是没有转义引号的完整正则表达式(当我从regex测试器复制到逐字C#字符串时,必须转义双引号):
^(?<Text>.*?)<|span class="(?<Class>.*?)">(?<Text>.*?)<\/span|a href="(?<Link>.*?)">(?<Text>.*?)<\/a|>(?<Text>.+?)<|>(?<Text>.+?)$表达式有五个部分,由|分隔。每个部分包含一个或多个命名组,它们捕获我们为该部分所关心的数据。
命名组具有以下格式:(?<Name>...)
以下是五个部分:
^(?<Text>.+?)<:匹配起始文本直到并包含第一个<span class="(?<Class>.*?)">(?<Text>.+?)<\/span:匹配<span>元素并捕获文本和类a href="(?<Link>.*?)">(?<Text>.+?)<\/a:匹配<a>元素并捕获文本和链接>(?<Text>.+?)<:匹配文本b/t两个HTML元素,包括>和<>(?<Text>.+?)$:匹配结束文本,包括最后一个>注1:如果字符串中没有HTML元素(例如,"one"),这将返回零匹配。最好单独处理那个特殊情况。
注2:这假设原来的字符串中没有\,而这些字符串放在那里只是为了转义示例C#代码中的双引号。如果字符串中包含\,则需要对正则表达式进行调整,以查找上面第2和第3部分中的\s。
(这里是我以前提出的测试器中的正则表达式:https://regex101.com/r/9C5dmy/2/ --除了在复制到C#代码时转义双引号之外,我还可以将所有"Text*“组名重命名为"Text”-- regex101.com不允许重复的组名,但是C#允许,并且所有命名为"Text“的"Text*”组都简化了逻辑。)
更新:将“文本”组从(?<Text>.*?)切换到(?<Text>.+?),以确保一个或多个字符以避免空字符串匹配。
https://stackoverflow.com/questions/52067960
复制相似问题