繁体   English   中英

StackOverflowException在非无限的递归字符串搜索中

[英]StackOverflowException in non-infinite, recursive string search

背景。 我的脚本遇到StackOverflowException,同时递归搜索大字符串中的特定文本。 循环不是无限的; 问题发生在9,000-10,000次合法搜索之间(对于特定搜索) - 我需要它继续前进。 我正在使用尾递归(我认为),这可能是我的问题的一部分,因为我认为C#不能很好地做到这一点。 但是,我不确定如何避免在我的情况下使用尾递归。

问题(S)。 为什么发生StackOverflowException? 我的整体方法是否有意义? 如果设计很糟糕,我宁愿从那里开始,而不仅仅是避免异常。 但如果设计可以接受,我该怎么办StackOverflowException呢?

码。 我编写的课程在大量文本(大约6MB)中搜索联系人(大约500+来自指定列表)。 我正在使用的策略是搜索姓氏,然后在姓氏之前或之后的某个地方查找名字。 我需要找到给定文本中每个联系人的每个实例。 StringSearcher类有一个递归方法,它继续搜索联系人,每当找到一个联系人时返回结果,但跟踪搜索中断的位置。

我以下列方式使用此类:

StringSearcher searcher = new StringSearcher(
    File.ReadAllText(FilePath),
    "lastname",
    "firstname",
    30
);

string searchResult = null;
while ((searchResult = searcher.NextInstance()) != null)
{
    // do something with each searchResult
}

总的来说,脚本似乎有效。 大多数联系人返回我期望的结果。 但是,当主搜索字符串非常常见(数千次点击),并且次要搜索字符串从未或很少发生时,似乎会出现问题。 我知道它不会卡住,因为CurrentIndex正在正常推进。

这是我正在谈论的递归方法。

public string NextInstance()
{
    // Advance this.CurrentIndex to the next location of the primary search string
    this.SearchForNext();

    // Look a little before and after the primary search string
    this.CurrentContext = this.GetContextAtCurrentIndex();

    // Primary search string found?
    if (this.AnotherInstanceFound)
    {
        // If there is a valid secondary search string, is that found near the
        // primary search string? If not, look for the next instance of the primary
        // search string
        if (!string.IsNullOrEmpty(this.SecondarySearchString) &&
            !this.IsSecondaryFoundInContext())
        {
            return this.NextInstance();
        }
        // 
        else
        {
            return this.CurrentContext;
        }
    }
    // No more instances of the primary search string
    else
    {
        return null;
    }
}

StackOverflowException发生在this.CurrentIndex = ...中,方法如下:

private void SearchForNext()
{
    // If we've already searched once, 
    // increment the current index before searching further.
    if (0 != this.CurrentIndex)
    {
        this.CurrentIndex++;
        this.NumberOfSearches++;
    }

    this.CurrentIndex = this.Source.IndexOf(
            this.PrimarySearchString,
            ValidIndex(this.CurrentIndex),
            StringComparison.OrdinalIgnoreCase
    );

    this.AnotherInstanceFound = !(this.CurrentIndex >= 0) ? false : true;
}

如果需要,我可以包含更多代码。 如果其中一种方法或变量值得怀疑,请告诉我。

*性能并不是真正的问题,因为这可能会在晚上作为计划任务运行。

你有一个1MB的堆栈。 当该堆栈空间用完并且您仍需要更多堆栈空间时,将抛出StackOverflowException 这可能是也可能不是无限递归的结果,运行时不知道。 无限递归只是使用更多堆栈空间的一种有效方式,然后可用(通过使用无限量)。 你可以使用一个有限的数量,这恰好比现有的更多,你会得到相同的例外。

虽然还有其他方法可以占用大量的堆栈空间,但递归是最有效的方法之一。 每种方法都根据该方法的签名和本地添加更多空间。 深度递归可以使用大量的堆栈空间,因此如果您希望深度超过几百个级别(甚至那么多),您可能不应该使用递归。 请注意,任何使用递归的代码都可以迭代编写,或使用显式Stack

很难说,因为没有显示完整的实现,但基于我可以看到你或多或少编写迭代器,但你没有使用C#构造(即IEnumerable )。

我的猜测是“迭代器块”将允许您使这个算法更容易编写,更容易编写非递归,并且更有效地从调用者方。

以下是如何将此方法构造为迭代器块的高级视图:

public static IEnumerable<string> SearchString(string text
    , string firstString, string secondString, int unknown)
{
    int lastIndexFound = text.IndexOf(firstString);

    while (lastIndexFound >= 0)
    {
        if (secondStringNearFirst(text, firstString, secondString, lastIndexFound))
        {
            yield return lastIndexFound.ToString();
        }
    }
}

private static bool secondStringNearFirst(string text
    , string firstString, string secondString, int lastIndexFound)
{
    throw new NotImplementedException();
}

这似乎不是递归是正确的解决方案。 通常,对于递归问题,您会将某些状态传递给递归步骤。 在这种情况下,你真的有一个简单的while循环。 下面我将你的方法体放在一个循环中,并改变递归步骤continue 看看是否有效......

public string NextInstance()
{
    while (true)
    {
        // Advance this.CurrentIndex to the next location of the primary search string
        this.SearchForNext();

        // Look a little before and after the primary search string
        this.CurrentContext = this.GetContextAtCurrentIndex();

        // Primary search string found?
        if (this.AnotherInstanceFound)
        {
            // If there is a valid secondary search string, is that found near the
            // primary search string? If not, look for the next instance of the primary
            // search string
            if (!string.IsNullOrEmpty(this.SecondarySearchString) &&
                !this.IsSecondaryFoundInContext())
            {
                continue; // Start searching again...
            }
            // 
            else
            {
                return this.CurrentContext;
            }
        }
        // No more instances of the primary search string
        else
        {
            return null;
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM