C# 是否有人在StringBuilder或Streams周围实现了正则表达式和/或Xml解析器?
我正在构建一个压力测试客户端,该客户端使用尽可能多的线程对服务器进行测试并分析响应。我经常发现自己被垃圾收集(和/或缺乏垃圾收集)所限制,在大多数情况下,我实例化的字符串只是为了将它们传递给正则表达式或Xml解析例程 如果您反编译Regex类,您将看到它在内部使用StringBuilder来完成几乎所有的事情,但是您不能将字符串生成器传递给它;在开始使用私有方法之前,它会很有帮助地深入到私有方法中,所以扩展方法也不会解决这个问题。如果您想从System.Xml.Linq中的解析器中获取对象图,则会遇到类似的情况 这不是一个学究式的提前过度优化的例子。我已经研究了这个问题和其他问题。我还分析了我的应用程序,看看上限是从哪里来的,现在使用C# 是否有人在StringBuilder或Streams周围实现了正则表达式和/或Xml解析器?,c#,regex,stringbuilder,C#,Regex,Stringbuilder,我正在构建一个压力测试客户端,该客户端使用尽可能多的线程对服务器进行测试并分析响应。我经常发现自己被垃圾收集(和/或缺乏垃圾收集)所限制,在大多数情况下,我实例化的字符串只是为了将它们传递给正则表达式或Xml解析例程 如果您反编译Regex类,您将看到它在内部使用StringBuilder来完成几乎所有的事情,但是您不能将字符串生成器传递给它;在开始使用私有方法之前,它会很有帮助地深入到私有方法中,所以扩展方法也不会解决这个问题。如果您想从System.Xml.Linq中的解析器中获取对象图,则
Regex.Replace()
确实在一个方法链中引入了大量开销,在这个方法链中,我试图以每小时数百万次的请求访问服务器,并检查XML响应中的错误和嵌入的诊断代码。我已经摆脱了几乎所有其他阻碍吞吐量的低效问题,我甚至通过扩展StringBuilder在不需要捕获组或反向引用的情况下执行通配符查找/替换,减少了大量的正则表达式开销,但在我看来,有人会打包一个自定义StringBuilder到目前为止,基于流的正则表达式和Xml解析实用程序(或者更好一些)
好吧,那就结束吧,但我要自己做吗
更新:我找到了一种解决办法,可以将峰值内存消耗从几GB降低到几百兆,因此我将其发布在下面。我不会添加它作为答案,因为a)我通常不喜欢这样做,b)我仍然想知道是否有人花时间自定义StringBuilder来执行正则表达式(反之亦然)在我之前
在我的例子中,我不能使用XmlReader,因为我摄取的流在某些元素中包含一些无效的二进制内容。为了解析XML,我必须清空这些元素。我以前使用一个静态编译的正则表达式实例来进行替换,这消耗了大量内存(我正在尝试每秒处理300个10KB的文档)。大幅降低消费的变化是:
IndexOf
方法添加了代码李>
WildcardReplace
方法,允许每次调用一个通配符(*或?)WildcardReplace()
调用替换了正则表达式的用法,以清空有问题元素的内容/// <summary>
/// Performs basic wildcard find and replace on a string builder, observing one of two
/// wildcard characters: * matches any number of characters, or ? matches a single character.
/// Operates on only one wildcard per invocation; 2 or more wildcards in <paramref name="find"/>
/// will cause an exception.
/// All characters in <paramref name="replaceWith"/> are treated as literal parts of
/// the replacement text.
/// </summary>
/// <param name="find"></param>
/// <param name="replaceWith"></param>
/// <returns></returns>
public static StringBuilder WildcardReplace(this StringBuilder sb, string find, string replaceWith) {
if (find.Split(new char[] { '*' }).Length > 2 || find.Split(new char[] { '?' }).Length > 2 || (find.Contains("*") && find.Contains("?"))) {
throw new ArgumentException("Only one wildcard is supported, but more than one was supplied.", "find");
}
// are we matching one character, or any number?
bool matchOneCharacter = find.Contains("?");
string[] parts = matchOneCharacter ?
find.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries)
: find.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
int startItemIdx;
int endItemIdx;
int newStartIdx = 0;
int length;
while ((startItemIdx = sb.IndexOf(parts[0], newStartIdx)) > 0
&& (endItemIdx = sb.IndexOf(parts[1], startItemIdx + parts[0].Length)) > 0) {
length = (endItemIdx + parts[1].Length) - startItemIdx;
newStartIdx = startItemIdx + replaceWith.Length;
// With "?" wildcard, find parameter length should equal the length of its match:
if (matchOneCharacter && length > find.Length)
break;
sb.Remove(startItemIdx, length);
sb.Insert(startItemIdx, replaceWith);
}
return sb;
}
//
///在字符串生成器上执行基本的通配符查找和替换,观察以下两种情况之一
///通配符:*匹配任意数量的字符,或者?匹配单个字符。
///每次调用仅对一个通配符进行操作;中有2个或多个通配符
///将导致异常。
///中的所有字符都被视为
///替换文本。
///
///
///
///
公共静态StringBuilder WildcardReplace(此StringBuilder sb、字符串查找、字符串替换为){
if(find.Split(新字符[]{'*}).Length>2 | | find.Split(新字符[]{'?'}).Length>2 | |(find.Contains(“*”)&&find.Contains(“?”){
抛出新ArgumentException(“仅支持一个通配符,但提供了多个通配符。”,“查找”);
}
//我们是匹配一个字符还是任意数字?
bool matchenecharacter=find.Contains(“?”);
字符串[]部分=匹配字符?
find.Split(新字符[]{'?'},StringSplitOptions.RemoveEmptyEntries)
:find.Split(新字符[]{'*},StringSplitOptions.RemoveEmptyEntries);
int startItemIdx;
int-endItemIdx;
int newStartIdx=0;
整数长度;
而((startItemIdx=sb.IndexOf(parts[0],newStartIdx))>0
&&(endItemIdx=sb.IndexOf(parts[1],startItemIdx+parts[0].长度))>0){
长度=(endItemIdx+部分[1]。长度)-startItemIdx;
newStartIdx=startItemIdx+replaceWith.Length;
//使用“?”通配符,查找参数长度应等于其匹配的长度:
if(matchenecharacter&&length>find.length)
打破
sb.移除(startItemIdx,长度);
sb.插入(startItemIdx,替换为);
}
归还某人;
}
XmlReader是一种基于流的XML解析器。请参见Mono项目已完成。如果您需要创建一个为特定应用程序的性能定制的正则表达式库,您应该能够从的实现中的最新代码开始。在这里尝试一下。一切都是基于字符的,效率相对较低。您可以使用任意数量的*
s或?
s。但是,您的*
现在是✪你的?
现在是★代码>。大约三天的工作使它尽可能干净。您甚至可以在一次扫描中输入多个查询
用法示例:通配符(新的StringBuilder(“Hello and welcome”),“Hello✪W★l“,”be“
导致“变成”
////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////使用“查找”参数在“文本”中搜索字符串,并使用“替换”参数替换为字符串
// ✪ 表示多个通配符(非贪婪)
// ★ 表示单个通配符
公共StringBuilder通配符(StringBuilder文本、字符串查找、字符串替换、布尔区分大小写=false)
{
返回通配符(文本,新字符串[]{find},新字符串[])
////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////// Search for a string/s inside 'text' using the 'find' parameter, and replace with a string/s using the replace parameter
// ✪ represents multiple wildcard characters (non-greedy)
// ★ represents a single wildcard character
public StringBuilder wildcard(StringBuilder text, string find, string replace, bool caseSensitive = false)
{
return wildcard(text, new string[] { find }, new string[] { replace }, caseSensitive);
}
public StringBuilder wildcard(StringBuilder text, string[] find, string[] replace, bool caseSensitive = false)
{
if (text.Length == 0) return text; // Degenerate case
StringBuilder sb = new StringBuilder(); // The new adjusted string with replacements
for (int i = 0; i < text.Length; i++) { // Go through every letter of the original large text
bool foundMatch = false; // Assume match hasn't been found to begin with
for(int q=0; q< find.Length; q++) { // Go through each query in turn
if (find[q].Length == 0) continue; // Ignore empty queries
int f = 0; int g = 0; // Query cursor and text cursor
bool multiWild = false; // multiWild is ✪ symbol which represents many wildcard characters
int multiWildPosition = 0;
while(true) { // Loop through query characters
if (f >= find[q].Length || (i + g) >= text.Length) break; // Bounds checking
char cf = find[q][f]; // Character in the query (f is the offset)
char cg = text[i + g]; // Character in the text (g is the offset)
if (!caseSensitive) cg = char.ToLowerInvariant(cg);
if (cf != '★' && cf != '✪' && cg != cf && !multiWild) break; // Break search, and thus no match is found
if (cf == '✪') { multiWild = true; multiWildPosition = f; f++; continue; } // Multi-char wildcard activated. Move query cursor, and reloop
if (multiWild && cg != cf && cf != '★') { f = multiWildPosition + 1; g++; continue; } // Match since MultiWild has failed, so return query cursor to MultiWild position
f++; g++; // Reaching here means that a single character was matched, so move both query and text cursor along one
}
if (f == find[q].Length) { // If true, query cursor has reached the end of the query, so a match has been found!!!
sb.Append(replace[q]); // Append replacement
foundMatch = true;
if (find[q][f - 1] == '✪') { i = text.Length; break; } // If the MultiWild is the last char in the query, then the rest of the string is a match, and so close off
i += g - 1; // Move text cursor along by the amount equivalent to its found match
}
}
if (!foundMatch) sb.Append(text[i]); // If a match wasn't found at that point in the text, then just append the original character
}
return sb;
}