- 投稿日:2021-02-26T20:40:54+09:00
先頭から7bitが年、4ビットが月、残り5ビットが日 (Elixir)
はじめに
- Elixir楽しんでいますか
- AndroidでSuicaの履歴をみるというものを作ってみました
- 「先頭から7bitが年、4ビットが月、残り5ビットが日」、「とある2バイトが残高、ただしリトルエンディアン」こういった処理って地味にけっこうたいへんではないでしょうか
- Elixirならパターンマッチですっきり書けますのでご紹介します
- 2021/2/27に開催するautoracex #12というもくもく会の成果とします
- 以下で紹介するサイトの記事を参考にするとすぐにできました
参考にしたサイト
- AndroidアプリでNFCタグを読み書きするための基礎知識
- AndroidのNFCでSuicaの履歴を読んでみる
- 履歴を読み出すためのコマンドと取得例
- https://github.com/thinkAmi/RubotoFelicaRead/blob/acccaa6f2a513154d25b7583061ac3e517be6c68/src/net/kazzz/felica/suica/Suica.java
- 「AndroidのNFCでSuicaの履歴を読んでみる」記事で省略されている処理の実装の参考になりそうなものが書いてあります
- Felica Library > Wiki > Suica
- 履歴を読み出すためのコマンドを実行したあとに返ってくる値の仕様(構造)を有志の方がまとめてくださっています
( Felica Library > Wiki > Suica )
先頭から7bitが年、4ビットが月、残り5ビットが日
Java
public Date getProccessDate(int date, int time) { int yy = date >> 9; int mm = (date >> 5) & 0xf; int dd = date & 0x1f; Calendar c = Calendar.getInstance(); c.set(Calendar.YEAR, 2000 + yy); c.set(Calendar.MONTH, mm-1); c.set(Calendar.DAY_OF_MONTH, dd); int hh = time >> 11; int min = (time >> 5) & 0x3f; c.set(Calendar.HOUR_OF_DAY, hh); c.set(Calendar.MINUTE, min); return c.getTime(); }Elixir
"FgEAAilfAlyLByQDAAWHMA=="
は冒頭の写真でみせました表示例の最初のデータをBase64エンコードしたものであります- これを例に2020年10月31日が簡単に取り出せることを示します
iex> Base.decode64 "FgEAAilfAlyLByQDAAWHMA==" {:ok, <<22, 1, 0, 2, 41, 95, 2, 92, 139, 7, 36, 3, 0, 5, 135, 48>>} iex> <<_::32, year::7, month::4, day::5, _::binary>> = <<22, 1, 0, 2, 41, 95, 2, 92, 139, 7, 36, 3, 0, 5, 135, 48>> <<22, 1, 0, 2, 41, 95, 2, 92, 139, 7, 36, 3, 0, 5, 135, 48>> iex> year 20 iex> month 10 iex> day 31
- どうでしょうか、細かい書き方は抜きにして
7bit
とか4bit
、5bit
を指定することでパターンマッチで値をとれます- シフトなどの演算を自分で書く必要はありません
とある2バイトが残高、ただしリトルエンディアン
Java
remain = Long.valueOf((bytesToInt(new byte[]{response[24], response[23]}))); ... private int bytesToInt(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X", b)); } return Integer.valueOf(sb.toString(), 16); }
- こちらも冒頭の写真の最初のデータの残高804円が取り出せる様子を示します
Elixir
iex> <<_::80, remain::16-little, _::binary>> = <<22, 1, 0, 2, 41, 95, 2, 92, 139, 7, 36, 3, 0, 5, 135, 48>> <<22, 1, 0, 2, 41, 95, 2, 92, 139, 7, 36, 3, 0, 5, 135, 48>> iex> remain 804
- どうでしょうか、こちらも
16-little
という文法さえ覚えてしまえばパターンマッチ一発で値を取得できますWrapping Up
- 投稿日:2021-02-26T17:06:16+09:00
あらためてJavaを学習する(その1)変数
Java学習の備忘録です。
Javaインストールから簡単なクラスの説明などは「あらためてJavaを学習する(その0)」で解説してます。
あらためてJavaを学習する(その0)変数(variable)
メモリ領域(記憶装置)に名前(識別子)をつけて値を保存すること。
変数を用いることで、データを一定期間記憶し必要なときに利用することができる
- メモリの場所に変数名をつける
データ型
を指定して、値(value)
を代入する- 値を呼び出す際は、変数名 → メモリ番地を参照 → 値を取得 の順になる
変数に入れる値のことを
リテラル
と呼ぶ。識別子のルール
- 予約語以外であれば任意で指定可能
- 英数字、アンダースコア(_)、ドルマーク($)が利用可能。
- 数字で始まることはできない
<重要ポイント>
小文字と大文字は区別されるデータ型
データ型は2種類ある。
- 基本データ型(プリミティブ型)
変数に直接値を入れる- 参照型
参照するメモリの番地(アドレスの値)を保持(文字列や配列など)基本データ型の一覧
データ型 説明 サイズ byte 整数( -128 ~ 127 ) 8bit short 整数( -32,768 ~ 32,767 ) 16bit int 整数( -2,147,483,648 ~ 2,147,483,647) 32bit long 整数( -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807) 64bit float 浮動小数( ±1.40239846 × (10 の- 45 乗) ~ ±3.40282347 × (10 の 38 乗) 32bit double 浮動小数( ±4.940655645841246544 × (10 の -324乗) ~ ±1.79769313486231570 × (10 の 308乗 ) 64bit char 1文字(Unicode) 16bit boolean 真偽値(true/false) 1bit 太字がよく使うデータ型
変数の宣言
// データ型 変数名; int num; // 宣言と初期化 // データ型 変数名 = 値; int num = 1;※変数の初期化
変数に対して最初に値を代入すること。
値を代入せず変数宣言を行い、変数を利用しようとするとエラーとなる。int num; System.out.println(num); // error: variable num might not have been initialized参照型
クラスから生成したインスタンスや配列などは参照型となる。
ざっくり書くと「基本データ型以外のデータ型」は参照型。参照 = 「値を指し示すもの(メモリのアドレス)」
参照型には何も参照していないことを示す
null
が代入できる。文字列
データ型は
String
。
文字列は基本データ型ではなく参照型。
JavaScriptなどの他の言語では、シングルクォーテーション('
)でも代入できるが、Javaの場合はダブルクォーテーション("
)のみとなる。String str1 = "Hello Java"; String str2 = 'Hello Java'; // error: unclosed character literal配列
同じデータ型で複数データをひとまとめにしたもの。
初期化にはnew
を指定する。宣言と初期化が同時であれば、newを用いず
{}
の記述を行い、その中に値をカンマ(,
)で区切って代入することが可能。
なお、{}
を使うことで、宣言と代入を分割して記述することもできるが、その際は[ ]の中に要素数の指定はできない。({ }で指定した要素数で配列の要素が決まるため)// **** 宣言 **** // データ型[] 変数名; // データ型 変数名[]; int[] numbers; // **** 初期化 **** // 変数名 = new データ型[データの個数]; numbers = new int[10]; // **** 宣言と初期化 **** String[] names = {"Java", "PHP", "COBOL"}; // **** 分割 **** int[] nums; nums = new int[]{1, 50, 200};各値は
インデックス
を指定することで取得することができる。先頭は0となる。
なお、インデックスを指定しない場合はその配列の参照値が取得できる。System.out.println(names[0]); // Java System.out.println(numbers); // [I@36baf30c初期化のデフォルト値
new
で要素分の配列を確保した時点で、デフォルト初期値が設定される。
データ型 初期値 boolean[ ] false int[ ], long[ ], float[ ], double[ ], char[ ], byte[ ] 0 上記以外(String型など) null 多次元配列
配列の中に配列を指定できる。
String[][] progs = new String[2][2]; progs[0][0] = "Java"; progs[0][1] = "PHP"; progs[1][0] = "Oracle"; progs[1][1] = "The PHP Group"; System.out.println(progs[0][0]); // Java System.out.println(progs[1][0]); // Oracle変数のスコープ
スコープ = 変数の有効範囲
ブロック{ }
内で宣言した変数は、そのブロック内だけで有効であり外からは参照できない。Main.javapublic class Main { int a = 1; public static void main(String[] args) { // 変数bをここで宣言する int b = 10; Main mainClass = new Main(); mainClass.printNum(); } void printNum(){ int c = 100; System.out.println(a); // bは宣言されていないためエラーが起きる System.out.println(b); // error: cannot find symbol System.out.println(c); } }演算子
演算子自体を
オペレータ
と呼び、値のことをオペランド
と呼ぶ。
値と演算子を組み合わせた計算の単位を式
と呼ぶ。主な演算子一覧
int a = 1;
int b = 2;
演算子 説明 記述例 結果 備考 + 加算 10 + 1 11 - 減算 10 - 1 9 * 乗算 10 * 10 100 / 除算 10 / 2 5 % 剰余 10 % 3 1 += 加算(a = a + 1) a += 1 2 ++ 加算(a = a + 1) a++ または ++a 2 インクリメント(サンプルコード参照) -= 減算(b = b -1) b -= 1 1 -- 減算(b = b -1) b-- または --b 1 デクリメント(サンプルコード参照) *= 乗算(b = b * 2) b *= 2 4 /= 除算(b = b / 2) b /= 2 1 %= 剰余(b = b % 2) b %= 2 0 > 比較(aがbより大きい) a > b false < 比較(aがbより小さい) a < b true >= 比較(aがb以上) a >= b false <= 比較(aがb以下) a <= b true == 比較(aがbと等しい) a == b false 文字列の値比較の場合はString#equalsメソッドを利用(サンプルコード参照) != 比較(aとbが異なる) a != b true & 論理積(AND) a > b & a != b false 左辺がfalseでも右辺も評価を行う && 論理積(AND) a > b && a != b false 左辺がfalseの時点で評価を終える(右辺の評価はしない) | 論理積(OR) a > b | a != b true 左辺がtrueでも評価を行う || 論理積(OR) a > b || a != b true 左辺がtrueの時点で評価を終える(右辺の評価はしない) サンプルコード
Sample1.javapublic class Sample1 { public static void main(String[] args) { // **** インクリメント・デクリメント **** int a = 1; int b = 2; System.out.println(a++); // 1 // System.out.println(a); // a = a + 1; System.out.println(++a); // 3 // a = a + 1; // System.out.println(a); System.out.println(a++ + --a + b--); // 8 System.out.println("a : " + a + " , b : " + b); // a : 3 , b : 1 // **** 文字列の値比較 **** String str1 = "Hello"; // インスタンス生成 String str2 = new String("Hello"); System.out.println(str1 == str2); // false System.out.println(str1.equals("Hello")); // true // **** ANDとOR **** int x = 10; if(false && ++x == 11){ // 右辺は評価されない System.out.println(x); } if(false & ++x == 11){ // 右辺は評価される System.out.println(x); } System.out.println(x); // 11 } }文字列と数値の連結
数値側は自動的に文字列に変換されて結合される
括弧() で括ることで、演算結果をうまく連結することができるSample2.javapublic class Sample2 { public static void main(String[] args) { int num = 1; String str = "1"; System.out.println(str + num); // 11 System.out.println(str + (num + 3)); // 14 System.out.println(str + num + 3); // 113 } }キャスト(明示的型変換)
大きい型から小さい型への変換の場合は、明示的に宣言(
キャスト
)しないとエラーとなる。型によって保持できる値の範囲があるため、容量越えの値をキャストした場合は桁落ちなどで想定外の結果になる可能性があるので注意。
小数から整数へのキャストの場合は、小数点以下は切り捨てられる。
<キャストが不要な場合の条件>
* 小さい型から大きい型への変換
* 整数から小数への変換int intNum = 10; // 小 → 大 long longNum = intNum; // 大 → 小 // データ型 変数名 = (データ型)変数名; byte byteNum = (byte)intNum; System.out.println(longNum); // 10 System.out.println(byteNum); // 10 // 小数 → 整数 double doubleNum = 3.1416; intNum = (int)doubleNum; System.out.println(intNum); // 3最後に
変数についてザックリと学習してみました。
次回は基本構文の学習をザッと行いたいと思います!参考文献
この記事は以下の情報を参考にして執筆しました。
【サイト】
・Wikipedia:変数(プログラミング)
・苦しんで覚えるC言語:数値を記録する
【書籍】
・「基礎からのJava」SB Creative出版:基礎からのJava 改訂版
- 投稿日:2021-02-26T15:03:49+09:00
Java小テスト② 〜 for 編 〜
これは友達向けのJavaの小テストです。
Java初心者の文法腕試しでお使いくださいー!!今日はfor文の小テストを作りたいと思います!
問題1:ミスを探しなさい。
public static void main(String[] args) { for i = 0; i < 10; i++ { System.out.println(i); } }問題2:出力されるものは何か?
public static void main(String[] args) { for(int i = 0; i < 100; i += 10) { System.out.print(i); } }問題3:以下を満たすプログラムを書きなさい。
- 100回繰り返す。
- 回数が3の倍数のときは, 「Fizz」を出力する
- 回数が5の倍数のときは. 「Buzz」を出力する
- 3と5の公倍数である場合は「FizzBuzz」を出力する
- それ以外は数字をそのまま出力する。
問題4:以下を満たすプログラムを書きなさい。
- 1から10を動く変数"i"
- 10から20を動く変数"j"
- 各 "i"と"j"に対して, "i + j"が問題3の状態を満たす
- 投稿日:2021-02-26T12:15:03+09:00
Javaの機能
クラス定義
class クラス名 { }クラス名は英字の大文字で始まる名詞にする。キャメルケースで命名。
mainメソッド
class クラス名 { public static void main(String[] args){ } }プログラムの処理を開始する場所。
クラスの中に定義され、JVMはmain
メソッドの先頭から処理を実行する。データ出力
System.out.println("こんにちは"); System.out.println(1);出力こんにちは 1
System.out.println
は出力時、改行される。System.out.print("こんにちは"); System.out.print(1);出力こんにちは1
System.out.print
は出力時、改行されない。コメントアウト
// 1行 /* 複数行 */変数
// データ型 変数名 = データ;で定義 int number = 777; //整数を扱うにはint型 double number2 = 0.14; //小数を扱うにはdouble型 char message = 'A'; //1文字をを扱うにはchar型 String messages = "ABC"; //文字列を扱うにはString型 boolean flag = false; //true or falseを扱うにはboolearn型定義したデータ型と違うデータを代入するとコンパイルエラーが起こる。
定数
final int SP_NUMBER = 777;再代入不可能の変数。普通の変数の定義の前に
final
を指定するだけ。
すべて大文字で定義。複数の単語を組み合わせるなら_
を使用する。演算子
変数の値に1加算する演算子 == インクリメント演算子
++変数
or変数++
変数の値から1を減算する演算子 == デクリメント演算子--変数
or変数--
int data1 = 100 int data2 = 100 System.out.println( ++data1 ) System.out.println( data1++ )出力11 10前と後ろどちらに記述しても効果は同じだが処理のタイミングが異なる。
前なら変数の利用前
後ろなら変数の利用後型変換
- 自動変換
データ型が異なる変数間で演算を行う際に、サイズの小さいデータ型から大きいデータ型に自動的に変換される。
(サイズとはバイトのこと)代入演算int number = 13; //double型のnumber2にint型のnumberを代入する double number2 = number; //double型に変換されて代入されている。算術演算int number = 13; double number2 = 7.7; //int型+double型の演算 double number3 = number + number2; //この場合はnumberがdouble型に変更されて計算されている。
- キャストによる変換
プログラマが明示的に行う型変換。キャスト演算子を使用する。
大→小double number = 7.7; int number2; //大きいデータ型numberを小さいデータ型のnumber2に代入したい。 number2 = (int)number; //キャストは成功。しかしint型では小数点は扱えないため、小数点以下が切り捨てられてしまう。一時的変換int number = 30; int number = 7; //キャストせずに、int型同時の割り算を行うと... double result = number/number2; //intでは小数を扱えないため、30/7=4となってしまい、resultの値は4を小数点で表した4.0となる。 //キャストを行った場合... double result = (double)number/number2; //numberが変換され30.0に、number2は自動変換で7.0となり、resultは4.5となる。配列
配列の宣言データ型[] 配列名;配列の生成int[] hoge; hoge = new int[7]; //配列の要素数を決めている。宣言と生成をまとめて行うint[] hoge = new int[7];宣言と生成と初期値を代入int[] hoge = {1,2,3,4,5,6,7}equalsメソッド
文字列の比較には比較演算子が使用できない。その代わり
equals
メソッド使用する。"aaa".equals("aaa")分岐処理
- if文
条件によって異なる処理を実行する。
public class Sample { public static void main(String[] args) { int a = 5; if (a < 7) { System.out.println("aは7未満です。"); } else { System.out.println("aは7以上です。"); } } }出力aは7未満です。
- swich文
public class Sample { public static void main(String[] args) { int a = 5; switch(a){ case 0: System.out.println("0ですね~"); break; case 5: System.out.println("5ですね~"); break; case 13: System.out.println("13ですね~"); break; case default: System.out.println("分かりませぬ"); break; } } } // breakを書かなければ当てはまっても処理を途中で抜けずに以降のケース全てを出力してしまう。繰り返し処理
- while文
public class Sample { public static void main(String[] args) { int a = 0; while(a<5){ //aが5になるまで処理を続ける。 System.out.println(a++); //aを出力後に+1 } } }出力0 1 2 3 4
- for文
繰り返したい回数が決まっている時に使用されやすい。
public class Sample { public static void main(String[] args) { for(int a = 0; a < 5; a++){ System.out.println(a); //aを出力後に+1 } } }出力0 1 2 3 4
- 拡張for文
public class Sample { public static void main(String[] args) { int[] a = {1,2,3,4,5}; for(int number: a){ //aの配列の要素がなくなるまで繰り返し処理を行う System.out.println(number); } } }出力1 2 3 4 5インスタンス(オブジェクト)
生成時Example exam = new Example(); //変数examに生成されインスタンスを参照するための値が入っている。メソッドの使用時Example exam = new Example(); exam.power(15) //参照先のインスタンスのインスタンスメソッドpowerに引数を渡して処理している。コンストラクタ
インスタンス生成時に呼び出される特殊なメソッド。
オブジェクト生成時にインスタンス変数を任意の値に初期化できる。定義時にはクラス名と同じ名前で定義する必要がある。
注意点として
コンストラクタがあるのに引数無しのインスタンスを作成しようとするとコンパイルエラーになる。
そのため、コンストラクタ未定義の場合にのみ引数無しのインスタンスを作成できる。
未定義の場合はコンパイル時にデフォルトコンストラクタが補完される。コンストラクタ定義時class Example{ String prefecture; String boss; Example(String p, String b){ //同じ名前で定義 prefecture = p; boss = b; } }コンストラクタ呼び出しExample exam = new Example("東京都知事","小池百合子"); //インスタンスメソッドにあらかじめ引数を受け取るように設定しておけば、コンストラクタによってインスタンス変数の初期値が引数の値になる。オーバーロード
1つのクラスに同名のメソッドやコンストラクタがあっても引数の型や数が違えば別のメソッドとみなされる。
メソッドのオーバーロードclass Example{ int b void aaa(){ b++; } void aaa(int a){ b += a; } } //同じメソッド名aaaでも引数の個数が違うため別のメソッドとして扱われる。呼び出しの際は、メソッド側の受け取れる引数の型と個数にあわせて呼び出せばOK。
オーバーライド
サブクラスでスーパークラスのメソッドを書き換える機能。
(しかし実際は書き換えているわけではなく、スーパークラスにあったメソッドも残っている。)残っているスーパークラスのメソッドを使用したいときは、
super.メソッド名
で使用できる。メソッド名が同じであり引数も同じでなければならない。
スーパークラスclass EXample -------------------------------- pubulic void abcd(){ System.out.println("abcd"); }サブクラスclass EeampleWord extends Example -------------------------------- pubulic void abcd(){ System.out.println("abcd"); System.out.println("efgh"); }テストクラスclass TestExample{ public static void main(String[] args){ ExampleWord a = new ExampleWord(); a.abcd(); //呼び出されるのはサブクラスのメソッド } }this
オブジェクト自身を表している。
インスタンス変数とローカル変数の区別、オーバーロードしている別のコンストラクタの呼び出しに使われる。
変数の区別class Example{ String prefecture; //インスタンス変数prefectureを定義。 //スコープが違えば同じ名前の変数が使用できる。 Example(String prefecture ){ //コンストラクタでローカル変数prefectureを定義。 this.prefecture = prefecture; //左辺がインスタンスprefectureを意味している。 } }カプセル化
オブジェクトの中身を外部に対して非公開にすること。
アクセス指定子を使って公開範囲を決めることができる。
指定子 同一クラス 同一パッケージ 別パッケージの子クラス 別パッケージ public 〇 〇 〇 〇 protected 〇 〇 〇 × 指定なし 〇 〇 × × private 〇 × × × ※パッケージはフォルダーみたいな解釈でOK
指定の方法//インスタンス変数 アクセス指定子 データ型 変数名; //インスタンスメソッド アクセス指定子 (void) 変数名()proivateなインスタンス変数にアクセスるためにはアクセサが必要。
詳しくは検索。static変数
staticメソッドを修飾子に指定した場合オブジェクト生成を必要としない。
インスタンス変数は、インスタンスが複数生成されてもインスタンスごとに独立していた。
一方
static
変数では、インスタンスごとではなくクラスに1つしか用意できない変数。
どのインスタンスからでもアクセスすることが出来る。
static
メソッドも存在しており、static
メソッドはstatic
メンバ(staticのインスタンス変数など)へのアクセスのみができる。定義の仕方static final データ型 変数名 = 初期値; //複数のインスタンスからアクセスできるため、定数にしなければどのインスタンスからでも書き換えができてしまう。列挙型
配列とは違い定数を集めて名前を付けたもの。
定義の仕方enum 列挙型名{ 要素,要素,要素... }参照の仕方列挙型名.要素名列挙型が定義されていた場合は、データ型に指定して変数に代入できる。
列挙型名 変数名 = 列挙型名.要素名インタフェース
定義interface インタフェース名 { // static定数と抽象メソッドが定義できる。 }抽象メソッドとは定義するだけのメソッドで中身を持たないメソッド。
そのため、インタフェースを実装したクラスには抽象メソッドの中身が存在しなくてはならない。interface インタフェース名 { public abstract void abcd(); //abcdメソッドが別に定義されている必要がある。 void efgh(); //修飾子を省略するとpublicメソッドになる。 }実装class クラス名 implements インタフェース名{ //インタフェースめ抽象メソッドをオーバーライドする。 }インタフェースは単体ではインスタンス化をすることはできない。
クラスに実装を行って初めてインスタンス化が行えるようになる。
インスタンス化を行うとインタフェース型変数に代入することも可能。
抽象クラス
定義abstract クラス名 { }インスタンスメソッドも抽象メソッドも定義が可能だがインスタンス化を行えない。
継承を前提としたクラス。インスタンス化も行えないため、継承しなければ使用の意味がない。