[Android] クリックしたタイムスタンプを保存して、メールで送信する ================================================================================ ちょっと必要があって、標記のアプリを作った。 メールの送信先のアドレスも固定(リソースで定義)。 タイムスタンプは long 型なので、それらの値を List<Long> で保持したいところ。でも、 onSaveInstanceState()、onRestoreInstanceState() でインスタンス間の受け渡しを行う にしても、残念ながら Bundle クラスには、putLongArrayList() というメソッドはない。 しょうがないので、long 型のタイムスタンプを上位 4 バイトと下位 4 バイトに分けて、 上位 4 バイトは long 型、下位 4 バイトは int 型のリスト(正確には Integer 型のリ スト)に保存することにした。そうすることで、putIntegerArrayList() メソッドが使え る。 しかし、putLongArrayList() がないのは、int が整数の標準の型なのでしょうがないと して、putIntegerArray() がないというのは、納得がいかない気がする。というよりも、 putIntegerCollection() というのがあってもよさそうだと思う。 ■ アクティビティ □ TimestampLoggerActivity.java --- package jp.marunomaruno.android.timestamplogger; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.TextView; /** * クリックしたタイムスタンプを保存して、メールで送信する。 * メールの送信先のアドレスも固定。 * * @author marunomaruno * @version 1.0, 2012-06-20 */ public class TimestampLoggerActivity extends Activity { private static final String TEMP_FILE_NAME = "data.dat"; private static final long TIMESTAMP_HEIGHT_BYTES_MASK = 0xFFFFFFFF00000000L; // long型の上位4バイトを取得するマスク // (1) private static final String TIMESTAMP_LIST = "timestampList"; private static final String TIMESTAMP_HEIGHT_BYTES = "timestampHightBytes"; private ArrayList<Integer> timestampList; // タイムスタンプの下位4バイト分のリスト // (2) private long timestampHightBytes = 0; // タイムスタンプの上位4バイト // (3) /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); timestampList = new ArrayList<Integer>(); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); timestampHightBytes = savedInstanceState .getLong(TIMESTAMP_HEIGHT_BYTES); timestampList = savedInstanceState.getIntegerArrayList(TIMESTAMP_LIST); System.out.println("onRestoreInstanceState: " + timestampList); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putLong(TIMESTAMP_HEIGHT_BYTES, timestampHightBytes); outState.putIntegerArrayList(TIMESTAMP_LIST, timestampList); System.out.println("onSaveInstanceState: " + timestampList); } @Override protected void onPause() { super.onPause(); try { write(); } catch (IOException e) { e.printStackTrace(); } System.out.println("onPause: " + timestampList); } @Override protected void onRestart() { super.onRestart(); try { read(); } catch (FileNotFoundException e) { assert true; } catch (IOException e) { e.printStackTrace(); } System.out.println("onRestart: " + timestampList); } /** * タイムスタンプを取得するボタンのクリック・ハンドラー * * @param view */ public void onStampButtonClick(View view) { // リストの最初のタイムスタンプの上位4バイトを保持する if (timestampHightBytes == 0) { timestampHightBytes = System.currentTimeMillis() & TIMESTAMP_HEIGHT_BYTES_MASK; // (4) } // タイムスタンプの下位4バイトをリストに追加する timestampList.add((int) System.currentTimeMillis()); // (5) TextView stampView = (TextView) findViewById(R.id.stampView); stampView.setText(toTimestampStrings()); } /** * タイムスタンプのリストをクリアするボタンのクリック・ハンドラー * * @param view */ public void onClearButtonClick(View view) { timestampList.clear(); TextView stampView = (TextView) findViewById(R.id.stampView); stampView.setText(""); } /** * タイムスタンプを送信するボタンのクリック・ハンドラー * * @param view */ public void onEmailButtonClick(View view) { if (timestampList.size() == 0) { TextView stampView = (TextView) findViewById(R.id.stampView); stampView.setText(R.string.noTimestamp); return; } Uri uri = Uri.parse("mailto:" + getString(R.string.emailAddress)); // (6) Intent intent = new Intent(Intent.ACTION_SENDTO, uri); // (7) intent.putExtra(Intent.EXTRA_SUBJECT, String.format( "timestamp %1$tF %1$tT", (timestampHightBytes | timestampList.get(0)))); // (8) intent.putExtra(Intent.EXTRA_TEXT, toTimestampStrings()); // (9) startActivity(intent); // (10) } /** * タイムスタンプのリストを文字列化する。 * タイムスタンプは、JISの日時の文字列(yyyy-mm-dd hh:mm:ss)であらわし、 * これをリストとして並べたもの。 * * @return タイムスタンプのリストの文字列 */ private String toTimestampStrings() { StringBuilder sb = new StringBuilder(); for (int timestamp : timestampList) { sb.append(String.format("[%1$tF %1$tT]%n", (timestampHightBytes | timestamp))); } return sb.toString(); } /** * データをファイルに一時保存のため書き出す。 * * @throws IOException */ public void write() throws IOException { DataOutputStream out = new DataOutputStream(openFileOutput(TEMP_FILE_NAME, MODE_PRIVATE)); out.writeLong(timestampHightBytes); for (int timestamp : timestampList) { out.writeInt(timestamp); } out.close(); } /** * 一時保存されたデータをファイルから読み出す。 * * @throws IOException */ public void read() throws IOException { DataInputStream in = new DataInputStream(openFileInput(TEMP_FILE_NAME)); timestampList.clear(); try { timestampHightBytes = in.readLong(); while (true) { timestampList.add(in.readInt()); } } catch (EOFException e) { // (10) assert true; } in.close(); } } --- (1) long型の上位4バイトを取得するマスク long 型のタイムスタンプを上位 4 バイトと下位 4 バイトに分けるためのマスク。 private static final long TIMESTAMP_HEIGHT_BYTES_MASK = 0xFFFFFFFF00000000L; // (1) (2) タイムスタンプの下位4バイト分のリスト private ArrayList<Integer> timestampList; // (2) (3) タイムスタンプの上位4バイト private long timestampHightBytes = 0; // (3) (4) リストの最初のタイムスタンプの上位 4 バイトを保持する TIMESTAMP_HEIGHT_BYTES_MASK と AND を取ることで、上位 4 バイト分を確保する。 timestampHightBytes = System.currentTimeMillis() & TIMESTAMP_HEIGHT_BYTES_MASK; // (4) (5) タイムスタンプの下位4バイトをリストに追加する 下位 4 バイトは、int 型にキャストすればよい。 timestampList.add((int) System.currentTimeMillis()); // (5) (6)-(10) タイムスタンプをメールで送信する メールのあて先アドレスを Uri インスタンスとして組み立てる。 Uri uri = Uri.parse("mailto:" + getString(R.string.emailAddress)); // (6) メールを送るためのインテント・インスタンスを生成する Intent intent = new Intent(Intent.ACTION_SENDTO, uri); // (7) メールの件名を設定する。このとき、件名に最初のタイムスタンプを入れるので、タイム スタンプを組み立ても行う。 上位 4 バイトと下位 4 バイトに分かれているものを、OR することで、ふたたび long 型にする。 intent.putExtra(Intent.EXTRA_SUBJECT, String.format( "timestamp %1$tF %1$tT", (timestampHightBytes | timestampList.get(0)))); // (8) 本文に、タイムスタンプを文字列化して並べる。 intent.putExtra(Intent.EXTRA_TEXT, toTimestampStrings()); // (9) メール送信のアクティビティを起動する。 startActivity(intent); // (10) (11) EOF の検知 DataInputStream の readInt() メソッドを使っているので、EOF は EOFException を使 って検知する。 } catch (EOFException e) { // (11) ■ レイアウト タイムスタンプの表示は、今回のわたしの使い方としては、せいぜい 10 件程度なので、 ListView などを使わずに、単純に TextView を使っている。 □ res/values/strings.xml --- <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/stamp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onStampButtonClick" android:text="@string/stampButtonLabel" android:textAppearance="?android:attr/textAppearanceLarge" /> <Button android:id="@+id/clear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClearButtonClick" android:text="@string/clearButtonLabel" android:textAppearance="?android:attr/textAppearanceLarge" /> <Button android:id="@+id/email" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onEmailButtonClick" android:text="@string/emailButtonLabel" android:textAppearance="?android:attr/textAppearanceLarge" /> </LinearLayout> <TextView android:id="@+id/stampView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="" /> </LinearLayout> --- ■ リソース 【emailアドレス】部分に、タイムスタンプを送信したいあて先を指定する。 □ res/values/strings.xml --- <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, TimestampLoggerActivity!</string> <string name="app_name">TimestampLogger</string> <string name="stampButtonLabel">クリック</string> <string name="clearButtonLabel">クリア</string> <string name="emailButtonLabel">email</string> <string name="noTimestamp">タイムスタンプがありません。メール送信はしません。</string> <string name="emailAddress">【emailアドレス】</string> </resources> --- 以上
[C][アルゴリズム] チューリングマシンのエミュレーター ================================================================================ 2ヶ月くらい前にチューリングマシンのエミュレーターを作った。 まだ、公開するつもりではなかった(もう少し検証する予定だった)が、本日が、チュー リングの生誕100年だったので、ベータ版として公開。 そのうち、直すかもしれない。 ■チューリングマシン チューリングマシンは、テープ、ヘッド、制御部の 3 つから構成されています。 テープは無限の長さを持っていて、各ます目には 0 か 1 のどちらかの記号が書けるよう になっています。ヘッドはテープ上の記号を読み書きするもので、常にひとつのます目を 見ています。また、制御部は、現在の状態を表す変数と動作を決める規則(プログラム) が入っています。 制御部は、あらかじめ決められた p0 から pn までの有限個の状態の うちのひとつを取ります(p0 を初期状態、pn を停止状態といいます)。また、このチ ューリングマシンは、動作を決めた規則の中で、ヘッドの読んだ記号と現在の状態の 2 つから次の動作を繰り返します。 ●チューリングマシンの動作 ① テープに 0 か 1 の記号を書き込む。 ② ヘッドを右か左のどちらかに移動する。 ③ 制御部の状態を別の(または同じ)状態に移す。 ●チューリングマシンを使った計算手順 ① テープ上に、入力データに相当する 0,1 の記号列を用意する。 ② 制御部の状態を初期状態にして、規則に従い動作を開始する。 ③ 制御部の状態が停止状態になったとき、計算を終了したものとする。 出力(計算結果)は、停止したときのテープ上に残された記号で表される。 ③’制御部の状態が停止状態にならないとき、計算は終了しないものとする。 ●例 5 + 3 = 8 を行う テープ上のデータについては、簡単にするために記号 1 の個数で整数値を表します。特 にここでは、n + 1 個の記号 1 で整数の n を表すことにし、足される 2 数の区切りと してはひとつの記号 0 を使うものとします。 5 3 入力データ:……000001111110111100000…… このテープ上のデータに対して、次の 4 つの状態と規則でチューリングマシンを動作さ せると出力はどうなるでしょうか。 (状態と規則) ① 制御部の状態:p0,p1,p2,p3 の 4 つ p0 が初期状態、p3 が停止状態 ② 規則 状態 入力記号 書込み記号 ヘッドの移動 次の状態 p0 0 0 右 p0 p0 1 0 右 p1 p1 0 0 右 p3 p1 1 0 右 p2 p2 0 1 右 p3 p2 1 1 右 p2 参考)コンピュータシステムの基礎、アイテック情報技術教育研究所 (著) より ■ 実装 上記の説明に合わせて、つぎのように作る。 ルールとテープを標準入力またはファイルから指定して、動かす。 テープの動きの結果は標準出力に出る。 注)ルールはファイルを使わなければならない。 コマンド形式 --- turing ルール・ファイル名 [テープ・ファイル名] --- □ テープ 0 と 1 を記述したファイル。 □ ルール つぎの 5 つをタブ区切りのファイルとして作る。 なお、停止状態は、「次の状態」の中での最大値とする。 --- 状態 入力記号 書込み記号 ヘッドの移動 次の状態 --- 状態 現在の状態。0 以上の整数で指定。 入力記号 現在の状態で、ヘッドが指す記号。0 または 1 書込み記号 現在の状態で、ヘッドの位置に書き込む記号。0 または 1 ヘッドの移動 ヘッドの移動方向。左は -1、右は 1 次の状態 遷移後の状態。0 以上の整数で指定。最大値が停止状態になる ■ プログラム □ turing.c --- /* ============================================================================ Name : turing.c Author : marunomaruno Version : 0.1, 2012-06-23 Description : チューリングマシン ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> /** * ルール */ typedef struct { int status; // 状態(0以上の値) char inSymbol; // 入力記号('0', '1' または 'B') char outSymbol; // 書込み記号('0' または '1') int direction; // ヘッドの移動方向(-1: 左、1: 右) int nextStatus; // 次の状態(0以上の値) } Rule; Rule* findRule(char symbol, int status, Rule* ruleTable, int size); void printlnRule(Rule* rule); void printRule(Rule* rule); int findCharOnTape(char* tape, int start, char c); void printProgress(char* tape, int head, Rule* rule); void printlnProgress(char* tape, int head, Rule* rule); void printCount(char* tape); void printlnCount(char* tape); int readTape(FILE* in, char* tape); int readRule(FILE* in, Rule** rule); void printRuleTable(Rule* rule, int size); int getStopStatus(Rule* rule, int size); /** * ルールとテープをファイルから入力して、チューリングマシンを実行する。 * ルールは、次の5項目をタブ区切りで記述する。 * 状態, 入力記号, 書込み記号, ヘッドの移動, 次の状態 * * @param argc コマンドライン・パラメーター数 * @param argv コマンドライン・パラメーター値 * argv[1]: ルールのファイル。省略時は標準入力 * argv[2]: テープのファイル。省略時は標準入力 * @return 常に0 */ int main(int argc, char* argv[]) { printf("program start¥n"); const int START_STATUS = 0; // 初期状態 // 状態, 入力記号, 書込み記号, ヘッドの移動, 次の状態 Rule* ruleTable; int status; // 状態 char tape[BUFSIZ]; // テープ int head; // テープのヘッドの位置 // ファイル FILE* tapeFp; FILE* ruleFp; switch(argc) { case 1: ruleFp = stdin; tapeFp = stdin; break; case 2: ruleFp = fopen(argv[1], "r"); tapeFp = stdin; break; default: ruleFp = fopen(argv[1], "r"); tapeFp = fopen(argv[2], "r"); break; } if (ruleFp == NULL) { assert(0); } if (tapeFp == NULL) { assert(0); } // ルールの読み取り int ruleSize = readRule(ruleFp, &ruleTable); printf("ruleSize = %d¥n", ruleSize); printRuleTable(ruleTable, ruleSize); fclose(ruleFp); // テープの読み取り int tapeLength = readTape(tapeFp, tape);; printf("tape = ¥"%s¥"¥n", tape); fclose(tapeFp); // 状態を定義する status = START_STATUS; // 初期状態 int stopStatus = getStopStatus(ruleTable, ruleSize); // 停止状態 head = 0; // テープ・ヘッドの初期化 while (status != stopStatus) { assert(0 <= head && head < tapeLength); Rule* rule = findRule(tape[head], status, ruleTable, ruleSize); // ルールを見つける printlnProgress(tape, head, rule); tape[head] = rule->outSymbol; // テープの書き換え head += rule->direction; // ヘッドを移動 status = rule->nextStatus; // 状態を新しい状態にする } printlnProgress(tape, head, NULL); free(ruleTable); printf("program end¥n"); return EXIT_SUCCESS; } /** * テープを読み込む。 * テープの最大長は、BUFSIZ (512) バイトとする。 * @param in ファイルポインター * @param tape テープ * @return テープの長さ */ int readTape(FILE* in, char* tape) { assert(in != NULL); assert(tape != NULL); fgets(tape, BUFSIZ, in); tape[BUFSIZ] = '¥0'; char* p = strstr(tape, "¥n"); if (p != NULL) { *p = '¥0'; } return strlen(tape); } /** * ルールを読み込む。 * なお、ルール配列はこの関数中で領域確保するので、使用側で解放する必要がある。 * @param in ファイルポインター * @param rule ルール配列 * @return ルール配列のサイズ */ int readRule(FILE* in, Rule** rule) { assert(in != NULL); assert(rule != NULL); const int RULE_MALLOC_UNIT = 16; // ルール配列をとる場合のサイズの初期単位 const char* DELIMITTER = "¥t"; int limit = RULE_MALLOC_UNIT; int size = 0; char buf[BUFSIZ]; char token[BUFSIZ]; Rule* p = (Rule*) malloc(RULE_MALLOC_UNIT * sizeof(Rule)); assert(p != NULL); *rule = p; size = 0; while (fgets(buf, BUFSIZ, in) != NULL) { size++; if (size == limit) { limit *= 2; realloc(*rule, limit * sizeof(Rule)); assert(*rule != NULL); p = *rule + size - 1; } strncpy(token, strtok(buf, DELIMITTER), BUFSIZ); token[BUFSIZ] = '¥0'; p->status = atoi(token); strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ); token[BUFSIZ] = '¥0'; p->inSymbol = token[0]; strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ); token[BUFSIZ] = '¥0'; p->outSymbol = token[0]; strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ); token[BUFSIZ] = '¥0'; p->direction = atoi(token); strncpy(token, strtok(NULL, DELIMITTER), BUFSIZ); token[BUFSIZ] = '¥0'; p->nextStatus = atoi(token); p++; } return size; } /** * 途中経過をプリントする。 * @param tape テープ * @param head ヘッドの位置 * @param rule ルール */ void printProgress(char* tape, int head, Rule* rule) { assert(tape != NULL); assert(0 <= head && head < BUFSIZ); int i; printf("%s ", tape); printRule(rule); printf(" "); printlnCount(tape); for (i = 0; i < head; i++) { printf(" "); } printf("^"); fflush(stdout); } /** * 途中経過をプリントし、改行する。 * @param tape テープ * @param head ヘッドの位置 * @param rule ルール */ void printlnProgress(char* tape, int head, Rule* rule) { assert(tape != NULL); assert(0 <= head && head < BUFSIZ); printProgress(tape, head, rule); printf("¥n"); fflush(stdout); } /** * 停止状態を取得する。 * 停止状態は、ルール中の状態の最大値になる。 * @param rule ルール * @param size ルール配列のサイズ * @return 停止状態 */ int getStopStatus(Rule* rule, int size) { assert(rule != NULL); assert(size > 0); int max = rule->nextStatus; rule++; int i = 0; for (i = 1; i < size; i++) { if (max < rule->nextStatus) { max = rule->nextStatus; } rule++; } return max; } /** * ルールをプリントする。 * @param rule ルール * @param size ルール配列のサイズ */ void printRuleTable(Rule* rule, int size) { assert(rule != NULL); assert(size > 0); int i = 0; for (i = 0; i < size; i++) { printlnRule(rule); rule++; } fflush(stdout); } /** * ルールをプリントする。 * @param rule ルール. NULLのときは、空白をプリントする。 */ void printRule(Rule* rule) { if (rule != NULL) { printf("[%d, %c, %c, %d, %d]", rule->status, rule->inSymbol, rule->outSymbol, rule->direction, rule->nextStatus); } else { printf("[ - ]"); } fflush(stdout); } /** * ルールをプリントし、改行する。 * @param rule ルール */ void printlnRule(Rule* rule) { printRule(rule); printf("¥n"); fflush(stdout); } /** * 記号と状態から合致するルールを見つける * @param symbol 記号 * @param status 状態 * @param ruleTable ルール表 * @param size ruleTableの要素数 * @return 合致したルール */ Rule* findRule(char symbol, int status, Rule* ruleTable, int size) { assert(symbol == '0' || symbol == '1'); assert(status >= 0); assert(ruleTable != NULL); assert(size > 0); // printf("* %c %d %d¥n", symbol, status, size); fflush(stdout); Rule* rule = NULL; for (rule = ruleTable; rule < ruleTable + size; rule++) { assert(ruleTable <= rule && rule < ruleTable + size); if (symbol == rule->inSymbol && status == rule->status) { return rule; } } assert(0); // 必ず見つかるはず return NULL; } /** * startの位置から、指定された文字cを見つけ、その位置を返す。 * @param tape テープ * @param start 見つけるための開始位置 * @param c 見つける文字 * @return 見つかった位置。見つからない場合は-1 */ int findCharOnTape(char* tape, int start, char c) { assert(tape != NULL); assert(start >= 0); assert(c == '0' || c == '1'); int i = -1; for (i = start; i < strlen(tape); i++) { assert(start <= i && i < strlen(tape)); if (tape[i] == c) { return i; } } return -1; } /** * テープにある連続した1の数をプリントする。 * @param tape テープ */ void printCount(char* tape) { assert(tape != NULL); int i = -1; int start = -1; // 1の位置を検索する while ((i = findCharOnTape(tape, (i + 1), '1')) != -1) { // 次の0の位置 start = i; if ((i = findCharOnTape(tape, (start + 1), '0')) == -1) { printf("%d ", (strlen(tape) - start - 1)); break; } printf("%d ", (i - start - 1)); } fflush(stdout); } /** * テープにある連続した1の数をプリントし、改行する。 * @param tape テープ */ void printlnCount(char* tape) { assert(tape != NULL); printCount(tape); printf("¥n"); fflush(stdout); } --- ■ 実行 例にあるように、「5 + 3 = 8」を計算する。 □ tape.txt --- 000001111110111100000 --- 1 の並んでいる数(から 1 引いたもの)が数値をあらわす。 間の 0 が区切り。 □ rule.txt --- 0 0 0 1 0 0 1 0 1 1 1 0 0 1 3 1 1 0 1 2 2 0 1 1 3 2 1 1 1 2 --- 最大の状態が「3」。 □ 実行結果 --- program start ruleSize = 6 [0, 0, 0, 1, 0] [0, 1, 0, 1, 1] [1, 0, 0, 1, 3] [1, 1, 0, 1, 2] [2, 0, 1, 1, 3] [2, 1, 1, 1, 2] tape = "000001111110111100000" 000001111110111100000 [0, 0, 0, 1, 0] 5 3 ^ 000001111110111100000 [0, 0, 0, 1, 0] 5 3 ^ 000001111110111100000 [0, 0, 0, 1, 0] 5 3 ^ 000001111110111100000 [0, 0, 0, 1, 0] 5 3 ^ 000001111110111100000 [0, 0, 0, 1, 0] 5 3 ^ 000001111110111100000 [0, 1, 0, 1, 1] 5 3 ^ 000000111110111100000 [1, 1, 0, 1, 2] 4 3 ^ 000000011110111100000 [2, 1, 1, 1, 2] 3 3 ^ 000000011110111100000 [2, 1, 1, 1, 2] 3 3 ^ 000000011110111100000 [2, 1, 1, 1, 2] 3 3 ^ 000000011110111100000 [2, 1, 1, 1, 2] 3 3 ^ 000000011110111100000 [2, 0, 1, 1, 3] 3 3 ^ 000000011111111100000 [ - ] 8 ^ program end --- 以上
[C] 日付関係のユーティリティ ================================================================================ 日付関係のユーティリティとして、次のようなものを作った。 関数の概要 --- time_t toSystemTime(char *dateString); 日付文字列をシステム時刻に変換 time_t toStrictSystemTime(char* dateString); 日付文字列をシステム時刻に変換 char* toDateString(char *dateString, time_t systemTime); システム時刻を日付文字列に変換 char* toJaWeekString(char *weekString, time_t systemTime) システム時刻を曜日(日、月、...、土)文字列に変換 int isLeap(int year); year 年がうるう年か平年かを返す int lastMonthDay(int year, int month); year 年 month 月の最後の日付を返す --- 関数の詳細 --- time_t toSystemTime(char *dateString); 日付文字列をシステム時刻に変換する。 日付は、西暦年が 1901 以上、月は 1~12 の範囲、日は 1~31 の範囲内でなければ不 正な日付とする。 引数 dateString 日付を表す文字列("YYYYMMDD") 戻り値 システム時刻(1970年1月1日 0時0分0秒からの経過時間(秒単位))。変換でき ない場合、-1. time_t toStrictSystemTime(char* dateString); 日付文字列をシステム時刻に変換する。 日付は、西暦年が1901以上とするが、厳密に月・日の範囲内でなければ不正な日付と する。 引数 dateString 日付を表す文字列("YYYYMMDD") 戻り値 システム時刻(1970年1月1日 0時0分0秒からの経過時間(秒単位))。変換でき ない場合、-1. char* toDateString(char *dateString, time_t systemTime); システム時刻を日付文字列に変換する。 引数 systemTime システム時刻 引数 dateString 日付を表す文字列を格納する文字列へのポインター 戻り値 日付を表す文字列("YYYYMMDD") char* toJaWeekString(char *weekString, time_t systemTime) { システム時刻を曜日(日、月、...、土)文字列に変換する。 引数 systemTime システム時刻 引数 weekString 曜日を表す文字列を格納する文字列へのポインター 戻り値 曜日を表す文字列 int isLeap(int year); year年がうるう年か平年かを返す。 引数 year 西暦年。正数を指定する。 戻り値 うるう年のとき1、平年のとき0 int lastMonthDay(int year, int month); year年month月の最後の日付を返す。 引数 year 西暦年 引数 month 月。ただし、通常の月より1引いた値を使う(0~11) 戻り値 year年month月の最後の日付 --- ▲ 公開用ヘッダー □ DateUtil.h --- /* ============================================================================ Name : DateUtil.h Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : 日付を扱うユーティリティ(公開用) ============================================================================ */ #ifndef DATEUTIL_H_ #define DATEUTIL_H_ enum { YEAR_LENGTH = 4, MONTH_LENGTH = 2, DAY_LENGTH = 2, }; /** * システム時刻を曜日(日、月、...、土)文字列に変換する。 * @param systemTime システム時刻 * @param weekString 曜日を表す文字列を格納する文字列へのポインター * @return 曜日を表す文字列 */ char* toJaWeekString(char *weekString, time_t systemTime); /** * year年がうるう年か平年かを返す。 * @param year 西暦年。正数を指定する。 * @return うるう年のとき1、平年のとき0 */ int isLeap(int year); /** * year年month月の最後の日付を返す。 * @param year 西暦年 * @param month 月。ただし、通常の月より1引いた値を使う(0~11) * @return year年month月の最後の日付 */ int lastMonthDay(int year, int month); /** * 日付文字列をシステム時刻に変換する。 * 日付は、西暦年が1900以上とするが、厳密に月・日の範囲内でなければ不正な日付と する。 * @param dateString 日付を表す文字列("YYYYMMDD") * @return システム時刻(1970年1月1日 0時0分0秒からの経過時間(秒単位))。変換でき ない場合、-1. */ time_t toStrictSystemTime(char* dateString); /** * 日付文字列をシステム時刻に変換する。 * 日付は、西暦年が1901以上、月は1~12の範囲、日は1~31の範囲内でなければ不正な日 付とする。 * @param dateString 日付を表す文字列("YYYYMMDD") * @return システム時刻(1970年1月1日 0時0分0秒からの経過時間(秒単位))。変換でき ない場合、-1. */ time_t toSystemTime(char *dateString); /** * システム時刻を日付文字列に変換する。 * @param systemTime システム時刻 * @param dateString 日付を表す文字列を格納する文字列へのポインター * @return 日付を表す文字列("YYYYMMDD") */ char* toDateString(char *dateString, time_t systemTime); #endif /* DATEUTIL_H_ */ ---- ▲ 非公開用ヘッダー 内部的に使うだけの関数の定義。 リトルエンディアンを意識するのはここだけ。 □ DateUtilInner.h --- /* ============================================================================ Name : DateUtilInner.h Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : 日付を扱うユーティリティ(非公開用) ============================================================================ */ #ifndef DATE_UTIL_INNER_H_ #define DATE_UTIL_INNER_H_ static struct tm* makeTMStruct(char* weekString, struct tm* workTime); #endif /* DATE_UTIL_INNER_H_ */ --- ▲ モジュール □ DateUtil.c --- /* ============================================================================ Name : DateUtil.c Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : 日付を扱うユーティリティ ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <assert.h> #include "DateUtil.h" #include "DateUtilInner.h" /** * 日付文字列をシステム時刻に変換する。 * 日付は、西暦年が1900以上とするが、厳密に月・日の範囲内でなければ不正な日付と する。 * @param dateString 日付を表す文字列("YYYYMMDD") * @return システム時刻(1970年1月1日 0時0分0秒からの経過時間(秒単位))。変換でき ない場合、-1. */ time_t toStrictSystemTime(char* dateString) { struct tm workTime; if (strlen(dateString) != (YEAR_LENGTH + MONTH_LENGTH + DAY_LENGTH)) { return -1; } if (makeTMStruct(dateString, &workTime) == NULL) { return -1; } if (workTime.tm_mday > lastMonthDay(workTime.tm_year, workTime.tm_mon)) { return -1; } return (mktime(&workTime)); } /** * 日付文字列をシステム時刻に変換する。 * 日付は、西暦年が1901以上、月は1~12の範囲、日は1~31の範囲内でなければ不正な日 付とする。 * @param dateString 日付を表す文字列("YYYYMMDD") * @return システム時刻(1970年1月1日 0時0分0秒からの経過時間(秒単位))。変換でき ない場合、-1. */ time_t toSystemTime(char* dateString) { struct tm workTime; if (makeTMStruct(dateString, &workTime) == NULL) { return -1; } return (mktime(&workTime)); } struct tm* makeTMStruct(char* weekString, struct tm* workTime) { char year[YEAR_LENGTH + 1]; char month[MONTH_LENGTH + 1]; char day[DAY_LENGTH + 1]; if (strlen(weekString) != (YEAR_LENGTH + MONTH_LENGTH + DAY_LENGTH)) { return NULL; } // 年を設定する strncpy(year, weekString + 0, YEAR_LENGTH); year[YEAR_LENGTH] = '¥0'; workTime->tm_year = atoi(year) - 1900; if (workTime->tm_year <= 0) { return NULL; } // 月を設定する strncpy(month, weekString + (YEAR_LENGTH), MONTH_LENGTH); month[MONTH_LENGTH] = '¥0'; workTime->tm_mon = atoi(month) - 1; if (workTime->tm_mon < 0 || workTime->tm_mon > 11) { return NULL; } // 日を設定する strncpy(day, weekString + (YEAR_LENGTH + MONTH_LENGTH), DAY_LENGTH); day[DAY_LENGTH] = '¥0'; workTime->tm_mday = atoi(day); if (workTime->tm_mday < 1 || workTime->tm_mday > 31) { return NULL; } // 時刻を設定する workTime->tm_hour = 0; workTime->tm_min = 0; workTime->tm_sec = 0; workTime->tm_isdst = -1; return workTime; } /** * システム時刻を日付文字列に変換する。 * @param systemTime システム時刻 * @param dateString 日付を表す文字列を格納する文字列へのポインター * @return 日付を表す文字列("YYYYMMDD") */ char* toDateString(char *dateString, time_t systemTime) { struct tm* time = localtime(&systemTime); strftime(dateString, ((YEAR_LENGTH + MONTH_LENGTH + DAY_LENGTH) + 1), "%Y%m%d", time); return dateString; } /** * システム時刻を曜日(日、月、...、土)文字列に変換する。 * @param systemTime システム時刻 * @param weekString 曜日を表す文字列を格納する文字列へのポインター * @return 曜日を表す文字列 */ char* toJaWeekString(char *weekString, time_t systemTime) { const char WEEK[][3] = { "日", "月", "火", "水", "木", "金", "土", "日", }; struct tm* time = localtime(&systemTime); strncpy(weekString, WEEK[time->tm_wday], 2); weekString[2] = '¥0'; return weekString; } /** * year年がうるう年か平年かを返す。 * @param year 西暦年。正数を指定する。 * @return うるう年のとき1、平年のとき0 */ int isLeap(int year) { assert(year > 0); return (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)); } /** * year年month月の最後の日付を返す。 * @param year 西暦年 * @param month 月。ただし、通常の月より1引いた値を使う(0~11) * @return year年month月の最後の日付 */ int lastMonthDay(int year, int month) { const int LAST_DAYS[][2] = { { 31, 31 }, // 1 { 28, 29 }, // 2 { 31, 31 }, // 3 { 30, 30 }, // 4 { 31, 31 }, // 5 { 30, 30 }, // 6 { 31, 31 }, // 7 { 31, 31 }, // 8 { 30, 30 }, // 9 { 31, 31 }, // 10 { 30, 30 }, // 11 { 31, 31 }, // 12 }; return LAST_DAYS[month][isLeap(year)]; } --- ▲ テスト 上記の関数を確認するためのテスト・プログラム。 数字、ひらがな、かたかなの限界値をチェックする。 また、リトルエンディアン前提なので、バイト順が逆転している場合に範囲になるかどう かもチェックする。 □ DateUtilTest.c --- /* ============================================================================ Name : DateUtilTest.c Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : 日付を扱うユーティリティのテスト ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <assert.h> #include "DateUtil.h" int main(void) { time_t calendarTime; char buffer[100]; printf("test start¥n"); fflush(stdout); // isLeap() assert(isLeap(2000)); assert(isLeap(2012)); assert(!isLeap(2013)); assert(!isLeap(2100)); // lastMonthDay() assert(31 == lastMonthDay(2012, 0)); assert(29 == lastMonthDay(2012, 1)); assert(31 == lastMonthDay(2012, 2)); assert(30 == lastMonthDay(2012, 3)); assert(31 == lastMonthDay(2012, 4)); assert(30 == lastMonthDay(2012, 5)); assert(31 == lastMonthDay(2012, 6)); assert(31 == lastMonthDay(2012, 7)); assert(30 == lastMonthDay(2012, 8)); assert(31 == lastMonthDay(2012, 9)); assert(30 == lastMonthDay(2012, 10)); assert(31 == lastMonthDay(2012, 11)); assert(31 == lastMonthDay(2013, 0)); assert(28 == lastMonthDay(2013, 1)); assert(31 == lastMonthDay(2013, 2)); assert(30 == lastMonthDay(2013, 3)); assert(31 == lastMonthDay(2013, 4)); assert(30 == lastMonthDay(2013, 5)); assert(31 == lastMonthDay(2013, 6)); assert(31 == lastMonthDay(2013, 7)); assert(30 == lastMonthDay(2013, 8)); assert(31 == lastMonthDay(2013, 9)); assert(30 == lastMonthDay(2013, 10)); assert(31 == lastMonthDay(2013, 11)); // 本日 printf("%s¥n", toDateString(buffer, time(NULL))); printf("%s¥n", toJaWeekString(buffer, time(NULL))); // toCalendarTime(), toStrictCalendarTime(), toDateString(), toJaWeekString () // 1. 平年 strcpy(buffer, "20100228"); calendarTime = toSystemTime(buffer); assert(strcmp("20100228", toDateString(buffer, calendarTime)) == 0); assert(strcmp("日", toJaWeekString(buffer, calendarTime)) == 0); // 2. 平年(厳密にはエラー) strcpy(buffer, "20100229"); calendarTime = toSystemTime(buffer); assert(strcmp("20100301", toDateString(buffer, calendarTime)) == 0); assert(strcmp("月", toJaWeekString(buffer, calendarTime)) == 0); // 3. 平年 strcpy(buffer, "20100228"); calendarTime = toStrictSystemTime(buffer); assert(strcmp("20100228", toDateString(buffer, calendarTime)) == 0); assert(strcmp("日", toJaWeekString(buffer, calendarTime)) == 0); // 4. 平年(エラー) strcpy(buffer, "20100229"); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); // 5. うるう年 strcpy(buffer, "20120229"); calendarTime = toSystemTime(buffer); assert(strcmp("20120229", toDateString(buffer, calendarTime)) == 0); assert(strcmp("水", toJaWeekString(buffer, calendarTime)) == 0); // 6. うるう年(厳密にはエラー) strcpy(buffer, "20120230"); calendarTime = toSystemTime(buffer); assert(strcmp("20120301", toDateString(buffer, calendarTime)) == 0); assert(strcmp("木", toJaWeekString(buffer, calendarTime)) == 0); // 7. うるう年 strcpy(buffer, "20120229"); calendarTime = toStrictSystemTime(buffer); assert(strcmp("20120229", toDateString(buffer, calendarTime)) == 0); assert(strcmp("水", toJaWeekString(buffer, calendarTime)) == 0); // 8. 平うるう年(エラー) strcpy(buffer, "20120230"); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); // 9. 日付不正(1900年) strcpy(buffer, "19000101"); calendarTime = toSystemTime(buffer); assert(calendarTime == -1); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); // 10. 日付不正(0月) strcpy(buffer, "20000010"); calendarTime = toSystemTime(buffer); assert(calendarTime == -1); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); // 11. 日付不正(32日) strcpy(buffer, "20120532"); calendarTime = toSystemTime(buffer); assert(calendarTime == -1); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); // 12. 日付不正(9文字) strcpy(buffer, "201205301"); calendarTime = toSystemTime(buffer); assert(calendarTime == -1); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); // 13. 日付不正(7文字) strcpy(buffer, "2012053"); calendarTime = toSystemTime(buffer); assert(calendarTime == -1); calendarTime = toStrictSystemTime(buffer); assert(calendarTime == -1); printf("test end¥n"); fflush(stdout); return 0; } --- 以上
[C][SJIS] Shift_JIS 文字関係のユーティリティ ================================================================================ Shift_JIS 文字関係のユーティリティとして、次のようなものを作った。 ただし、リトル・エンディアン限定。 関数の概要 --- int stoi(const char* s); 日本語の数字列を数値(int)に変換する。 int sjisToDigit(const char* s); 日本語の数字を数値(int)に変換する。 int isSjisDigit(const char* s); 数字("0"~"9")か判断する。 int isSjisHiragana(const char* s); ひらがなか判断する。 int isSjisKatakana(const char* s); カタカナか判断する。 --- 関数の詳細 --- int stoi(const char* s); 引数sで与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。 この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。 数字列は Shift_JIS の数字以外は記述できない。Shift_JIS の数字以外が出てきた 時点で変換をやめる。 引数 s Shift_JIS で書かれた日本語の数字列 戻り値 Shift_JIS で書かれた日本語の数字列を変換した数値 int sjisToDigit(const char* s); 引数sで与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。 この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ けで判断する。 引数 s Shift_JIS で書かれた日本語の数字 戻り値 Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は -1 int isSjisDigit(const char* s); 引数sで与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。 この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ けで判断する。 Shift_JIS の"0"~"9"は、0x824f ~ 0x8258. 引数 s Shift_JIS で書かれた日本語の数字 戻り値 数値であれば 1.そうでなければ 0 int isSjisHiragana(const char* s); 引数sで与えられた Shift_JIS で書かれた日本語がひらがなか判断する。 この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ けで判断する。 Shift_JIS のひらがなは、0x829F ~ 0x82F1. 引数 s Shift_JIS で書かれた日本語文字列 戻り値 ひらがなであれば 1.そうでなければ 0 int isSjisKatakana(const char* s); 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。 この数字は、C言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだ けで判断する。 Shift_JIS のカタカナは、0x829F ~ 0x82F1. 引数 s Shift_JIS で書かれた日本語文字列 戻り値 カタカナであれば 1.そうでなければ 0 --- ▲ 公開用ヘッダー □ sjisUtil.h --- /* ============================================================================ Name : sjisUtil.h Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ(公開用) ============================================================================ */ #ifndef SJIS_UTIL_H_ #define SJIS_UTIL_H_ /** * 引数 s で与えられた Shift_JIS で書かれた日本語がひらがなか判断する。 * この数字は、C言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * Shift_JIS のひらがなは、0x829F ~ 0x82F1. * @param s Shift_JIS で書かれた日本語文字列 * @return ひらがなであれば 1.そうでなければ 0 */ int isSjisHiragana(const char* s); /** * 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * Shift_JIS のカタカナは、0x829F ~ 0x82F1. * @param s Shift_JIS で書かれた日本語文字列 * @return カタカナであれば 1.そうでなければ 0 */ int isSjisKatakana(const char* s); /** * 引数 s で与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。 * この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。 * 数字列は Shift_JIS の数字以外は記述できない。 Shift_JIS の数字以外が出てきた時点で変換をやめる。 * @param s Shift_JIS で書かれた日本語の数字列 * @return Shift_JIS で書かれた日本語の数字列を変換した数値 */ int stoi(const char* s); /** * 引数 s で与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * @param s Shift_JIS で書かれた日本語の数字 * @return Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は-1 */ int sjisToDigit(const char* s); /** * 引数 s で与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * Shift_JIS の"0"~"9"は、0x824f ~ 0x8258. * @param s Shift_JIS で書かれた日本語の数字 * @return 数値であれば 1.そうでなければ 0 */ int isSjisDigit(const char* s); #endif /* SJIS_UTIL_H_ */ ---- ▲ 非公開用ヘッダー 内部的に使うだけの関数の定義。 リトルエンディアンを意識するのはここだけ。 □ sjisUtilInner.h --- /* ============================================================================ Name : sjisUtilInner.h Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ(内部用) ============================================================================ */ #ifndef SJIS_UTIL_INNER_H_ #define SJIS_UTIL_INNER_H_ /** * 2 バイト文字 s が 2 バイト文字 start と end の間に入っているか判断する。 * この 2 バイト文字は、C 言語の文字列の要件を満たす必要はなく、それぞれの引数からの 2 バイトだけで判断する。 * @param s Shift_JIS で書かれた 2 バイト文字 * @param start Shift_JIS で書かれた 2 バイト文字で、検査する範囲の始端 * @param end Shift_JIS で書かれた 2 バイト文字で、検査する範囲の終端 * @return 範囲内であれば 1.そうでなければ 0 */ static int inRange2byteChar(const char* s, const char* start, const char* end); #endif /* SJIS_UTIL_INNER_H_ */ --- ▲ モジュール □ sjisUtil.c --- /* ============================================================================ Name : sjisUtil.c Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティ ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sjisUtil.h" #include "sjisUtilInner.h" /** * 引数 s で与えられた Shift_JIS で書かれた日本語の数字列を数値(int)に変換する。 * この関数は、標準関数 atoi() の Shift_JIS 版であるが、負値を扱えない。 * 数字列は Shift_JIS の数字以外は記述できない。 Shift_JIS の数字以外が出てきた時点で変換をやめる。 * @param s Shift_JIS で書かれた日本語の数字列 * @return Shift_JIS で書かれた日本語の数字列を変換した数値 */ int stoi(const char* s) { int num = 0; int i; for (i = 0; i < strlen(s); i += 2) { int digit = 0; if ((digit = sjisToDigit(&s[i])) == -1) { break; } num = num * 10 + digit; } return num; } /** * 引数 s で与えられた Shift_JIS で書かれた日本語の数字を数値(int)に変換する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * @param s Shift_JIS で書かれた日本語の数字 * @return Shift_JIS で書かれた日本語の数字を変換した数値。変換できない場合は -1 */ int sjisToDigit(const char* s) { if (!isSjisDigit(s)) { return -1; } return s[1] - 0x4f; } /** * 引数 s で与えられた Shift_JIS で書かれた日本語が数字("0"~"9")か判断する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * Shift_JIS の"0"~"9"は、0x824f ~ 0x8258. * @param s Shift_JIS で書かれた日本語の数字 * @return 数値であれば 1.そうでなければ 0 */ int isSjisDigit(const char* s) { return inRange2byteChar(s, "0", "9"); } /** * 引数 s で与えられた Shift_JIS で書かれた日本語がひらがなか判断する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * Shift_JIS のひらがなは、0x829F ~ 0x82F1. 長音(ー)もひらがなとする。 * @param s Shift_JIS で書かれた日本語文字列 * @return ひらがなであれば 1.そうでなければ 0 */ int isSjisHiragana(const char* s) { return inRange2byteChar(s, "ぁ", "ん") || inRange2byteChar(s, "ー", "ー"); } /** * 引数 s で与えられた Shift_JIS で書かれた日本語がカタカナか判断する。 * この数字は、C 言語の文字列の要件を満たす必要はなく、引数 s からの 2 バイトだけで判断する。 * Shift_JIS のカタカナは、0x829F ~ 0x82F1. 長音(ー)もカタカナとする。 * @param s Shift_JIS で書かれた日本語文字列 * @return カタカナであれば 1.そうでなければ 0 */ int isSjisKatakana(const char* s) { return inRange2byteChar(s, "ァ", "ヶ") || inRange2byteChar(s, "ー", "ー"); } /** * 2 バイト文字sが 2 バイト文字startとendの間に入っているか判断する。 * この 2 バイト文字は、C 言語の文字列の要件を満たす必要はなく、それぞれの引数からの 2 バイトだけで判断する。 * @param s Shift_JIS で書かれた 2 バイト文字 * @param start Shift_JIS で書かれた 2 バイト文字で、検査する範囲の始端 * @param end Shift_JIS で書かれた 2 バイト文字で、検査する範囲の終端 * @return 範囲内であれば 1.そうでなければ 0 */ int inRange2byteChar(const char* s, const char* start, const char* end) { typedef union { unsigned char str[2]; unsigned short num; } DoubleByteChar; DoubleByteChar value; DoubleByteChar startValue; DoubleByteChar endValue; value.str[0] = s[1]; value.str[1] = s[0]; startValue.str[0] = start[1]; startValue.str[1] = start[0]; endValue.str[0] = end[1]; endValue.str[1] = end[0]; return (startValue.num <= value.num) && (value.num <= endValue.num); } --- ▲ テスト 上記の関数を確認するためのテスト・プログラム。 数字、ひらがな、かたかなの限界値をチェックする。 また、リトルエンディアン前提なので、バイト順が逆転している場合に範囲になるかどう かもチェックする。 □ test.c --- /* ============================================================================ Name : test.c Author : marunomaruno Version : V1.0, 2012-06-08 Copyright : marunomaruno Description : リトルエンエンディアンの Shift_JIS 関係のユーティリティのテスト・ プログラム ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include "sjisUtil.h" int main(void) { printf("test start\n"); char hiragana[][3] = { "あ", "い", "か", "さ", "た", "や", "ら", "わ", "っ", "ん", "ー", }; char katakana[][3] = { "ア", "イ", "ウ", "サ", "タ", "ヤ", "ラ", "ワ", "ッ", "ン", "ー", }; char num[][3] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", }; char other[][3] = { { 0x4e, 0x82, '0' }, { 0x59, 0x82, '0' }, { 0x9e, 0x82, '0' }, { 0xf2, 0x82, '0' }, { 0xfc, 0x82, '0' }, { 0x97, 0x823, '0' }, "氓", "A", "一", "所", }; int i; // 長音も含め、ひらがな、カタカナ、数字 for (i = 0; i < 11; i++) { assert(isSjisHiragana(hiragana[i])); } for (i = 0; i < 11; i++) { assert(isSjisKatakana(katakana[i])); } for (i = 0; i < 10; i++) { assert(isSjisDigit(num[i])); } // 長音 assert(isSjisKatakana("ー")); assert(isSjisHiragana("ー")); // ひらがな、カタカナ、数字でない for (i = 0; i < 10; i++) { assert(!isSjisHiragana(katakana[i])); assert(!isSjisHiragana(num[i])); assert(!isSjisHiragana(other[i])); } for (i = 0; i < 10; i++) { assert(!isSjisKatakana(hiragana[i])); assert(!isSjisKatakana(num[i])); assert(!isSjisKatakana(other[i])); } for (i = 0; i < 10; i++) { assert(!isSjisDigit(hiragana[i])); assert(!isSjisDigit(katakana[i])); assert(!isSjisDigit(other[i])); } // 全角数字を数値に変換 for (i = 0; i < 10; i++) { assert(i == sjisToDigit(num[i])); } // 全角数字を数値に変換 assert(1234 == stoi("1234")); assert(1234567890 == stoi("1234567890")); assert(0 == stoi("01234567890")); assert(123456 == stoi("12345607890")); // 文字列としての検査 char* s1; s1 = "あいうえおー"; for (i = 0; i < strlen(s1); i += 2) { if (!isSjisHiragana(&s1[i])) { assert(0); } } assert(1); s1 = "あいうえおー1"; for (i = 0; i < strlen(s1); i += 2) { if (!isSjisHiragana(&s1[i])) { assert(1); } } assert(i >= strlen(s1)); s1 = "あいうえおーア"; for (i = 0; i < strlen(s1); i += 2) { if (!isSjisHiragana(&s1[i])) { assert(1); } } assert(i >= strlen(s1)); s1 = "アイウエオー"; for (i = 0; i < strlen(s1); i += 2) { if (!isSjisKatakana(&s1[i])) { assert(0); } } assert(1); s1 = "アイウエオー1"; for (i = 0; i < strlen(s1); i += 2) { if (!isSjisKatakana(&s1[i])) { assert(1); } } assert(i >= strlen(s1)); s1 = "アイウエオーあいうえおア"; for (i = 0; i < strlen(s1); i += 2) { if (!isSjisKatakana(&s1[i])) { assert(1); } } assert(i >= strlen(s1)); printf("test end\n"); return EXIT_SUCCESS; } --- 以上