JSONデータの読み取り(Java)
(ブログ記事の一覧は「こちら」)
JSON(JavaScript Object Notation)とは、データを定義するためのフォーマットの1つです。同じような考えのフォーマットとしてXMLやCSVがあります。最近では、Web APIの実行結果でよく使われるフォーマットとしてJSONが多く見られます。
JSONは、その名前の通りJavaScriptでデータをパース(解析)するのに適していますが、他のプログラム言語でもパースするためのメソッドは用意されています。Javaでは外部ライブラリを使用することでパースが可能です。(Androidではパースするための関数が最初から用意されています)
ここでは、JavaでJSONデータをパースする方法を学びましょう。
JSONデータが書かれたテキストファイルを用意して、テキストファイルからJSONデータを読み込むサンプルを作成します。
テキストファイルの読み込み
まず準備として、テキストファイルからテキストデータを読み込むプログラムを作成します。
以下のテキストデータが入力されたファイル「json_test.txt」を作成して(文字コードはUTF-8で保存)、作成したプロジェクトフォルダ(「.project」ファイルがある場所)にコピーします。
{ "user" : { "id" : "A1234567", "name" : "Taro Yamada" } }
ちなみに、上記はシンプルなJSONデータのサンプルになります。
キー"id"に対する値は "A1234567"、という関係になります。
テキストファイル「json_test.txt」を読み込み、コンソールに出力するサンプルプログラムです。
String strFileName = "json_test.txt"; FileReader fr = null; BufferedReader br = null; try{ // ファイル読み込み File file = new File(strFileName); fr = new FileReader(file); br = new BufferedReader(fr); // テキストを取得 String strLine; StringBuilder sbSentence = new StringBuilder(); while ((strLine = br.readLine()) != null) { sbSentence.append(strLine); } // JSONデータ(後で、この部分を修正する) System.out.println(sbSentence.toString()); } catch (FileNotFoundException e) { System.out.println(e); } catch (IOException e) { System.out.println(e); } finally { try { if (br != null) { br.close(); } if (fr != null) { fr.close(); } } catch (IOException e) { e.printStackTrace(); } }
JSONデータ形式を含む全ての文字列が取得できたことを確認してください。
次に、JSONデータをパースするプログラムに書き換える方法について確認しましょう。
JSONライブラリの登録
以下のリンクからJSONライブラリを取得します。
https://mvnrepository.com/artifact/org.json/json
リストの「Version」欄から最新のものをクリックします。移動したページで、表の「Files」にある「bundle」をクリックすると、「json-xxxxxxxx.jar」がダウンロードされます。
次に、下図のように、プロジェクト名を右クリックして、「ビルド・パス」→「外部アーカイブの追加」を選択して、「json-xxxxxxxx.jar」を登録します。
下図のように追加されたことを確認してください。
これで準備ができました。
JSONデータの読み込み
例1
先ほどの「json_test.txt」のJSONデータについて、各キーの値を取得するためのプログラムです。先ほどのテキストファイルの読み込みのサンプルに追記します。
// JSONオブジェクトのインスタンス作成 JSONObject jsonObj = new JSONObject(sbSentence.toString()); // キー"user"の値(JSONオブジェクト)をパース JSONObject item = jsonObj.getJSONObject("user"); // 各キーの値を出力 System.out.println(item.get("id")); System.out.println(item.get("name"));
以下のimport文が必要になります。(自動で追加されます)
import org.json.JSONObject;
例2
次に、配列を含むJSONデータについても試してみます。 以下のJSONデータを「json_test2.txt」としてファイルに保存して、プロジェクトフォルダに追加してください。
{ "users" : [ { "id" : "A1234567", "name" : "Taro Yamada" }, { "id" : "B1234567", "name" : "Jiro Tanaka" }, { "id" : "C1234567", "name" : "Ichiro Suzuki" } ] }
各キーの値を取得するためのプログラムです。例1の部分から書き換えます。
// JSONオブジェクトのインスタンス作成 JSONObject jsonObj = new JSONObject(sbSentence.toString()); // キー"users"の値(JSON配列オブジェクト)をパース JSONArray items = jsonObj.getJSONArray("users"); for (int i = 0; i < items.length(); i++) { // JSONオブジェクトをパース JSONObject item = items.getJSONObject(i); // 各キーの値を出力 System.out.println(item.get("id")); System.out.println(item.get("name")); }
以下のimport文が必要になります。(自動で追加されます)
import org.json.JSONArray;
課題
以下のJSONデータについて、キー"id", "name"の各値を出力するプログラムを作成してください。
{ "user" : { "info" : { "id" : "A1234567", "name" : "Taro Yamada" } } }
文字列の追加(Java)
(ブログ記事の一覧は「こちら」)
(今回は、Javaのみの内容になります)
前回は、文字列を分割することについて学びました。今回は、その逆で、文字列を追加(連結)する方法について確認していきます。
Stringクラスを使って文字列を連結する場合、+演算子を使用します。
String str = "あいう" + "えお" + "かきく" + "けこ";
特に問題の無いように見えますが、実はJavaプログラミングとしては適切な書き方とは言えません。
実は、+演算子で文字列を連結するたびに、新たにStringクラスを生成する、という処理を行っています。その結果、メモリの使用量が増えることで負荷が大きくなり、処理が遅くなってしまうことになります。
(上記の例くらいであれば、影響はほとんどありませんが^^;)
これらの問題を解決するための方法として、StringBuilderクラスというものを使用します。(似た内容でStringBufferクラスというものもありますが、今回は説明は行いません)
以下は、StringBuilderクラスを使用した、文字列を追加(連結)する方法のサンプルです。
Javaでの記述例
package com.test; public class TestApp { public static void main(String[] args) { // 配列 String[] arFruit = {"りんご", "みかん", "メロン", "バナナ"}; // 文字列の連結 StringBuilder sbFruit = new StringBuilder(); sbFruit.append(arFruit[0]); sbFruit.append(arFruit[1]); sbFruit.append(arFruit[2]); sbFruit.append(arFruit[3]); System.out.println(sbFruit.toString()); } }
出力結果
りんごみかんメロンバナナ
課題
以下のカンマ区切りの文字列に対して、カンマを取り除いた文字列に変換して、その結果を出力してください。
String str = "りんご,みかん,メロン,バナナ";
(方法の1つとして、(前回学んだ)文字列の分割と、文字列の追加を行うことで実現できます。)
package com.test; public class TestApp { public static void main(String[] args) { String str = "りんご,みかん,メロン,バナナ"; // TODO: 文字列の分割 // TODO: 文字列の追加 // 分割、追加した文字列を出力 System.out.println(sbFruit.toString()); } }
出力結果
りんごみかんメロンバナナ
文字列の分割(Java, JavaScript)
(ブログ記事の一覧は「こちら」)
いろいろなアプリを作成するときに必要なスキルの1つに、文字列の処理があります。
ここでは、文字列を指定の文字で分割する方法を練習していきましょう。
文字列の分割を行う方法は、Java, JavaScriptともにsplit関数が用意されています。(同じ名前の関数ですが、使い方は異なります)
以下にサンプルのプログラムを用意しました。カンマ区切りの文字列に対して、ご自身でsplit関数を調べてみて、カンマで文字列を分割するプログラムとなるように完成させてください。
(Java, JavaScriptのどちらか一方を選択してもらって構いません)
Javaでの記述例
package com.test; public class TestApp { public static void main(String[] args) { String str = "りんご,みかん,メロン,バナナ"; // TODO: 文字列の分割 // 分割した文字列を別々に出力 System.out.println(”ここに分割した文字列の変数を入れる”); } }
出力結果
りんご みかん メロン バナナ
JavaScriptでの記述例
index.html
<html> <head> <meta charset="UTF-8"> <script src="main.js"></script> </head> <body> <input type="button" value="文字列の分割" onclick="divText()"> <div id="hoge"></div> </body> </html>
main.js
function divText() { var str = "りんご,みかん,メロン,バナナ"; // TODO: 文字列の分割 document.getElementById("hoge").innerHTML = (分割した文字列を改行で表示); }
出力結果
りんご みかん メロン バナナ
課題
以下の文字列に対して、「出力結果」のように出力させるプログラムを完成させなさい。
りんご=150円,みかん=80円,メロン=800円,バナナ=250円
出力結果
りんごは150円です。 みかんは80円です。 メロンは800円です。 バナナは250円です。
(ヒント)
まず、文字列全体をカンマ「,」で分割して、さらに区切ったそれぞれの文字列に対して「=」で分割します。
あとは、出力結果に合わせて必要な文字を追加することで完成します。
2020年1月 実力テスト(C言語)
(ブログ記事の一覧は「こちら」)
C言語に関する、以下の問いについて回答しなさい。
問1
以下のC言語のプログラムがあるとします。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(void) 5 { 6 int i = 0; 7 8 int array[100]; 9 for (i = 0; i < 100; i++) { 10 array[i] = 1000 + i; 11 } 12 13 // 動的なメモリ領域の確保 14 15 16 for (i = 0; i < 100; i++) { 17 *(p + i) = array[i]; 18 } 19 20 for (i = 0; i < 100; i++) { 21 printf("Pointer: %d\n", *(p + i)); 22 } 23 24 // 動的なメモリ領域の解放 25 26 27 return 0; 28 }
問1-1(各10点)
14行目、25行目に入る適切なプログラムを書きなさい。
問1-2(10点)
(14行目に入るプログラムは正しく書かれていたとして)25行目に入るプログラムが書かれなかった場合、どのような問題点があると考えられますか?簡単に説明しなさい。
問2
C言語の文字列の処理に関する標準関数について、以下の問いを回答しなさい。
問2-1(10点)
以下のプログラム内の、if文の中(「問2-1」の部分)には何が入るか書きなさい。
#include <string.h> #include <stdio.h> int main(void) { char str1[] = "ABC"; char str2[] = "123"; if ( /* 問2-1 */ ) { printf("str1とstr2は同じ\n"); } else { printf("str1とstr2は異なる\n"); } return 0; }
問2-2(10点)
以下のプログラムを実行したときに出力される結果を書きなさい。
#include <string.h> #include <stdio.h> int main(void) { char str1[] = "123"; char str2[] = "ABC"; strcat(str1, str2); printf("%s\n", str1); return 0; }
問2-3(各5点)
以下のプログラム内の「①」「②」の部分には何が入るか書きなさい。
#include <string.h> #include <stdio.h> int main(void) { char str1[] = "123"; char str2[] = "ABCDEF"; printf("str1の長さは %d, str2の長さは %d\n", /* ① */, /* ② */); return 0; }
問3
C言語のファイル処理(テキストファイルの編集)に関する以下の問いについて回答しなさい。
問3-1(20点)
以下は、テキストファイルの読み込みについてのプログラム例です。
#include <stdio.h> int main(void) { FILE *fp; fp = fopen("test_r.txt", "r"); if (fp == NULL) { return -1; // <- ここを通る場合 } int chr; while ((chr = fgetc(fp)) != EOF) { printf("%c", chr); } fclose(fp); return 0; }
プログラム内の「ここを通る場合」の箇所を通るのはどのような場合に起こるでしょうか? わかりやすく説明しなさい。
(プログラムは最後まで処理されずに終了することになります。)
問3-2(20点)
以下は、テキストファイルの書き込みについてのプログラム例です。
#include <stdio.h> int main(void) { FILE *fp; fp = fopen("test_w.txt", "w"); if (fp == NULL) { return -1; } fclose(fp); return 0; }
上のプログラムに追記して、以下のテキストを書き込んだファイルを作成できるようにしたいです。追記が必要なコードを書きなさい。
おはようございます。 今日もがんばりましょう!
2020年1月 実力テスト(JavaScript, jQuery)
(ブログ記事の一覧は「こちら」)
問1
JavaScript, jQueryに関する、以下の問いについて回答しなさい。
問1-1(20点)
以下のindex.htmlがあるとします。
<html> <head> <meta charset="UTF-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="main.js"></script> </head> <body> <div id="message">こんにちは</div> <input type="button" id="editText" value="テキスト変更"> </body> </html>
ボタンをクリックすると、「こんにちは」の表示が以下のテキストに切り替わるような、main.jsのプログラムを作成しなさい。
ありがとう
問1-2(20点)
以下のindex.htmlがあるとします。
<html> <head> <meta charset="UTF-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="main.js"></script> </head> <body> <div id="hoge1" class="message"></div> <div id="hoge2" class="message"></div> <input type="button" id="editTextClass" value="テキスト変更"> </body> </html>
ボタンをクリックすると、以下のテキストが表示されるような、main.jsのプログラムを作成しなさい。
ありがとう ありがとう
問1-3(20点)
以下のindex.htmlがあるとします。
また、index.htmlと同じディレクトリに画像ファイル「icon.png」があるとします。
<html> <head> <meta charset="UTF-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="main.js"></script> </head> <body> <img id="image"/> <input type="button" id="showImage" value="画像を表示"> </body> </html>
ボタンをクリックすると、画像ファイル「icon.png」が表示されるような、main.jsのプログラムを作成しなさい。
問2
OpenWeatherMap APIを用いた、現在の天気情報をWebブラウザに表示する方法について考えます。
東京(Tokyo)の現在の天気情報を表示する例は以下になります。
index.html
<html> <head> <meta charset="UTF-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="main.js"></script> </head> <body> <h1>OpenWeatherMap API</h1> <p>場所:<span id="place"></span></p> <p>天気:<span id="weather"></span></p> <p><span id="description"></span></p> <p><img id="icon"></p> <p>気温:<span id="temp"></span>℃</p> <p>湿度:<span id="humidity"></span>%</p> </body> </html>
main.js
$.ajax({ type: "GET", url: "https://api.openweathermap.org/data/2.5/weather?q=Tokyo&appid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }).done(function(data) { $("#place").html(data.name); var img = $("#icon"); img.attr("src", "https://openweathermap.org/img/wn/" + data.weather[0].icon + "@2x.png"); img.attr("alt", data.weather[0].main); $("#weather").html(data.weather[0].main); $("#description").html(data.weather[0].description); $("#temp").html(Math.floor((data.main.temp - 273.15) * 100) / 100); $("#humidity").html(data.main.humidity); }).fail(function(data) { alert("error"); });
上記に関する、以下の問いについて回答しなさい。
問2-1(各10点)
「気圧」の項目を追加したい場合、index.htmlとmain.jsに追加するコードを書きなさい。
(気圧の単位は「hPa(ヘクトパスカル)」です。)
問2-2(20点)
index.htmlにボタンを3個追加して、クリックすると「Tokyo」「Osaka」「XXX(あなたの生まれた場所)」に天気情報の表示が切り替わるような仕組みになるように改造したい場合について考えます。
index.htmlは以下のようにボタンを追加していく場合、main.jsはどのように修正する必要がありますか?(他の学生に説明しても理解できるような)文章とコードで、わかりやすく説明しなさい。
... <p><input type="button" value="Tokyo" onclick="callOpenWeatherMap('Tokyo')"></p> ...
ファイル処理(画像ファイルの編集)
(ブログ記事の一覧は「こちら」)
C言語での画像ファイルの読み込み・書き込みの処理について確認していきましょう。 (画像ファイルのような、バイトデータの集まりのファイルをバイナリファイルと呼びます)
【復習】 ファイルのオープン/クローズの定義は以下のようになります。
ファイル名(filename)で示されるファイルを、指定モード(mode)でオープン
FILE *fopen(const char *filename, const char *mode);
fopen でオープンされたファイルをクローズ
int fclose(FILE *fp);
「FILE *」は、ファイルポインタというファイルを扱うときの型であるFILE型のポインタです。
以下も復習です。
テキストファイルの読み込み
fp = fopen("test_r.txt", "r");
テキストファイルの書き込み
fp = fopen("test_w.txt", "w");
以下が、今回で扱う書き方になります。
バイナリ・ファイルの読み込み
fp = fopen("input.bmp", "rb");
バイナリ・ファイルの書き込み
fp = fopen("output.bmp", "wb");
Bitmap(ビットマップ)ファイルのフォーマット
Bitmap(ビットマップ)ファイルは、圧縮されていない画像ファイルのことです。 まず、Bitmapファイルを用意します。(「bmp sample」で検索すると見つかります。)
次に、用意したBitmapファイルのフォーマットを確認します。
バイナリファイルのデータを確認するには、「Stirling」というツールを使用すると便利です。 StirlingでBitmapファイルを開くと、16進数の値が16列ずつに並んだ表示が得られます。 バイナリファイルはそれらの値が意味を持っています。
赤い枠で囲った部分が、Bitmapファイルの画像ファイルの幅と高さになります。
(Bitmapファイルのフォーマットについては、「bmp フォーマット」などで検索すると情報が見つかります。)
読み取った順に処理すると、「00 00 01 00」となります。これは10進数で「256」です。 つまり、上の例での、画像ファイルの幅と高さは「256 * 256」です。
Bitmap(ビットマップ)ファイルの読み込みのサンプル
Bitmap(ビットマップ)ファイルのデータを読み込むサンプルです。あらかじめ、プロジェクトフォルダに「input.bmp」をコピーして、以下のプログラムを実行してください。
#include <stdio.h> int main(void) { unsigned char BitMapFileHeader[14]; // BMPのファイルヘッダー unsigned int biSize; // 情報ヘッダーのサイズ int biWidth; // BMPの幅 int biHeight; // BMPの高さ unsigned char BitMapInfoHeader[28]; // 他のBMPの情報ヘッダー unsigned char cData[512][512][3]; //画素情報(幅、高さ、RGB) int i, j, k; FILE *fp; fp = fopen("input.bmp", "rb"); fread(&BitMapFileHeader, sizeof(char), 14, fp); fread(&biSize, sizeof(int), 1, fp); fread(&biWidth, sizeof(int), 1, fp); fread(&biHeight, sizeof(int), 1, fp); fread(&BitMapInfoHeader, sizeof(char), 28, fp); for (i = 0; i < biWidth; i++) { for (j = 0; j < biHeight; j++) { for (k = 0; k < 3; k++) { fread(&cData[i][j][k], sizeof(char), 1, fp); } } } fclose(fp); return 0; }
以下は、Bitmapファイル「input.bmp」を読み込み、「out.bmp」を書き出すサンプルです。
#include <stdio.h> int main(void) { unsigned char BitMapFileHeader[14]; // BMPのファイルヘッダー unsigned int biSize; // 情報ヘッダーのサイズ int biWidth; // BMPの幅 int biHeight; // BMPの高さ unsigned char BitMapInfoHeader[28]; // 他のBMPの情報ヘッダー unsigned char cData[512][512][3]; //画素情報(幅、高さ、RGB) int i, j, k; FILE *fp1; FILE *fp2; // BMPファイルの読み込み fp1 = fopen("lena_color_256.bmp", "rb"); fread(&BitMapFileHeader, sizeof(char), 14, fp1); fread(&biSize, sizeof(int), 1, fp1); fread(&biWidth, sizeof(int), 1, fp1); fread(&biHeight, sizeof(int), 1, fp1); fread(&BitMapInfoHeader, sizeof(char), 28, fp1); for (i = 0; i < biWidth; i++) { for (j = 0; j < biHeight; j++) { for (k = 0; k < 3; k++) { fread(&cData[i][j][k], sizeof(char), 1, fp1); } } } fclose(fp1); // BMPファイルの書き込み fp2 = fopen("out.bmp", "wb"); fwrite(&BitMapFileHeader, sizeof(char), 14, fp2); fwrite(&biSize, sizeof(int), 1, fp2); fwrite(&biWidth, sizeof(int), 1, fp2); fwrite(&biHeight, sizeof(int), 1, fp2); fwrite(&BitMapInfoHeader, sizeof(char), 28, fp2); for (i = 0; i < biWidth; i++) { for (j = 0; j < biHeight; j++) { for (k = 0; k < 3; k++) { fwrite(&cData[i][j][k], sizeof(char), 1, fp2); } } } fclose(fp2); return 0; }
ファイル処理(テキストファイルの編集)
(ブログ記事の一覧は「こちら」)
ファイルのオープン/クローズの定義は以下のようになります。
ファイル名(filename)で示されるファイルを、指定モード(mode)でオープンする
FILE *fopen(const char *filename, const char *mode);
「FILE *」は、ファイルポインタというファイルを扱うときの型であるFILE型のポインタです。
テキストファイルについての、モード(mode)の基本的な種類については、以下の通りです。
モード | 機能 | ファイルが無い場合 |
---|---|---|
"r" | 読み込み | エラー |
"w" | 書き込み | 新規作成 |
"a" | 追記 | 新規作成 |
fopen でオープンされたファイルをクローズする
int fclose(FILE *fp);
テキストファイルの読み込み
以下のプログラムは、プロジェクト直下にあるテキストファイル「test_r.txt」をオープンしてクローズするだけの処理を行っています。
まず、プロジェクト直下にテキストファイル「test_r.txt」を作成して、読み込むテキストを適当に入力してください。
テキストファイル「test_r.txt」が存在しない場合、ファイルポインタ「fp」はNULLとなります。
#include <stdio.h> int main(void) { // 読み込み用のテキストファイルを開く FILE *fp; fp = fopen("test_r.txt", "r"); if (fp == NULL) { printf("file cannot open.\n"); return -1; } // テキストファイルの読み込み int chr; while ((chr = fgetc(fp)) != EOF) { printf("%c", chr); } // テキストファイルを閉じる fclose(fp); return 0; }
テキストファイルの読み込みはfgetc関数を使用します。1文字ずつ読み出します。
テキストファイルへの書き込み
以下のプログラムは、プロジェクト直下にテキストファイル「test_w.txt」をオープンしてクローズするだけの処理を行っています。
その結果、プロジェクト直下に、空の(何も書かれていない)テキストファイル「test_w.txt」が作成されます。
#include <stdio.h> int main(void) { // 書き込み用のテキストファイルを開く FILE *fp; fp = fopen("test_w.txt", "w"); if (fp == NULL) { printf("file cannot open.\n"); return -1; } // テキストファイルへの書き込み fprintf(fp, "こんにちは!\n"); fprintf(fp, "お元気ですか?\n"); // テキストファイルを閉じる fclose(fp); return 0; }
テキストファイルの書き込みはfprintf関数を使用します。
テキストファイルのコピー
テキストファイルのコピーは、テキストファイルの読み込みと書き込みを行うことで実現できます。
以下の例では、テキストファイル「test_r.txt」の内容を読み取り、「test_cp.txt」に書き込むことでコピーを行っています。
#include <stdio.h> int main(void) { // 読み込み用のテキストファイルを開く FILE *fp1; fp1 = fopen("test_r.txt", "r"); if (fp1 == NULL) { printf("file cannot open.\n"); return -1; } // 書き込み用のテキストファイルを開く FILE *fp2; fp2 = fopen("test_cp.txt", "w"); if (fp2 == NULL) { printf("file cannot open.\n"); return -1; } // テキストファイルの読み込み int chr; while ((chr = fgetc(fp1)) != EOF) { // テキストファイルの書き込み fputc(chr, fp2); } // テキストファイルを閉じる fclose(fp1); fclose(fp2); return 0; }
読み込み用のテキストファイルからfgetc関数で1文字ずつ読み出したデータを、書き込み用のテキストファイルにfputc関数で1文字ずつ書き込みます。