终止循环中的StackOverflowError?

时间:2017-01-28 01:48:28

标签: java regex loops overflow

提供一些背景信息:我最近开始和一群朋友一起玩“龙与地下城”。我决定尝试制作一个允许我按级别,魔法学校等方式搜索法术的程序。为此,我拿了一个文本文件,其中包含每个法术及其信息按字母顺序列出的拼写名称,并创建了一个很少有正则表达式来对它进行排序。我终于得到它给我每个属性的正确结果。但是一旦我把它放在循环中以便立即获取所有内容,我会得到一长串错误,从StackOverflowError开始。据我所知,当你得到无限循环时,这应该会发生,但我的确无法终止。此外,我可以手动进一步循环(使用循环检查我在每个循环结束时使用键盘设置的布尔值),而不是使用简单的for或while循环。

我正在使用的代码如下。我没有包括Spell类,因为它只是标准的getter / setter和变量声明。我所拥有的学校类型只是八所学校的一个校友。

Map<String, Spell> allSpells = new HashMap<String, Spell>();
    ArrayList<Spell> spellArray = new ArrayList<Spell>();

    int finalLevel;
    int lastMatch = 0;
    int startIndex = 0;
    Matcher match;
    String finalTitle;
    Spell.School finalSchool;
    String finalDescription;
    String fullList;


    String titleString = ".+:\\n";                                          //Finds the titles of spells
    Pattern titlePattern = Pattern.compile(titleString);
    String levelString = "\\d\\w+-level";                                   //Finds the level of spells
    Pattern levelPattern = Pattern.compile(levelString);
    String schoolString = "(C|c)onjuration|(A|a)bjuration|(E|e)nchantment|(N|n)ecromancy|(E|e)vocation|(D|d)ivination|(I|i)llusion|(T|t)ransmutation";  //Finds the school of spells
    Pattern schoolPattern = Pattern.compile(schoolString);
    String ritualString = "\\(ritual\\)";                                   //Finds if a spell is a ritual
    Pattern ritualPattern = Pattern.compile(ritualString);
    String descriptionString = "\nCasting Time: (.|\\n)+?(\\n\\n)";         //Finds the description of spells
    Pattern descriptionPattern = Pattern.compile(descriptionString);

    try
    {
        BufferedReader in = new BufferedReader(new FileReader("Spell List.txt"));

        // buffer for storing file contents in memory
        StringBuffer stringBuffer = new StringBuffer("");

        // for reading one line
        String line = null;

        // keep reading till readLine returns null
        while ((line = in.readLine()) != null) 
        {
            // keep appending last line read to buffer
            stringBuffer.append(line + "\n");
        }
        fullList = stringBuffer.toString();     //Convert stringBuffer to a normal String. Used for setting fullList = a substring

        boolean cont = true;
        for(int i = 0; i < 100; i++) //This does not need to be set to 100. This is just a temporary number. Anything over 4 gives me this error, but under 4 I am fine. 
        {
            //Spell Title
            match = titlePattern.matcher(fullList);                             
            match.find();                                                       //Makes match point to the first title found
            finalTitle = match.group().substring(0, match.group().length()-1);  //finalTitle is set to found group, without the newline at the end
            allSpells.put(finalTitle, new Spell());                             //Creates unnamed Spell object tied to the matched title in the allSpells map
            spellArray.add(allSpells.get(finalTitle));                          //Adds the unnamed Spell object to a list. 
                                                                                //To be used for iterating through all Spells to find properties matching criteria


            //Spell Level
            match = levelPattern.matcher(fullList.substring(match.end(), match.end()+50));  //Gives an approximate region in which this could appear
            if(match.find())    //Accounts for cantrips. If no match for a level is found, it is set to 0
            {
                finalLevel = Integer.valueOf(match.group().substring(0, 1));
            }
            else
            {
                finalLevel = 0;
            }
            allSpells.get(finalTitle).setSpellLevel(finalLevel);


            //Spell School
            match = schoolPattern.matcher(fullList);
            match.find();
            finalSchool = Spell.School.valueOf(match.group().substring(0, 1).toUpperCase() + match.group().substring(1, match.group().length())); //Capitalizes matched school
            allSpells.get(finalTitle).setSpellSchool(finalSchool);


            //Ritual?
            match = ritualPattern.matcher(fullList.substring(0, 75));
            if(match.find())
            {
                allSpells.get(finalTitle).setRitual(true);
            }
            else
                allSpells.get(finalTitle).setRitual(false);



            //Spell Description
            match = descriptionPattern.matcher(fullList);
            match.find();
            finalDescription = match.group().substring(1);      //Gets rid of the \n at the beginning of the description
            allSpells.get(finalTitle).setDescription(finalDescription);

            lastMatch = match.end();
            System.out.println(finalTitle);
            fullList = fullList.substring(lastMatch);

        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

如果有帮助,我有我正在使用的列表here。 正如我在代码的注释中提到的,通过循环超过4次给了我这个错误,但是在4下没有。我也尝试过这样做一个while循环,我得到了同样的错误。

我曾尝试在线搜索解决方案,但我看到的有关此错误的所有信息都只是谈到了递归调用。如果有人有解决方案,我会非常感激。感谢。

编辑:我收到的错误列表很大,所以我把它放在一个文本文件中here 。我知道人们要求堆栈跟踪,我希望这就是他们的意思。我还是比较新的java,从来没有使用堆栈跟踪。

编辑2:我发现如果我只是用“\ nCast Time:”替换描述正则表达式,它会在整个事情中运行而没有错误。当然,唯一的问题是它没有收集我想要的所有信息。希望这些信息有助于确定问题。

最终编辑:一旦找到导致问题的特定行,我做了一些搜索,发现增加堆栈大小解决了问题。

1 个答案:

答案 0 :(得分:0)

通过增加堆栈大小,您正在处理症状并使问题得不到解决。在这种情况下,问题是效率低下的正则表达式。

首先,如果你想匹配任何包括换行符,你应该总是使用DOTALL选项。像.|\n这样的替代效率要低得多。 (它也是不正确的。点匹配任何不是line terminator的东西,这可能远远超过\n。)

其次,该替换是在捕获组内部,量化器位于组外:(.|\n)+?。这意味着你一次捕获一个角色,只用下一个角色覆盖捕获的角色,依此类推。你正在使正则表达式引擎做了很多不必要的工作。

这是我要使用的正则表达式:

"(?ms)^Casting Time: (.+?)\n\n" 

可以使用内联修饰符(?s)激活DOTALL选项。我还使用了MULTILINE选项,它允许我使用^将匹配锚定到行的开头。这样,就没有必要消耗领先的\n,只是稍后将其删除。实际上,如果您使用group(1)代替group(),则也会排除尾随\n\n

对于RegExr,它使用与Java不同的正则表达式 - 一个功能少得多的一个。大多数Java正则表达式都适用于选择了pcre (php)选项的优秀Regex101站点。为了绝对兼容,请使用RegexPlanet的Java page或代码测试网站Ideone