我正在编写一个应用程序,该应用程序旨在处理数以千计的文章/条目,其中包含以下格式的大量spintax:
{Hello|Hi} {World|There!}, how are you?但是,当我使用profiler运行应用程序时,我注意到处理Regex的部分会占用大量资源,我的应用程序最终会因为内存不足问题而崩溃。有没有人能建议一种方法来改进我的代码,或者更好地解析spintax?
public static String Spin(String text)
{
Regex reg = new Regex(@"\{[^\{\}]*\}");
Random rand = new Random((int)DateTime.Now.Ticks);
while (true)
{
Match m = reg.Match(text);
if (!m.Success) break;
String[] parts = m.Value.TrimStart('{').TrimEnd('}').Split('|');
int i = rand.Next(parts.Length);
text = text.Substring(0, m.Index) + parts[i] + text.Substring(m.Index + m.Length);
}
return text;
}发布于 2012-12-27 07:31:15
我已经实现了我的快速版本(没有Regex、没有Split、没有Substring、没有Replace和其他字符串操作方法)。为了复制字符串,我使用String.CopyTo,它将符号复制到一个普通的char数组中。
这段代码完全支持嵌套的Spintaxes (可能没有深度限制)。一个限制是每个Spintax的最大选项数,目前是100,但可以更改为1000或更多...另一个限制是输入字符串的最大长度,现在是100000,但也可以增加。
关于性能-我的测试表明,这个代码比任何优化的Regex解决方案(包括Jim Mischel的解决方案)快15倍以上,比使用Substring和其他字符串操作方法的版本快约5倍。我用VS2012中的优化代码设置在发布模式下测试了这一点。
static int[] partIndices = new int[100];
static int[] depth = new int[100];
static char[] symbolsOfTextProcessed = new char[100000];
public static String SpinEvenMoreFaster(String text)
{
int cur = SpinEvenMoreFasterInner(text, 0, text.Length, 0);
return new String(symbolsOfTextProcessed, 0, cur);
}
public static int SpinEvenMoreFasterInner(String text, int start, int end, int symbolIndex)
{
int last = start;
for (int i = start; i < end; i++)
{
if (text[i] == '{')
{
int k = 1;
int j = i + 1;
int index = 0;
partIndices[0] = i;
depth[0] = 1;
for (; j < end && k > 0; j++)
{
if (text[j] == '{')
k++;
else if (text[j] == '}')
k--;
else if (text[j] == '|')
{
if (k == 1)
{
partIndices[++index] = j;
depth[index] = 1;
}
else
depth[index] = k;
}
}
if (k == 0)
{
partIndices[++index] = j - 1;
int part = rand.Next(index);
text.CopyTo(last, symbolsOfTextProcessed, symbolIndex, i - last);
symbolIndex += i - last;
if (depth[part] == 1)
{
text.CopyTo(partIndices[part] + 1, symbolsOfTextProcessed, symbolIndex, partIndices[part + 1] - partIndices[part] - 1);
symbolIndex += partIndices[part + 1] - partIndices[part] - 1;
}
else
{
symbolIndex = SpinEvenMoreFasterInner(text, partIndices[part] + 1, partIndices[part + 1], symbolIndex);
}
i = j - 1;
last = j;
}
}
}
text.CopyTo(last, symbolsOfTextProcessed, symbolIndex, end - last);
return symbolIndex + end - last;
}发布于 2012-12-26 23:15:14
下面是一个非正则表达式的替代方法。
RegexOptions.Compiled,使用Substring而不是TrimLeft和TrimRight。针对具有嵌套实例的clarity.SpinNoRE来处理任意嵌套的spintaxes,优化了代码,并将comments.Spin和SpinFaster分别添加到SpinRE和SpinNoRE中。OP的代码在处理嵌套的spintaxes时要慢得多(可以理解,因为每一层嵌套都会强制额外的正则表达式匹配)。新的ideone demo可用;代码如下(可在演示中获得注释;请参阅链接):
public static String SpinNoRE(String text)
{
int i, j, e = -1;
char[] curls = new char[] {'{', '}'};
text += '~';
do
{
i = e;
e = -1;
while ((i = text.IndexOf('{', i+1)) != -1)
{
j = i;
while ((j = text.IndexOfAny(curls, j+1)) != -1 && text[j] != '}')
{
if (e == -1) e = i;
i = j;
}
if (j != -1)
{
parts = text.Substring(i+1, (j-1)-(i+1-1)).Split('|');
text = text.Remove(i, j-(i-1)).Insert(i, parts[rand.Next(parts.Length)]);
}
}
}
while (e-- != -1);
return text.Remove(text.Length-1);
}结果:
Input Text: Oh! {{I'm|You're} here!|How are you{ doing{|, {buddy|pal|guy}}|}?}
Testing SpinRE: Oh! You're here!
Testing SpinRE: Oh! How are you doing?
Testing SpinRE: Oh! How are you?
Testing SpinRE: Oh! How are you doing, buddy?
Testing SpinRE: Oh! I'm here!
Testing SpinRE: Oh! How are you doing, guy?
Testing SpinRE: Oh! How are you doing?
Testing SpinRE: Oh! I'm here!
Testing SpinRE: Oh! I'm here!
Testing SpinRE: Oh! How are you doing?
Testing SpinNoRE: Oh! How are you doing, buddy?
Testing SpinNoRE: Oh! You're here!
Testing SpinNoRE: Oh! How are you?
Testing SpinNoRE: Oh! How are you?
Testing SpinNoRE: Oh! You're here!
Testing SpinNoRE: Oh! I'm here!
Testing SpinNoRE: Oh! How are you doing?
Testing SpinNoRE: Oh! How are you?
Testing SpinNoRE: Oh! How are you doing, buddy?
Testing SpinNoRE: Oh! I'm here!
Time elapsed over 100,000 runs of each in alternation:
SpinRE: 03.686s
SpinNoRE: 00.921s请原谅并指出任何错误。)
发布于 2012-12-27 03:51:42
我建议对你的代码做一些修改。首先,将正则表达式定义移出方法,并使用RegexOptions.Compiled选项来减少每次调用的设置时间。此外,将随机数生成器的创建从频繁使用的方法中移出。
此外,通过告诉正则表达式从哪里开始匹配,可以消除许多不必要的字符串搜索。如果您最终执行了多次循环迭代,这一点很重要。这个想法是,如果您已经完成了字符串中位置M的替换,则没有理由检查这些匹配,因为将不会有任何匹配。
可以通过将表达式替换为以下内容来消除对TrimStart和TrimEnd的调用:
String[] parts = m.Value.Substring(1, m.Value.Length-2).Split('|');您已经知道,字符串以{开头,以}结尾,中间没有这两个字符中的任何一个,所以您所要做的就是去掉第一个和最后一个字符。没有理由产生由TrimStart和TrimEnd创建的临时字符串的成本。
另一种可能是将捕获组添加到正则表达式(将要捕获的部分放在圆括号中),并对捕获的文本而不是整个匹配的表达式进行操作。
将所有这些建议放在一起会导致:
static Regex reg = new Regex(@"\{([^\{\}]*)\}", RegexOptions.Compiled);
static Random rand = new Random();
public static String Spin(String text)
{
int matchPos = 0;
while (true)
{
Match m = reg.Match(text, matchPos);
if (!m.Success) break;
String[] parts = m.Groups[1].Value.Split('|');
int i = rand.Next(parts.Length);
text = text.Substring(0, m.Index) + parts[i] + text.Substring(m.Index + m.Length);
matchPos = m.Index;
}
return text;
}也就是说,这将不支持嵌套,并且创建支持嵌套的正则表达式解决方案可能会有些困难。就速度而言,它也不是最优的,因为它花费了大量时间来构建和重新构建text字符串。稍微考虑一下,您就可以对它进行更多的优化,但它永远不会像SergyS supplied这样的优化自定义解析器解决方案那样快。
如果速度是最重要的,那么您将需要一个自定义解析器。正则表达式版本不会那么快,但如果它足够快,它的好处是更小,并且比自定义解析器更容易理解和修改。
https://stackoverflow.com/questions/14041495
复制相似问题