2017-01-28 41 views
-3

給出一些背景:我最近開始和一羣朋友一起玩龍與地下城。我決定嘗試製作一個程序,讓我按等級,魔法學校等搜索法術。爲此,我用一個拼寫名稱按字母順序列出了每個咒語及其信息的文本文件,並創建了一個很少有正則表達式來排序。我終於得到它給我每個屬性的正確結果。但是一旦我把它放在一個循環中以便一次獲得所有內容,我就會從StackOverflowError開始得到一長串錯誤。據我所知,這應該發生在你得到無限循環時,但我的絕對終止。而且,我可以用一個簡單的for或while循環手動進行更遠的循環(用循環來檢查我在每個循環結束時用鍵盤設置的布爾值)。終止循環中的StackOverflowError?

我使用的代碼如下。我沒有包含Spell類,因爲它只是標準的getters/setters和變量聲明。我所擁有的學校類型只是八所學校的一個枚舉。

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:我發現,如果我只需更換與描述正則表達式「\ nCasting時間:」它貫穿了整個事情沒有錯誤。唯一的問題當然是它不會收集我想要的所有信息。希望這些信息有助於確定問題。

最後編輯:我做了一點搜索一次,我發現造成問題的具體路線,並發現,增加堆棧大小固定的問題。

+3

堆棧跟蹤是什麼樣的? – Axel

+0

我們需要一個堆棧跟蹤和拋出異常的代碼中的位置。否則,很難說出可能導致問題的原因。 – Paul

+0

我添加了我收到的錯誤列表。如果這不是你問的問題,請讓我知道,我會盡力給出正確的信息。 – Stormfather

回答

0

通過增加堆棧大小,你治療的症狀和離開的問題沒有解決。在這種情況下,問題是一個低效的正則表達式。

首先,如果你想匹配任何東西包括換行符,你應該總是使用DOTALL選項。像.|\n這樣的交替效率要低得多。 (這也是不正確的點匹配任何這不是一個line terminator,這可不僅僅是\n得多。)

其次,交替是捕獲組內,與集團外的量詞:(.|\n)+?。這意味着你一次只捕獲一個角色,只是用下一個角色覆蓋捕獲的角色,等等。你正在使正則表達式引擎做很多不必要的工作。

這裏的正則表達式,我會用:

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

的DOTALL選項可以與在線修改,(?s)被激活。我還使用了MULTILINE選項,它可以讓我將匹配定位到^的開頭。這樣,就沒有必要消費領先的\n,只能在稍後切斷。實際上,如果您使用group(1)而不是group(),則尾部\n\n也將被排除。

至於RegExr,它使用了不同於Java的正則表達式 - 一個功能少得多。大多數Java正則表達式將在出色的Regex101網站上工作,並且選擇pcre (php)選項。對於絕對兼容性,RegexPlanet的Java page或代碼測試站點如Ideone

+0

謝謝你的幫助和明確的解決方案 – Stormfather