StackOverflow终止循环出错



为了提供一些上下文:我最近开始和一群朋友玩龙与地下城。我决定尝试制作一个程序,让我可以按级别、魔法学校等搜索咒语。为此,我制作了一个文本文件,其中包含每个咒语及其信息,按咒语名称的字母顺序列出,并创建了一些正则表达式来对其进行排序。我终于得到了它,它给了我每个属性的正确结果。但是,一旦我把它放在一个循环中以一次获得所有内容,我就会得到一长串错误,从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();
}

如果有帮助的话,我在这里有我正在使用的列表。正如我在代码的注释中提到的,循环4次以上会出现这个错误,但4次以下不会。我也试过把它作为while循环来做,但我也得到了同样的错误。

我试着在网上搜索解决方案,但我看到的关于这个错误的一切都只是关于递归调用。如果有人对此有解决方案,我将不胜感激。谢谢。

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

编辑2:我发现,如果我简单地用";\nCache时间:";它贯穿了整个过程,没有任何错误。当然,唯一的问题是它没有收集到我想要的所有信息。希望这些信息能帮助确定问题所在。

最终编辑:当我找到导致问题的特定行时,我做了更多的搜索,并发现增加堆栈大小可以解决问题。

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

首先,如果要匹配任何(包括换行符),则应始终使用DOTALL选项。像CCD_ 1这样的交替的效率要低得多。(这也是不正确的。点匹配任何不是行终止符的东西,它可能不仅仅是n。)

其次,这种交替是在一个捕获组内,而量词在组外:(.|n)+?。这意味着你一次捕获一个字符,却用下一个字符覆盖捕获的字符,以此类推。你让正则表达式引擎做了很多不必要的工作。

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

"(?ms)^Casting Time: (.+?)nn" 

可以使用内联修饰符(?s)激活DOTALL选项。我还使用了MULTILINE选项,它使我可以用^将匹配项锚定到行的开头。这样,就不需要消耗领先的n,只需要稍后将其砍掉。事实上,如果使用group(1)而不是group(),那么后面的nn也将被排除在外。

至于RegExr,它使用了与Java不同的regex风格——一种功能少得多的regex。选择.|n0选项后,大多数Java正则表达式将在优秀的Regex101站点上工作。为了获得绝对的兼容性,这里有RegexPlanet的Java页面,或者像Ideone这样的代码测试网站。

最新更新