正直メソッドや変数の命名のセンスがないのは自分でよく分かっています。
このプログラムを見る際は、その辺を理解して見てください。
また、ここでは初心者が読んでも理解できるようにしています。ちなみに俺の初心者の定義は、Javaを始めたばっかりで何も分からない人~専門学校での初心者クラスレベルの人です。
あと、ちゃんとJavaやってる人からすれば何だこれと思うかもしれませんが、俺の場合、初心者がよくthrowを忘れることを想定してあえてthrowを使わずに書いています。
初心者の皆さん、本当はthrow使ったほうがもう少し短くなるんですよ?
import java.io.*; import java.util.Random; public class Main { //インポートクラスの定義 static Random rand = new Random(); static BufferedReader input = new BufferedReader (new InputStreamReader (System.in)); //メインメソッド public static void main(String[] args){ int randMax = getRandSeed("最大値",Integer.MAX_VALUE,Integer.MIN_VALUE); int randMin = getRandSeed("最小値",Integer.MAX_VALUE,Integer.MIN_VALUE); int makeRand = getRandNumber(); System.out.println("Randomクラスの乱数"); for(int i = 1; i <= makeRand; i++){ System.out.print(NextIntRandom(randMax,randMin) + " "); } for (int j = 1; j <= 2; j++){ System.out.println(); } System.out.println("Mathクラスの乱数"); for(int k = 1; k <= makeRand; k++){ System.out.print(mathRandom(randMax,randMin) + " "); } } //乱数の最大値と最小値を決める public static int getRandSeed(String s, int max, int min){ System.out.println("乱数の" + s + "を入力してください。(" + max + "から" + min +"まで)"); System.out.println("正しく値が入力されない場合、再入力となります。"); return getnumAssistant(max,min); } //生成する乱数の個数を決める public static int getRandNumber(){ System.out.println("いくつの乱数を生成しますか?(最大30個まで)"); System.out.println("正しく値が入力されない場合、再入力となります。"); return getnumAssistant(30,0); } //Randomクラスの乱数生成器 public static int NextIntRandom(int max,int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } return rand.nextInt(max - min) + min; } //Mathクラスの乱数生成器 public static int mathRandom(int max,int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } return (int)(Math.ceil(Math.random())*(max - min)) + min; } //値を取得するメソッド public static int getnum(){ try { return Integer.parseInt(input.readLine()); } catch (IOException | NumberFormatException e) { return Integer.MIN_VALUE; } } //getnumメソッドが返すエラーの処理および値が最大値と最小値の間にあるかチェックするメソッド public static int getnumAssistant(int max,int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } if(max == min) max += 10;//最大値と最小値が一致しているとき、最大値を+10する int inputNum; boolean flag; do{ inputNum = getnum(); flag = (inputNum == Integer.MIN_VALUE); if(min == Integer.MIN_VALUE) flag = false; }while(flag || inputNum > max || inputNum < min); return inputNum; } }
ここでは2つの乱数生成器を使っています。
一つはRandomクラスを用いた乱数生成器で、もう一つはMathクラスを用いた乱数生成器です。
作り方はもしかするとパッと見ただけで分かるんじゃないかな…。
まあ俺の場合、最大値と最小値が逆になっても大丈夫なようにそこの処理もしてあるんですが…。
注意しなくちゃいけないことは、まず、Randomクラスの乱数生成器を使う時は、必ず「import java.util.Random;」ってのを書いてあげなきゃいけないということですね。
俺が書いたものも2行目にありますよね?あれがないとRandomクラスを使用することはできません。
次にMathクラスですが、こちらは必ず頭にintをつけないといけないということですね。
正直俺もあんまりMathクラスの原理は分かってないんですよね…。だからもう少し詳しく知りたい方はこちらを参照してください。
とりあえず乱数生成器についてはこのくらいかな…。
正直このメソッドはもうそのまま使ってと投げてもいいかなと思うくらい話すことがないです。
まあ簡単にお話しすると、getnumメソッドで値の入力を受けてそのエラー処理とかをgetnumAssistantメソッドがやってくれるって感じです。
まずgetnumメソッドですが、これは名前の通り、値の入力を受けるメソッドです。
まずinput.readLine()で数字を文字列として取得し、それをInteger.parseInt()で数字に変換しています。
俺が作ったメソッドの場合、入力過程や値への変換過程でエラーが生じると、int型の最小値を返すようにしています。
しかし、このメソッドだけではエラーが起きた時、int型の最小値が返されると言いましたよね?(…ってさっき言ったばっかだけど…(汗))
それだとどうでしょう?もしこの値で年齢とか身長の値の入力を受けてエラーが生じた時、あなたの年齢や身長の値はint型の最小値、つまり-2147483648になってしまいます。
もしこのメソッドがlong型なら-9223372036854775808ですよ?おかしいですよね?(笑)
そこでそんなことにならないようにしてくれているのがgetnumAssistantメソッドです。
やってることは至ってシンプルです。
まずgetnumメソッドから値が飛んできます。それをinputNumという変数に格納しています。そして次にboolean型のflagという変数がありますよね?これはinputNum変数にint型の最小値が格納されるとtrueが入ります。
do-while文は知ってますよね?do-while文は、whileの( )の中身が真の間、ループするというものです。言ってしまえばやってることはwhile文と同じです。
このメソッド内のdo-while文の場合、getnumメソッドがエラーを起こした場合またはgetnumメソッドから返ってきた値があらかじめ設定された最大値より大きいまたは最小値より小さい時、ループするというふうになっています。
つまり、getnumメソッドがエラーを起こしていないかつ最大値と最小値の間にある時以外はループを抜けるというふうになっています。え?もし最小値をint型の最小値に設定しててint型の最小値を入力したらはじかれるじゃないかって?
flagの下のif文よーく読もうね。もし最小値がint型の最小値の時はflagがfalseになるようにしてあるよね?
まあ…これだと最小値にint型の最小値を設定しててgetnumメソッドがエラー返してきたらループ抜けちゃうっていう欠陥もあるんだけどね…(- -;)
かといってあのif文消すと最小値をint型の最小値にしててint型の最小値を入力すると受けられないんだよね…(- -;)
そこで改良版を作ってみました。なんか結構嫌なものになりましたが…。
public static int getnum(int max, int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } int i = 0; boolean flag; do{ try { i = Integer.parseInt(input.readLine()); flag = true; } catch (IOException | NumberFormatException e) { flag = false; } }while(!flag || i < min || i > max); return i; }
とりあえず暫定版ですがこんなのでどうでしょう?
はい、汚いのは自分でも重々承知しています。
そもそもgetnumAssistantにあった機能も全部こっちに統合しちゃいましたからね。
これでさらにgetnumAssistantコピペで持ってきたらしっかりけなしてあげます。意味ないです。getnumAssistantの機能入ってるんだから。
xmlやjson、ini等の外部ファイルからデータを文字列として受け取ってもそれを数字に変換できるようにgetnumメソッドから入力を受けた文字列を数字に変換する機能の部分を分離してみました。
キーボード入力でしか値を持ってこないならこんなことする必要ないんですが…。
ちなみに上のgetnumAssistantをコピぺして併用することも可能です。
public static int getnum(){ try { return convertStringToNum(input.readLine()); } catch (IOException e1) { return Integer.MIN_VALUE; } } public static int convertStringToNum(String num){ try{ return Integer.parseInt(num); }catch(NumberFormatException e){ return Integer.MIN_VALUE; } }
見れば分かりますが、convertStringToNumはあくまで引数として受けた文字列を数字に変換する機能しかないメソッドなので、値の入力を受けるためにといってconvertStringToNumを呼んでもあなたの
求めてるようなことはできません。
こうすればコンバーター呼び出しでできるけどこんな使い方されるために分けたわけじゃないですよ。それにint型の最小値を一度String型にしてまた数字化するとか馬鹿としか思えません。やめてください。
一応getnumAssistantも変わるので一応書いておきます。あ…ここで使ってるgetnumメソッドは改良前のやつね。
public static void main(String[] args){ int i = getnum(4,1); System.out.println(i); } public static String getString(){ try { return = input.readLine(); } catch (IOException e1) { return String.valueOf(Integer.MIN_VALUE); } } public static int convertStringToNum(String num){ try{ return Integer.parseInt(num); }catch(NumberFormatException e){ return Integer.MIN_VALUE; } } public static int getnum(int max,int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } if(max == min) max += 10;//最大値と最小値が一致しているとき、最大値を+10する int inputNum; boolean flag; do{ inputNum = convertStringToNum(getString()); flag = (inputNum == Integer.MIN_VALUE); if(min == Integer.MIN_VALUE) flag = false; }while(flag || inputNum > max || inputNum < min); return inputNum; }
うわー…見づらい。もうやだ、見たくない。
え?改良版のほうもgetnumAssistantの機能は別のメソッドにできないのって?できるよ。
でもあんまり意味ないよ?無駄にメソッドが増えるだけだしgetnumメソッドのエラー処理するわけでもないし。だってgetnumメソッドは値取得する過程でエラー起こすとループするし…。
分離する機能といったら最大値と最小値の設定機能くらいなわけで、こうなります。
public static int getnum(){ int i = 0; boolean flag; do{ try { i = Integer.parseInt(input.readLine()); flag = true; } catch (IOException | NumberFormatException e) { flag = false; } }while(!flag || i < min || i > max); return i; } public static int getnumAssistant(int max, int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } int inputNum; do{ inputNum = getnum(); }while(inputNum < min || inputNum > max); return i; }
なんか今回はすっげー嫌なもの書かされた気がする…。
そこ、「お前が勝手に書いただけだろ」とか言わない。
じゃああなたは絶対聞かないんだな?聞かないならそう言ってもいいよ。…というかそういうこと言えるならこのページ見なくても自分で値取得メソッドとか作れるだろ。
まあいいや。あと、ゲーム作るならこんなの使えばどうかな?
public static String getString(){ try { return input.readLine(); } catch (IOException e) { return ""; } } public static int convertStringToNum(String s){ int i = 0; boolean flag; do{ try { i = Integer.parseInt(s); flag = true; } catch(NumberFormatException e){ flag = false; } }while(!flag); return i; } public static int getnum(int max,int min){ if(max < min){//最小値が最大値より大きいとき、値を入れ替える int a = max; max = min; min = a; } int i; do{ i = convertStringToNum(getString()); }while(i < min || i > max); return i; }
え?getnumAssistantがgetnumになってる。誤植じゃないのって?
いや…これでいいんだけど…。だってgetnumメソッドでやってることって、コンバーターにString引数としてgetString入れてるから、getStringで数字を文字列として取得してコンバーターで
数字に変換してint型変数iに代入してそれが最大値と最小値の間にあるか判定して問題なければiに代入された値を返す。何もおかしくないよね?
ばらかした理由?え?それ聞くってことはまさかあなた、文字列取得メソッド新しく用意しようと考えてた?
値取得するのにもこのメソッドの中身の処理は必要あったよね?それだったら値取得メソッドだけで独占しないで文字列取得する必要があるところと仲良く使えばいいよね?
じゃあ文字列から値に変換するためのコンバーターは分離する必要あったのかって?
あなたが作るゲームはセーブできないの?うわーやりたくないわー…。
ゲームのセーブデータはソース内にもRAMにも残しておけないんだよ?
じゃあどこに残すの?ストレージ内しかないでしょ。つまり外部ファイルとして保存しとかなきゃいけないよね?
でも外部ファイルに保存する時はString型で保存するし、読み込む時だってString型で読み込むんだよ?
そうなったらRPGだとしたらHPやMPはずっとString型だから表示することはできても足し算や引き算はできないよね?
じゃあどうする?数字に変換するしかないよね?
じゃあ新しくメソッドを作ればいいじゃんって?2度も言わせないでくれないかな?仲良く使えばいいじゃん。
なんでそんな無駄なことに労力を使おうとするの?意味分かんないんだけど。
あるものをまた新しく作ろうとするなんて時間と労力の無駄。それやる時間あるなら他のシステム書けるしリリースだって早くできるじゃん。
あとはこんなふうにすると行減らしができますよ。
一応使い方も分かるようメインメソッドの例も書いておきます。
import java.io.*; public class Sample{ static BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); public static void main(String[] args){ } public static int getString(String message){ }
こんなメソッドを作ってみました。
import java.io.*; import java.util.*; public class Sample { public static double log(double base, double antilogarithm, String patchPath, String patchKey){ //対数計算。底や真数は0に設定することで自然対数の底"e"として計算する。 if(judgeLogSubstituteNum(antilogarithm, patchPath, patchKey) == 0 || judgeLogSubstituteNum(base, patchPath, patchKey) == 0) return Double.MIN_VALUE; else return Math.log10(judgeLogSubstituteNum(antilogarithm, patchPath, patchKey)) / Math.log10(judgeLogSubstituteNum(base, patchPath, patchKey)); } private static double judgeLogSubstituteNum(double num, String patchPath, String patchKey){ if(!(checkPatch(patchPath, patchKey) && !"".equals(iniReader(patchPath, patchKey))) && num < 0) return 0; else if(checkPatch(patchPath, patchKey) && !"".equals(iniReader(patchPath, patchKey))) { String numFromPatch = iniReader(patchPath, patchKey); try{ return Double.parseDouble(numFromPatch); }catch(NumberFormatException e){ return 0; } } else if(num == 0) return Math.E; else return num; } public static boolean checkPatch(String patchPath, String patchKey){ return !"".equals(iniReader(patchPath, patchKey)); } public static String iniReader(String FilePath, String key){ //iniファイルの読み込み(暫定版) try{ Properties prop = new Properties(); prop.load(new FileInputStream(FilePath)); return prop.getProperty(key); }catch(IOException e){ return ""; } } }
パッチ使わない人(計算機を作る人等)はこれだけで十分です。
public class Sample{ public static double log(double base, double antilogarithm){ if(base < 0 || antilogarithm < 0) return Double.MIN_VALUE; else{ if(base == 0) base = Math.E; if(antilogarithm == 0) antilogarithm = Math.E; return Math.log10(antilogarithm) / Math.log10(base); } } }
import java.io.*; import java.util.*; public class Sample { public static String getCalendar(){ //エラーログを作成するための日付を取得 Calendar calendar = Calendar.getInstance(); int integerYear = calendar.get(Calendar.YEAR) - (((int)(calendar.get(Calendar.YEAR) / 100)) * 100); String year = changeTwoDigits(integerYear); String month = changeTwoDigits(calendar.get(Calendar.MONTH)); String day = changeTwoDigits(calendar.get(Calendar.DATE)); String hour = changeTwoDigits(calendar.get(Calendar.HOUR_OF_DAY)); String minute = changeTwoDigits(calendar.get(Calendar.MINUTE)); String second = changeTwoDigits(calendar.get(Calendar.SECOND)); return year + month + day + "_" + hour + minute + second; } public static String changeTwoDigits(int num){ //getCalendar()で返す各日付を2桁に直す if(num < 10) return "0" + String.valueOf(num); else return String.valueOf(num); } public static boolean errorLogFile(String message){ //エラーログを作成する try{ String FilePath = "./log/" + getCalendar() + ".log"; File file = new File(FilePath); PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); pw.println(message); return true; }catch(IOException e){ return false; } } }
import java.io.*; import javax.xml.parsers.*; import org.w3c.dom.*; import org.xml.sax.SAXException; public class Sample{ public static String XMLReader(String filePath, String dataType, int number, String key){ String data = "NoData"; try{ for(int i = 0; firstRoopCondition(filePath, data, dataType, i, number); i++) { for (int j=0; secondRoopCondition(filePath, data, key, i, j); j++) { data = children(filePath).item(i).getChildNodes().item(j).getTextContent(); } } if("NoData".equals(data)) System.out.println("XMLのデータを読み込めませんでした。"); }catch(IOException | SAXException | ParserConfigurationException e){ System.out.println("XMLの読み込みでエラーが発生しました。"); } return data; } private static NodeList children(String filePath) throws IOException, SAXException, ParserConfigurationException{ return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(filePath).getDocumentElement().getChildNodes(); } private static boolean firstRoopCondition(String filePath, String data, String dataType, int num1, int num2) throws IOException, SAXException, ParserConfigurationException{ return (num1 < children(filePath).getLength() && "NoData".equals(data) && children(filePath).item(num1).getNodeType() == Node.ELEMENT_NODE && element(filePath, num1).getNodeName().equals(dataType) && element(filePath, num1).getAttribute("id").equals(String.valueOf(num2))); } private static boolean secondRoopCondition(String filePath, String data, String key, int num1, int num2) throws IOException, SAXException, ParserConfigurationException{ return (num2 < children(filePath).item(num1).getChildNodes().getLength() && "NoData".equals(data) && children(filePath).item(num1).getChildNodes().item(num2).getNodeType() == Node.ELEMENT_NODE && children(filePath).item(num1).getChildNodes().item(num2).getNodeName().equals(key)); } private static Element element(String filePath, int num) throws IOException, SAXException, ParserConfigurationException { return (Element)children(filePath).item(num); } }
このメソッドは下のようなXMLを読み込むような構造になっています。
別にdatabookとかsavedataとかでなくても読めるように引数にこのタグの名前を指定するようにしてます。
山田太郎 280 35
参考までにこのXMLを読み込むためにはどのようにリーダーを読めばいいかを書いておきます。
public class Sample{ public static void main(String[] args){ if(!"NoData".equals(XMLReader("C:/test.xml", "savedata", 1, "name"))) System.out.println((XMLReader("C:/test.xml", "savedata", 1, "name"))); } }
実はここに書いたXMLリーダーは、読み込みエラーを起こした時、"NoData"という文字列を返すように作ってあるのでif文で"NoData"を返してきたら表示しないようにしてあります。
え?エラー発生した時のメッセージは表示しないのかって?XMLリーダーメソッドの最後、ちゃんと見て。
"NoData"の時とIOException、SAXException、ParserConfigurationExceptionが飛ばされた時はエラーメッセージが出るようになってますよ。
あと、余談ですがここでは絶対パスで書いてますが、ゲームとかでこれ使うなら絶対パスだと不都合が起きるので、アプリケーションと同じディレクトリにxmlを保存してるなら"./test.xml"、アプリケーションと同じディレクトリにあるフォルダの中に保存してるなら"./test/test.xml"のようにしたほうがいいと思います。
あ…そうだ。言い忘れてたんですが、
もう1階層XMLを深くしたり、逆に浅くしたりすることも可能ですが、そこは自分で考えてカスタマイズしてください。
まあ遊びなので使いたい人はどうぞお好きにお使いください。
あと、getCalendarメソッドなんですが、最悪2行にできないことはないんですが、やるとウインドウをはみ出すのでバラバラにしました。まとめたい人は勝手にまとめてください。