SQLiteDataBaseが上手く使えません。
解決済
回答 3
投稿
- 評価
- クリップ 0
- VIEW 4,110
public class MainActivity extends AppCompatActivity {
/** Called when the activity is first created. */
private static MySQLiteHelper helper;
private SQLiteDatabase db;
private Context context;
private ListView listView;
private String ssid;
private int date ;
private int level;
private int freq;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
helper = new MySQLiteHelper(this);
db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(helper.COLUM_SSID,ssid);
values.put(helper.COLUM_FREQ,freq);
values.put(helper.COLUM_LEVEL,level);
values.put(helper.COLUM_DATE,date); //時刻を入れたい ButtonContent.time();
db.insert(helper.TABLE_NAME,null,values);
listView = (ListView)findViewById(R.id.listView); //Listviewにリンク
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter); //ListViewに渡す
final WifiManager manager = (WifiManager) getSystemService(WIFI_SERVICE);
/* メインスレッド以外から UI を触るための 仲介役 */
final Handler handler = new Handler();
/* 一定時間ごとに 実行してほしいことを run() の中に書く */
final TimerTask timerTask = new TimerTask(){
@Override
public void run() {
if (manager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// APをスキャン
manager.startScan();
// スキャン結果を取得
List<ScanResult> apList = manager.getScanResults();
final String[] aps = new String[apList.size()];
for (int i = 0; i < apList.size(); i++) {
aps[i] ="SSID" + apList.get(i).SSID + "\n" + apList.get(i).frequency + "MHz"
+ apList.get(i).level + "dBm" + ButtonContent.time();
}
/* スキャンが終わったら表示する */
/* TimerTask は メインスレッドで実行できないので、UIを触りたいのでHandlerを使う */
handler.post(new Runnable() {
@Override
public void run() {
/* リストをきれいさっぱり空にして */
adapter.clear();
/* 新しい情報を全部足して */
adapter.addAll(aps);
/* 中身変わったと教える */
adapter.notifyDataSetChanged();
}
});
}
}
};
/* TimerTaskは UI スレッドで実行できないので 別なスレッドの中で実行する */
new Thread(new Runnable() {
@Override
public void run() {
new Timer(true).schedule(timerTask, 0, 500);
}
}).start();
}
@Override
public void onDestroy(){
super.onDestroy();
db.close();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
SQLiteOpenHelperクラスです。↓
public class MySQLiteHelper extends SQLiteOpenHelper implements BaseColumns {
private static final int DATABASE_VIRSION = 1;
private static final String DATABASE_NAME = "sqlite_wifi.db";
//テーブル名やカラムはデータベースのアクセスに必要なのでpublic
public static final String TABLE_NAME = "mytable";
public static final String COLUM_ID = "_id";
public static final String COLUM_SSID = "ssid";
public static final String COLUM_FREQ = "freq"; //カラムの割り振り、ID カラムIDには_idを使うことが必須事項
public static final String COLUM_LEVEL = "level";
public static final String COLUM_DATE = "date" ;
private static final String MY_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + COLUM_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
COLUM_SSID + " TEXT NOT NULL," + COLUM_FREQ + " INTEGER NOT NULL," + COLUM_LEVEL + " INTEGER NOT NULL," + COLUM_DATE + "INTEGER NOT NULL );"; //クエリ分の作成処理
private static final String DROP_TABLE = "drop table " + TABLE_NAME; //tableの削除
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VIRSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(MY_CREATE_TABLE); //tableを作成するSQLを実行
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DROP_TABLE);
onCreate(db); //tableを削除して再生成
}
}
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
SQL文を正しく作ること.の2つで解決するはずです.
参考までに,write()メソッドを呼ぶことで,データを追加できるサンプル.
Activityなどから,必要に応じて write(context, scanResultList, date);を呼ぶことでデータベースを更新できます.
/* BaseColumns を使うなら これ自身に COLUM_ID を 定義する必要はありませんね */
/* 私的には使う必要性を感じません */
class MySQLiteHelper extends SQLiteOpenHelper /* implements BaseColumns */ {
/* VIRSION -> VERSION */
/* Database の構成を変更したら この値を変更する */
/* e.g.) column 名を書き変えた, column を増やした */
/* 通常 インクリメント (+1) するだけでよい*/
private static final int DATABASE_VIRSION = 2;
private static final String DATABASE_NAME = "sqlite_wifi.db";
//テーブル名やカラムはデータベースのアクセスに必要なのでpublic
/* COLUM -> COLUMN */
public static final String TABLE_NAME = "mytable";
public static final String COLUM_ID = "_id";
public static final String COLUM_SSID = "ssid";
public static final String COLUM_FREQ = "freq"; //カラムの割り振り、ID カラムIDには_idを使うことが必須事項
public static final String COLUM_LEVEL = "level";
public static final String COLUM_DATE = "date";
private static final String MY_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" +
COLUM_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
COLUM_SSID + " TEXT NOT NULL," +
COLUM_FREQ + " INTEGER NOT NULL," +
COLUM_LEVEL + " INTEGER NOT NULL," +
COLUM_DATE + " INTEGER NOT NULL );"; /* スペース抜けの修正 */
private static final String DROP_TABLE = "drop table " + TABLE_NAME; //tableの削除
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VIRSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(MY_CREATE_TABLE); //tableを作成するSQLを実行
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DROP_TABLE);
onCreate(db); //tableを削除して再生成
}
/* 汎用メソッド追加 */
/* date の型を知らないので long にしてあります */
public static void write(Context context, List<ScanResult> resultList, long date) {
final SQLiteDatabase db = new MySQLiteHelper(context).getWritableDatabase();
try {
for (ScanResult scanResult : resultList) {
ContentValues values = new ContentValues();
values.put(COLUM_SSID, scanResult.SSID);
values.put(COLUM_FREQ, scanResult.frequency);
values.put(COLUM_LEVEL, scanResult.level);
values.put(COLUM_DATE, date); //時刻を入れたい ButtonContent.time();
/* insert にすべきか update にすべきかは 考え所ですね */
db.insert(TABLE_NAME, null, values);
}
} finally {
db.close();
}
}
/* データベースからデータを取ってくる */
public static List<MyResult> read(Context context) {
/* Database を読み取り専用で開く */
final SQLiteDatabase db = new MySQLiteHelper(context).getReadableDatabase();
try {
/* rawQuery() にてデータベースから指定アイテムを取ってくる */
/* ? をわざわざ使わなくてもいいと考えるだろうが SQL インジェクション対策になる */
/* ダメな例) db.rawQuery("SELECT * FROM " + TABLE_NAME, new String[0]) */
/* もし TABLE_NAME に悪質なSQL文が入っていたら...? */
final Cursor cursor = db.rawQuery("SELECT * FROM ?", new String[]{TABLE_NAME});
try {
/* columnIndex は データベースを作った際に */
/* 実行した CREATE TABLE 句の 順番で割り振られている */
/* 手動で 0, 1, 2, 3 と振ることもできるが column 変更への耐性がなくなる */
/* 良くない例) int ssidIndex = 1; int freqIndex = 2; ... */
final int ssidIndex = cursor.getColumnIndex(COLUM_SSID);
final int freqIndex = cursor.getColumnIndex(COLUM_FREQ);
final int levelIndex = cursor.getColumnIndex(COLUM_LEVEL);
final int dateIndex = cursor.getColumnIndex(COLUM_DATE);
/* ArrayList のコンストラクタは 引数を取らなくてもよいが */
/* サイズがわかっている場合には 指定した方が 高速である */
/* ScanResult クラス は自分では扱えないため 自前で作ってみた */
final List<MyResult> scanResults = new ArrayList<>(cursor.getCount());
/* 最後まで cursor を動かす */
/* Cursor は結構特殊な 振る舞いをします この書き方は覚えておきましょう */
while (cursor.moveToNext()) {
final String ssid = cursor.getString(ssidIndex);
final int frequency = cursor.getInt(freqIndex);
final int level = cursor.getInt(levelIndex);
final long date = cursor.getInt(dateIndex);
scanResults.add(new MyResult(ssid, frequency, level, date));
}
return scanResults;
} finally {
/* 忘れずに close させるために try-finally を使用 */
cursor.close();
}
} finally {
db.close();
}
}
}
class MyResult {
public final String ssid;
public final int frequency;
public final int level;
public final long date;
public MyResult(String ssid, int frequency, int level, long date) {
this.ssid = ssid;
this.frequency = frequency;
this.level = level;
this.date = date;
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
for (int i = 0; i < apList.size(); i++) {}の中で
ContentValues values = new ContentValues();
values.put(helper.COLUM_SSID,ssid);
values.put(helper.COLUM_FREQ,freq);
values.put(helper.COLUM_LEVEL,level);
values.put(helper.COLUM_DATE,date); //時刻を入れたい ButtonContent.time();
db.insert(helper.TABLE_NAME,null,values);
上記の処理を行えばいいでしょう。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.09%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2015/10/29 03:03
先ほどのコードはおっしゃるとおりVERSIONの値を変えたら動くようになりました。以後よく覚えておきます。
えっと、こちらのコードは
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
helper = new MySQLiteHelper(this);
db = helper.getWritableDatabase();
このときの`db = helper.getWritableDatabase();`をwrite()メソッドに置き換えるという考えで正しいでしょうか?
2015/10/29 03:05
2015/10/29 03:13 編集
public class MainActivity extends AppCompatActivity {
/** Called when the activity is first created. */
private static MySQLiteHelper helper;
private SQLiteDatabase db;
private Context context;
private ListView listView;
private String ssid;
private int date ;
private int level;
private int freq;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView); //Listviewにリンク
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter); //ListViewに渡す
final WifiManager manager = (WifiManager) getSystemService(WIFI_SERVICE);
/* メインスレッド以外から UI を触るための 仲介役 */
final Handler handler = new Handler();
/* 一定時間ごとに 実行してほしいことを run() の中に書く */
final TimerTask timerTask = new TimerTask(){
@Override
public void run() {
if (manager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// APをスキャン
manager.startScan();
// スキャン結果を取得
List<ScanResult> apList = manager.getScanResults();
/*************** 例えばこの辺で データベースに書き込んでみる ***************/
MySQLiteHelper.write(MainActivity.this, apList, ButtonContent.time());
final String[] aps = new String[apList.size()];
for (int i = 0; i < apList.size(); i++) {
aps[i] ="SSID" + apList.get(i).SSID + "\n" + apList.get(i).frequency + "MHz"
+ apList.get(i).level + "dBm" + ButtonContent.time();
}
/* スキャンが終わったら表示する */
/* TimerTask は メインスレッドで実行できないので、UIを触りたいのでHandlerを使う */
handler.post(new Runnable() {
@Override
public void run() {
/* リストをきれいさっぱり空にして */
adapter.clear();
/* 新しい情報を全部足して */
adapter.addAll(aps);
/* 中身変わったと教える */
adapter.notifyDataSetChanged();
}
});
}
}
};
/* TimerTaskは UI スレッドで実行できないので 別なスレッドの中で実行する */
new Thread(new Runnable() {
@Override
public void run() {
new Timer(true).schedule(timerTask, 0, 500);
}
}).start();
}
}
2015/10/29 03:36
格納された値を確認するにはadb shellを使えば良いのでしょうか?
2015/10/29 03:41
格納された値の確認方法はいくつかありますが,上のソースコードならば,Activity内で,MySQLiteHelper.read()メソッドを呼べば,値一覧をListで取り出します.
Toastを使えば,実機やエミュレータ上で見れるメッセージを出すことができます.LogやSystem.outを用いれば,デバッグ用のLogCat上で見ることができます.
2015/10/29 03:59
私の知識が全く足りず、質問攻めになってしまい大変申し訳ないです。
また別の話になってしまうのですが、このデータベースはアプリを終了したらそれまでの値は消えてしまうのでしょうか?
2015/10/29 04:04
データベースはアプリを終了しても消えません.データを一度入力したら,次回以降もそのデータは利用できます.アプリ内でそのデータを消す,アプリ外でデータベースを消す,アプリをアンインストールする,携帯を初期化するなどであれば,データは消えてしまいます.
2015/10/29 04:24
MySQLiteHelper.read()メソッドで取り出した値を画面上で一覧として表示させるにはListViewを利用すれば良いのでしょうか?
2015/10/29 04:33
何を利用してもよいでしょうが,ListViewは一案として良いでしょう.その他,TextViewに入れてしまうこともできるでしょう.例えば,↓のような感じです.
/* StringBuilder.append() は 文字列同士の+演算より高速なのでこちらを利用 */
StringBuilder stringBuilder = new StringBuilder();
/* 改行コードを手に入れておく */
String BR = System.getProperty("line.separator");
for (MyResult result :MySQLiteHelper.read(context)) {
stringBuilder.append("SSID: ");
stringBuilder.append(result.ssid);
stringBuilder.append(" DATE: ");
stringBuilder.append(result.date);
stringBuilder.append(BR);
}
/* textView にセット */
textView.setText(stringBuilder);
2015/10/29 04:55
そういう方法もあるんですね、そこも勉強します!
あともう一つ質問なのですが、このプログラムの取得データをexcelファイル、csvファイルにすることは可能なのでしょうか?
2015/10/29 05:11 編集
Activityに↓のようなコードを追記して,呼び出すだけ.csvはカンマ(,)と改行のみで構成されるので簡単に作ることができます.
private void csvOutput(String filename, List<MyResult> resultList) {
final String br = System.getProperty("line.separator");
OutputStream out = null;
try {
out = openFileOutput(filename, MODE_PRIVATE);
try {
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
writer.append("ssid,frequency,level,date");
for (MyResult result : resultList) {
writer.append(result.ssid);
writer.append(',');
writer.append(result.frequency);
writer.append(',');
writer.append(result.level);
writer.append(',');
writer.append(result.date);
writer.append(br);
}
} finally {
writer.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} finally {
if (out != null)
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2015/10/29 05:22
これによって値が格納されたファイルがcsvになるということでしょうか?
すみません、流れが分からなくて,,,
2015/10/29 05:26
このcsvOutputを呼ぶと,List<MyResult>をcsvの形式で,filenameというファイル名で保存します.
2015/10/29 05:39
filenameが作成される場所は/data/data/{app dir}/filesになるんでしょうか。
2015/10/29 05:45
そうです.基本的にはそのアプリ用の領域にのみ読み書きの権限が与えられるので,そこに作成します.filenameには"test.csv"などのみでパスは含めなくてよかったはずです.
Androidは主に,イベントによって動作します.クリックイベントや時間イベントなどに,リスナーをくっつけて,そのタイミングでこれをさせようとかっていうのを書いていきます.あなたが必要だと思うイベントを拾ったときに,呼び出しましょう.(Buttonクリック時など)
2015/10/29 06:17
先述のtextviewに表示するコードなんですけど
public void run() {
if (manager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// APをスキャン
manager.startScan();
// スキャン結果を取得
List<ScanResult> apList = manager.getScanResults();
MySQLiteHelper.write(MainActivity.this, apList, ButtonContent.time());
/* StringBuilder.append() は 文字列同士の+演算より高速なのでこちらを利用 */
StringBuilder stringBuilder = new StringBuilder();
/* 改行コードを手に入れておく */
String BR = System.getProperty("line.separator");
for (MyResult result :MySQLiteHelper.read(context)) {
textView = (TextView) findViewById(R.id.database_text);
stringBuilder.append("SSID: ");
stringBuilder.append(result.ssid);
stringBuilder.append(" DATE: ");
stringBuilder.append(result.date);
stringBuilder.append(BR);
}
run()メソッドで繰り返し行うと考えてここに記述したのですがエラーになってしまいました。データベースに同時に複数アクセスしたのが問題でしょうか?
10-29 06:13:25.681 31329-31345/com.example.sakamotoryusei.wifidatabase E/AndroidRuntime﹕ FATAL EXCEPTION: Timer-0
java.lang.NullPointerException
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:188)
at com.example.sakamotoryusei.wifidatabase.MySQLiteHelper.read(MySQLiteHelper.java:81)
at com.example.sakamotoryusei.wifidatabase.MainActivity$1.run(MainActivity.java:61)
at java.util.Timer$TimerImpl.run(Timer.java:284)
2015/10/29 06:28
getDatabaseLockedは,DatabaseHelperに渡したContextがnullのとき吐かれるようです.
あなたのソースコードでは,MainActivityで private Context context;という記述がありますね.このcontextは何も代入していない以上,nullを示しています.nullを渡しても仕方ありません.MainActivityの実体(MainActivity.this)を渡してあげれば解決します.
2015/10/29 15:21
↑こういうことですよね?今度はまた違うエラーが出てしまったのですがこれは何がいけないのでしょうか?
10-29 15:09:35.820 6045-6078/com.example.sakamotoryusei.wifidatabase E/SQ
LiteLog﹕ (1) near "?": syntax error
10-29 15:09:35.820 6045-6078/com.example.sakamotoryusei.wifidatabase W/dalvikvm﹕ threadid=12: thread exiting with uncaught exception (group=0x41cea438)
10-29 15:09:35.820 6045-6078/com.example.sakamotoryusei.wifidatabase E/AndroidRuntime﹕ FATAL EXCEPTION: Timer-0
android.database.sqlite.SQLiteException: near "?": syntax error (code 1): , while compiling: SELECT * FROM ?
2015/10/29 16:17
final Cursor cursor = db.rawQuery("SELECT * FROM ?", new String[]{TABLE_NAME});
↓こちらが正しいです.
final Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
これでそのエラーは解決します.
2015/10/29 17:45
ひとまず自分でいろいろ試してみようと思います。
また何かあったら質問させていただいてもよろしいでしょうか?
2015/10/29 17:46 編集
ここでも,新規の質問でもきっと誰かが答えてくれますよ.
2015/11/06 00:21
質問なのですが、android端末で取得したデータをPCのファイルに常に転送できるようにするということは可能なのでしょうか?
2015/11/06 02:53 編集
もちろん可能です.低レベルの実装ならば,ソケット通信を利用することができます.
PC: ポートを解放しておく->
Android: カメラを使って撮影(その他のデータ取得)->
Android: データをシリアライズ(転送しやすいようにbyte列に直す)->
Android: PCのIPアドレス:ポートに対してデータを送信->
PC: データを受信->
PC: データをデシリアライズ(byte列から元の形に戻す)->
PC: データを見る
など.
2015/11/06 04:40
考えてることといたしましては、このwifiのAPについてのデータをUDP/TCP通信などをつかってデータベースではなくprintlnなどを利用して簡単にテキストデータを出力できるようにできないかなと考えていまして。
まとめるとPC上でファイルとして閲覧できるようにデータを取れるようしたいと考えています。
2015/11/06 04:47
PCで,アクセスポイント情報受信,ファイルに保存ってことですね.
可能です.Androidの障壁を除けば,できないことはありません!
2015/11/06 05:34
調べてみます!
2015/11/06 17:32