2017-01-28 4 views
-3

いくつかの文脈を与える:私は最近、友人のグループと一緒にダンジョンズとドラゴンを始めました。私はレベル、魔法学校などで呪文を検索できるプログラムを作ろうと思った。これを行うために、綴りとその情報を綴り名でアルファベット順に並べたテキストファイルを作成し、それをすべてソートする正規表現はほとんどありません。私は最終的にすべての属性に対して正しい結果を得ることができました。しかし、一度にすべてを取得するためにループに入れたら、StackOverflowErrorから始まる長いエラーリストを取得します。私が知っている限り、これはあなたが無限のループを得るときに起こるはずですが、私は間違いなく終了します。さらに、私は単純なforループまたはwhileループでできるよりも、手作業で(各ループの終わりにキーボードで設定したブール値をチェックするループを使用して)さらに遠くにループすることができます。終結ループのStackOverflowError?

私が使用しているコードは以下の通りです。 Spellクラスは標準getter/setterと変数宣言のため含まれませんでした。私が持っている学校のタイプは、8つの学校の列挙です。

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であり、以前はスタックトレースで作業する必要はありませんでした。

EDIT 2:説明正規表現を単に "\ nキャストタイム:"に置き換えると、エラーなしで全体が実行されることがわかりました。唯一の問題は、もちろん、私が望むすべての情報を収集しないということです。うまくいけば、この情報は問題の判定に役立ちます。

最終編集:問題の原因となる特定の行が見つかったら、もう少し検索を行い、スタックサイズを大きくすると問題が解決されました。

+3

スタックトレースはどのように見えますか? – Axel

+0

例外がスローされる場所には、スタックトレースとコード内の場所が必要です。それ以外の場合は、何が問題の原因になるかは分かりません。 – Paul

+0

私はエラーリストを追加しました。それがあなたが求めていたものでないなら、私に知らせてください。私は正しい情報を提供しようとします。 – Stormfather

答えて

0

スタックサイズを増やすことで、症状を治療していて問題は解決していません。この場合、問題は非効率な正規表現です。

最初に、改行を含むと一致させる場合は、常にDOTALLオプションを使用する必要があります。 .|\nのような交代はずっと効率が悪いです。 (.|\n)+?:(。。また、誤っだドットがちょうど\nよりもはるかにすることができline terminator、ないものにマッチ)

第二に、その交代はグループ外の数量詞で、キャプチャグループ内にあります。つまり、キャプチャしたキャラクタを次のキャラクタで上書きするなど、一度に1つのキャラクタをキャプチャしているということです。あなたは正規表現エンジンに多くの不必要な作業をさせています。ここで

は、私が使用する正規表現です:

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

DOTALLオプションはインライン修飾子、(?s)で活性化することができます。私も^との行の先頭にマッチを固定することができるマルチラインオプションを使用しました。そうすれば、先導番号\nを消費する必要はなく、後でそれを切り落とすだけです。実際、group()の代わりにgroup(1)を使用すると、末尾の\n\nも除外されます。

RegExrに​​ついては、Javaとは異なる正規表現のフレーバを使用しています。ほとんどのJava正規表現はpcre (php)オプションが選択された素晴らしいRegex101サイトで動作します。絶対的な互換性のために、RegexPlanetのJava page、またはIdeoneのようなコードテストサイトがあります。

+0

お手伝いをしていただきありがとうございます – Stormfather