[英]Fastest way to extract a substring in C#
我将处理数千个字符串(平均大小约为 150kB)。 它们中的每一个都包含零个或多个以下形式的子字符串:
<a href="/link/i/want">Fixed_String</a>
我想提取所有此类链接并将它们放入列表中。
此外,还有另一个固定字符串,之后我要查找的字符串将不会出现。
获取琴弦的最快方法是什么?
假设字符串是正确格式的 HTML 格式,您可以使用XmlReader class 轻松解析它们,它是非缓存和仅转发(这使得它非常非常快)。 您只需在检索其“href”属性值时寻找合适的节点。
您也可以使用像.SubString()
这样的普通字符串操作,但是您必须编写许多子例程来处理异常情况。 这里的重点是避免使用 RegEx,因为它将是最慢的。
SubString() 选项
正如 Teoman Soygul 所指出的,有一个 SubString() 选项,我不知道它是慢还是快,因为我没有并排测试它们。
现在,这没有被正确地分解为子方法,但应该给你一个大致的想法。
我只是使用ReadOnlyCollection
因为它是我在不需要进一步操作列表时所习惯的。 将其更改为您喜欢的任何 output 列表类型。
someText
变量很可能最终会成为GetLinks
的参数。
public ReadOnlyCollection<string> GetLinks()
{
string startingText = "href=''";
string endText = "''>";
string stopText = "Fixed_String";
string someText = @"what is this text <a href=''/link/i/want''>somenormallink</a> some random text <a href=''/another link/i/want''>Fixed_String</a> some more radnom txt ";
List<string> myLinks = new List<string>();
string[] rawLinks = someText.Split(new string[] { "<a " }, StringSplitOptions.None);
foreach (string rawLink in rawLinks)
{
if (!rawLink.StartsWith(startingText))
{
continue;
}
myLinks.Add(rawLink.Substring(startingText.Length, rawLink.IndexOf(endText, 1) - startingText.Length));
if (rawLink.Contains(stopText))
{
break;
}
}
return new ReadOnlyCollection<string>(myLinks);
}
这会产生一个包含链接的集合:
一些手动解析可能是解决这个问题的最快方法。 正则表达式也是可能的,因为它实际上只是解析链接的一个非常简单的案例,而不是整个 HTML 文档,但这很容易在性能方面扼杀那些大文件。
现在,让我先说明一下,我根本没有测试过它,而且我觉得发布它有点脏(我确信它需要更多的边缘情况检查以避免错误),但是这里是 go:
const char[] quotes = new char[] { '"', '\'' };
private List<string> ExtractLinks(string html)
{
var links = new List<string>();
string searchFor = ">Fixed_String</a>";
for (int i = html.IndexOf(searchFor); i >= 0; i = html.IndexOf(searchFor, i + searchFor.Length))
{
string href = ExtractHref(html, i);
if (!String.IsNullOrEmpty(href))
links.Add(href);
}
return links;
}
private string ExtractHref(string html, int backtrackFrom)
{
int hrefStart = -1;
// Find "<a", but limit search so we don't backtrack forever
for (int i = backtrackFrom; i > backtrackFrom - 255; i--)
{
if (i < 0)
return null;
if (html[i] == '<' && html[i + 1] == 'a')
{
hrefStart = html.IndexOf("href=", i);
break;
}
}
if (hrefStart < 0)
return null;
int start = html.IndexOfAny(quotes, hrefStart);
if (start < 0)
return null;
int end = html.IndexOfAny(quotes, start + 1);
if (end < 0)
return null;
return html.Substring(start + 1, end - start - 1);
}
XmlReader
可能不可行,因为您很可能无法保证这些文件是 XHTML 格式的。 如果您想进行正确的解析, HTML 敏捷包可能是您的最佳选择,或者如果无法帮助,则可能是正确完成的正则表达式。 我发布了此手动解析,因此您可以使用另一种替代方法进行性能测试。
通常,Regex 处理小文件时速度更快。 如果文件大小变大(根据我的经验>~60Kb),那么正则表达式会变慢(甚至 static,编译等)。 找到用非常好的英语描述的确切情况:
以高效的方式和总线因素剥离空 XmlElements
发现什么是“高总线因素”,玩得开心。 它给我带来了一天的好心情。
我认为在这种情况下,如果字符串平均足够大并且包含零个或多个子字符串,最好的方法是像这样使用Regex class :
string anchorPattern = @"<(.|/)a(.|\n)+?>";
foreach (string str in strings)
{
Regex regex = new Regex(anchorPattern);
foreach (Match match in regex.Matches(str))
{
// do here what you want with substring in match.Value
}
}
从基准测试来看,生成 substring 的最佳方法是使用 ReadOnlySpans,而不是使用 string.Split
string.Split 要慢得多,并且会向 memory 写入很多内容,而 Readonly 仅跨越写入堆。
| Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated |
|------------------------ |----------:|---------:|----------:|----------:|-------:|------:|------:|----------:|
| SpanParseLongVersion | 17.84 ns | 0.385 ns | 0.674 ns | - | - | - | - | - |
| ParseLongFWVersionSplit | 95.74 ns | 1.928 ns | 3.274 ns | 95.05 ns | 0.0373 | - | - | 176 B |
public const string FWLongVersion= "FWabcdefghijklmnopqrstuvwxyz-1.0000000000001";
[Benchmark]
public void SpanParseLongVersion()
{
var dashChar = '-';
var vSpan = FWLongVersion.AsSpan();
var length = FWLongVersion.Length;
ReadOnlySpan<char> fwVersion = null;
for (int x = 0; x < length; x++)
{
if (vSpan[x] == dashChar)
{
fwVersion = vSpan.Slice(x + 1, 5);
break;
}
}
}
[Benchmark]
public void ParseLongFWVersionSplit()
{
var x = FWLongVersion.Split('-');
}
另外,使用string.StartsWith
和string.Contains
是很好......关于如何有效地检查这个,请在这里查看我的帖子: https://stackoverflow.com/a/64395744/4926590
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.