【问题标题】:Between of two strings两个字符串之间
【发布时间】:2011-06-22 04:19:58
【问题描述】:

我在 C# 中有简单的方法:

public static string BetweenOf(string ActualStr, string StrFirst, string StrLast)
        {
            return ActualStr.Substring(ActualStr.IndexOf(StrFirst) + StrFirst.Length, (ActualStr.Substring(ActualStr.IndexOf(StrFirst))).IndexOf(StrLast) + StrLast.Length);
        }

我该如何优化这个?

【问题讨论】:

  • 我想仔细的代码分析会告诉你这个方法实际上是你应用程序性能的瓶颈?
  • 如果我调用它 100,000 次,这个方法会非常慢,所以建议一个替代逻辑来获得与这个函数中表达的相同的输出。
  • @Rahul:你为什么要调用该方法 100,000 次?我不是在谈论独立地对方法进行基准测试。我说的是分析您的实际 应用程序 代码。
  • 哦,对了,就是“我认为这很慢,所以我将运行它非常多的次数并进行基准测试来证明它!”又是心态。

标签: c# .net optimization string


【解决方案1】:

如果我理解了你想要做什么,我认为你的实现可能不正确。

这是一个我相信至少在 GC 方面会表现更好的实现,因为它不使用对 SubString 的多次调用,这会在堆上创建新的字符串,这些字符串只是临时使用的。

public static string BetweenOfFixed(string ActualStr, string StrFirst, string StrLast)
{
  int startIndex = ActualStr.IndexOf(StrFirst) + StrFirst.Length;
  int endIndex = ActualStr.IndexOf(StrLast, startIndex);
  return ActualStr.Substring(startIndex, endIndex - startIndex);
}

比较这个与正则表达式解决方案的性能会很有趣。

【讨论】:

  • 谢谢!!但是你能告诉我为什么要声明 startIndex,endIndex 如果它们没有用
  • 两者都使用,endIndex 一次,startIndex 两次。
  • @Rahul,正如@Simon 所说,两者都被使用。在最终的 SubString 中,我使用 StartIndex 作为字符串的偏移量,然后使用 endIndex - startIndex 来获取要从字符串中提取的字符数,这给出了 strFirst 和 strLast 之间的字符串部分。
  • 我将代码与正则表达式进行了比较,我将其作为 CW 的答案发布,因为我没有任何要添加的内容。这里:stackoverflow.com/questions/4912155/between-of-two-strings/…
【解决方案2】:

下面是来自@Chris here 的代码如何与正则表达式测试相叠加:

void Main()
{
    string input = "abcdefghijklmnopq";
    string first = "de";
    string last = "op";
    Regex re1 = new Regex("de(.*)op", RegexOptions.None);
    Regex re2 = new Regex("de(.*)op", RegexOptions.Compiled);

    // pass 1 is JIT preheat
    for (int pass = 1; pass <= 2; pass++)
    {
        int iterations = 1000000;
        if (pass == 1)
            iterations = 1;

        Stopwatch sw = Stopwatch.StartNew();
        for (int index = 0; index < iterations; index++)
            BetweenOfFixed(input, first, last);
        sw.Stop();
        if (pass == 2)
            Debug.WriteLine("IndexOf: " + 
                sw.ElapsedMilliseconds + "ms");

        sw = Stopwatch.StartNew();
        for (int index = 0; index < iterations; index++)
            BetweenOfRegexAdhoc(input, first, last);
        sw.Stop();
        if (pass == 2)
            Debug.WriteLine("Regex adhoc: " + 
                sw.ElapsedMilliseconds + "ms");

        sw = Stopwatch.StartNew();
        for (int index = 0; index < iterations; index++)
            BetweenOfRegexCached(input, first, last);
        sw.Stop();
        if (pass == 2)
            Debug.WriteLine("Regex uncompiled: " +
                sw.ElapsedMilliseconds + "ms");

        sw = Stopwatch.StartNew();
        for (int index = 0; index < iterations; index++)
            BetweenOfRegexCompiled(input, first, last);
        sw.Stop();
        if (pass == 2)
            Debug.WriteLine("Regex compiled: " +
                sw.ElapsedMilliseconds + "ms");
    }
}

public static string BetweenOfFixed(string ActualStr, string StrFirst,
    string StrLast)
{
    int startIndex = ActualStr.IndexOf(StrFirst) + StrFirst.Length;
    int endIndex = ActualStr.IndexOf(StrLast, startIndex);
    return ActualStr.Substring(startIndex, endIndex - startIndex);
}

public static string BetweenOfRegexAdhoc(string ActualStr, string StrFirst,
    string StrLast)
{
    // I'm assuming you don't replace the delimiters on every call
    Regex re = new Regex("de(.*)op", RegexOptions.None);
    return re.Match(ActualStr).Groups[1].Value;
}

private static Regex _BetweenOfRegexCached =
    new Regex("de(.*)op", RegexOptions.None);
public static string BetweenOfRegexCached(string ActualStr, string StrFirst,
    string StrLast)
{
    return _BetweenOfRegexCached.Match(ActualStr).Groups[1].Value;
}

private static Regex _BetweenOfRegexCompiled =
    new Regex("de(.*)op", RegexOptions.Compiled);
public static string BetweenOfRegexCompiled(string ActualStr, string StrFirst,
    string StrLast)
{
    return _BetweenOfRegexCompiled.Match(ActualStr).Groups[1].Value;
}

输出:

IndexOf: 1419ms
正则表达式即席:7788ms
正则表达式未编译:1074ms
正则表达式编译:682ms

【讨论】:

  • 您是否根据Debug.WriteLine 语句将其作为发布代码运行构建,我猜您可能已在IDE 中将其作为调试构建运行。我把你的代码改成了Console.WriteLine 并做了一个发布版本。从控制台窗口运行我得到以下结果 399ms、9083ms、1258ms、806ms。我在 Framework 4.0 上进行了测试。
【解决方案3】:

你可以构造一个正则表达式:

var regex = strFirst + "(.*)" + strLast;

您的中间文本将是匹配的第一个(也是唯一一个)捕获。

【讨论】:

  • 你肯定想在这些字符串上使用Regex.Escape
  • @Kobi : 你能告诉我如何删除转义序列,因为我是通过 Replace("\n","").Replace("\0","").Replace( "\\","") 等等。
【解决方案4】:

使用正则表达式怎么样?这可能比构建临时字符串更快。 这也可以轻松轻松地处理找不到此类字符串的情况。

【讨论】:

    猜你喜欢
    • 2012-12-28
    • 2015-04-10
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    相关资源
    最近更新 更多