marunomaruno-memo

marunomaruno-memo

[Android] クリックしたタイムスタンプを保存して、メールで送信する

2012年06月24日 | Android
[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][アルゴリズム] チューリングマシンのエミュレーター

2012年06月23日 | C / C++
[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] 日付関係のユーティリティ

2012年06月11日 | Android
[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 文字関係のユーティリティ

2012年06月10日 | C / C++
[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;
}
---

                                                                            以上