ホーム » t-saitoh の投稿 (ページ 144)

作者アーカイブ: t-saitoh

2025年6月
1234567
891011121314
15161718192021
22232425262728
2930  

検索・リンク

libpafeで行き先表示板

1207131341_622x550.jpg

linuxで、Felica(Edy)の読み込みのできるライブラリ libpafe が単純で、使いやすそうだったので、ちょいと遊んでみた。 1秒間隔でポーリングさせ、Edyが認識できる度に"(在室)"と"(不在)"が切り替わるようにしてみた。Edy認識のプログラムを20行ほどと、判断+表示切替のShell Script を20行ほど、書いてみた。

下の電光掲示板は、随分前にサーバで制御できるようにしてあったので、 かなり簡単にできてます。

// felica.cxx
//
//	EdyのIDを1秒間隔でポーリング。
//	見つけたらIDを表示して停止
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
extern "C" {
#include <libpafe/libpafe.h>
#include <libpafe/pasori_command.h>
#include <libpafe/felica_command.h>
}
// 停止判定
volatile int flag_abort = 0 ;
// ^C による停止の処理
void func_sigterm( int x ) {
flag_abort = 1 ;
}
int main() {
pasori* p ;
// PaSoRiを初期化
if ( (p = pasori_open()) != NULL ) {
felica* f ;
sighandler_t old ;
pasori_init( p ) ;
// pasori_set_timeout( p , 0 ) ; // timeout forever
// イベントハンドラの切り替え
old = signal( SIGTERM , func_sigterm ) ;
while( !flag_abort ) {
// Felicaがあるかチェックする(Edyのみ)
if ( (f = felica_polling( p , FELICA_POLLING_EDY ,
0 , 1 )) != NULL ) {
uint8 idm[ 16 ] ;
// IDmを取得する
felica_get_idm( f , idm ) ;
printf( "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n" ,
idm[ 0 ] , idm[ 1 ] , idm[ 2 ] , idm[ 3 ] ,
idm[ 4 ] , idm[ 5 ] , idm[ 6 ] , idm[ 7 ] ) ;
// felica_free()があるはずなんだけど...
free( f ) ;
// 強制終了
flag_abort = 1 ;
break ;
}
// 1秒間隔
usleep( 1000000 ) ;
}
// イベントハンドラを戻す
signal( SIGTERM , old ) ;
// PaSoRiを閉じる
pasori_close( p ) ;
}
return 0 ;
}

これを処理する Shell Script。

#!/bin/bash
MY_EDY_ID="11:22:33:44:55:66:77:88"
EDY_ID="/usr/local/bin/edy_id"
EABADGE="/usr/local/bin/eabadge.pl"
STATE="HERE"
while [ "$STATE" != "" ]; do
case $STATE in
HERE ) $EABADGE -f "(在室)" ;;
OUT  ) $EABADGE -f "(不在)" ;;
esac
GET_ID=`$EDY_ID`
while [ "$GET_ID" != "$MY_EDY_ID" ]; do
GET_ID=`$EDY_ID`
done
case $STATE in
HERE ) STATE="OUT" ;;
OUT  ) STATE="HERE" ;;
esac
done

1文字/1行・入出力関数

ファイル入出力の課題中であるが、並行して1文字/1行・入出力関数の説明を行う。

1文字入出力関数

1文字単位の入出力関数は、以下のとおり。

int fgetc( fp ) // ファイルから1文字入力
getchar()       // 標準入力から1文字入力
fputc( c , fp ) // ファイルに1文字出力
putchar( c ) // 標準出力に1文字出力

fgetc,getcharは、読み込みデータがこれ以上無い場合には、EOF(#defineで-1)が 帰ってくる。使い方の一例は以下のとおり。

FILE* fp ;
if ( (fp = fopen( "hoge.txt" , "rt" )) != NULL ) {
int c ;
while( (c = fgetc( fp )) != EOF ) {
putchar( c ) ; // ファイルからのデータを標準出力に
}
fclose( fp ) ;
}

fgetc(),getchar() は、読み込んだ1文字データの文字コードを返す。 これ以上読むデータが無い場合は、-1(EOF)を返す。 このため、文字コード(256通り)と"-1"を合わせた257通りが考えられるため、 読み込んだ値を保存する c は、char型でなくint型でなければならない。

入力バッファリング

getchar() の例として、入力の小文字を大文字に変換するプログラムは、 以下のようになる。このプログラムを動かすと、本来であれば1文字入力・変換・出力 の繰り返しであり、キーボードで"abc"と入力すれば、1文字毎に大文字が出力され、 画面には、"aAbBcC"と表示されると思われがちである。

int c ;
while( (c = getchar()) != EOF ) {
if ( c >= 'a' && c <= 'z' )
c = c-'a' + 'A' ;
putchar( c ) ;
}

しかしながら、getchar()などの関数は、バッファリングが行われるため、 特にgetcharでは、行単位のバッファリングが行われる。 プログラムで入力処理が始まると、1行分のデータが溜まるまで、getchar()からは 値が読み取れない。1行のデータが溜まった時点で、その蓄えられたバッファから、 1文字づつ読み取られ処理が繰り返される。

1行入出力関数

1行単位のファイル入出力では、fgets() , fputs() が使われる。 標準出力の文字列出力では、puts()が使われる。

FILE* fp ;
if ( (fp = fopen( "in.txt" , "rt" )) != NULL ) {
char str[ 100 ] ;
while( fgets( str , sizeof( str ) , fp ) != NULL ) {
puts( str ) ;
}
fclose( fp ) ;
}

待ち行列(QUEUE)、2進数を使った集合

前回の授業でStack(LIFO)を説明したので、 今日はQueue(FIFO)を説明する。

待ち行列Queue

待ち行列(Queue)は、FIFO(First In First Out)を配列で実装する場合、 一般的には、以下のようになる。 ただしエラー対策は記載していないので、要注意。

int que[ 100 ] ;
int wp = 0 ; // 書き込み用ポインタ
int rp = 0 ; // 読み出し用ポインタ
void put( int x ) {
que[ wp ] = x ;
wp = (wp + 1) % 100 ; // 循環させる
}
int get() {
int ans = que[ rp ] ;
rp = (rp + 1) % 100 ; // 循環させる
return ans ;
}

このような配列の領域を使い切ったら、先頭から再利用するような方法は、 リングバッファなどと呼ばれる。 このような待ち行列は、キー入力バッファや、プロセス待ち行列などに よく利用される。 しかし、このプログラムでも、配列サイズ以上の データは保存できないので、 リストを用いる。

struct List* top = NULL ;
struct List** tail = &top ;
void put( int x ) {
*tail = cons( x , NULL ) ;
tail = &( (*tail)->next ) ;
}
int get() {
int ans = top->data ;
struct List* del = top ;
top = top->next ;
free( del ) ;
return ans ;
}

ただし、このプログラムは、 常に1件以上データがリストに入っている場合は 問題がないが、 get() を実行して、データ件数が0件になると、 tail の指す先が おかしくなるので注意が必要。

また、待ち行列では、先頭ポインタと末尾ポインタの2つが必要であるが、 リスト構造の末尾のNULLを、先頭データを指すようにする循環リストと する場合も多い。 特に、プロセス待ち行列を実装するときのラウンドロビン方式 などでは、 末尾まで処理が及んだ次は先頭に戻って処理を行うため、 循環リストは都合がいい。

集合と2進数

集合を扱うプログラムでもリスト構造はよく利用される。 しかし、対比のためにまずは、2進数を使った集合処理を考える。

// 数学的な集合計算
A = { 1,2,3,5,7 }
B = { 1,2,4,8 }
A∩B = { 1,2 } // 積集合
A∪B = { 1,2,3,4,5,7,8 } // 和集合
A-B = { 3,5,7 } // 差集合

この例のような数値の集合であれば、2進数を使ったテクニックで簡単。

int a = (1<<1) | (1<<2) | (1<<3) | (1<<5) | (1<<7) ;
int b = (1<<1) | (1<<2) | (1<<4) | (1<<8) ;
int a_cap_b = a & b ;
int a_cup_b = a | b ;
int n = 値 ;
// nがaに含まれるか?
if ( a & (1<<n) != 0 )
nがaに含まれる ;

ファイル処理演習と安全な入力

ファイル処理に関する説明が終わったので、 ファイル処理の演習を行う。 演習にあたって、バッファオーバフローの危険性やその対応方法などの説明も行う。

演習とファイル入出力

演習は、ファイルの入出力であることから、あらかじめ作っておいたデータファイルを 読み込み、何らかの加工を加えてファイルに出力とする。 データは、名前+点数5科目、名前+身長体重、名前+生年月日より、 出席番号に応じて課題に取り組む。

ファイル入力+出力の処理の大まかは、以下のとおりになるだろう。

FILE* fp_in ;
FILE* fp_out ;
if ( (fp_in = fopen( "..." , "rt" )) != NULL ) {
if ( (fp_out = fopen( "..." , "wt" )) != NULL ) {
while( fscanf( fp_in , .... ) != ... ) {
fprintf( fp_out , .... ) ;
}
fclose( fp_out ) ;
}
fclose( fp_in ) ;
}

安全な入力

ファイルの説明にあたり、scanf() の %s , %d の入力の仕組みとして、 「基本は空白があれば読み飛ばし、空白を見つけるまでをデータとして格納」 といった説明を、ファイルポインタ(FILE*という意味でなく、ファイル上の読み書き位置の意味)を交えながら解説する。

char  str[ 10 ] ;
scanf( "%s" , str ) ;

安全な入力対策として、上記のような入力は、10文字以上のデータを与えた場合、 危険であることを説明する。 特に、文字配列が局所変数であれば、近辺にある"関数からの戻り番地"も破壊される 可能性がある。さらに配列をはみ出す領域に、悪意のあるプログラム(機械語)を配置し、 関数戻り番地をそのプログラムに合わせることができれば、悪意のあるプログラムを 起動できることを説明する。(一般的にバッファオーバフローと呼ばれる)

このため、バッファオーバフローを起こさないためにも、入力文字制限の 可能な fgets() を使用するなどのテクニックも紹介する。

FILE* fp ; // 何らかのファイル
char buff[ 100 ] ;
while( fgets( buff , sizeof( buff ) , fp ) != NULL ) {
char name[ 100 ] ;
int  data ;
if ( sscanf( buff , "%s%d" , name , &data ) == 2 ) {
// name,dataを使った処理
}
}

7/1は、うるう秒の日

C言語の標準関数のlocaltimeでも、想定はしている。 実装は…NTPの上位サーバでもなけりゃ、いい加減だろうけど…

1206301049_694x521.jpg

創造工学演習の熱気のある風景

1206291222_960x640.JPG

リスト処理を用いたスタック

前回の課題の演習が終わっていない人も多いので、 前半を講義で、後半は演習課題の続きとした。

配列のスタック

int stack[ 100 ] ;
int *sp = stack ;
void push( int x ) {
*sp = x ;
sp++ ;
}
int pop() {
sp-- ;
return *sp ;
}
void main() {
push( 1 ) ;
push( 2 ) ;
push( 3 ) ;
printf( "%d" , pop() ) ; // 3
printf( "%d" , pop() ) ; // 2
printf( "%d" , pop() ) ; // 1
}

この様なプログラムでは、最後に入れたデータを最初に取り出せるということで、 "Last In First Out(LIFO)"と呼ぶ。一般的にはスタック。

このようなスタックは、関数の戻り番地や局所変数管理に使われる。 また、上記のプログラムでは、stackの配列が100件分しかないので、 push()が連続100回呼び出されれば、配列をはみ出してしまう。

リストのスタック

前述のとおり、配列の大きさ以上のpush()できないので、 必要に応じてメモリを確保するリスト構造を使ってpush() , pop()を書いてみる。

struct List* sp = NULL ;
void push( int x ) {
// mallocに慣れてほしいので、わざと補助関数consを使わずに
struct List* n ;
n = (struct List*)malloc( sizeof( struct List ) ) ;
if ( n != NULL ) {
n->data = x ;
n->next = sp ;
sp = n ;
}
}
int pop() {
struct List* d = sp ;
int ans = sp->data ;
sp = sp->next ;
free( d ) ;
return ans ;
}

認証評価自己点検書完成

今年は福井高専は認証評価とJABEEの審査の重なる年。ひとまず、認証評価自己点検書が完成、印刷して送付するんだけど、ただいま印刷の落丁の確認と印刷の変なところの差し替えで、疲れた…^_^;

1206272037_640x618.jpg

絶対PATH/相対PATH演習+C言語のファイル

今週は公開授業週間ということで、私のプログラミング応用も見学を受けた。 同じ学科の先生だし妙な緊張もなく、いつもどおり。
# 気合が少し入ったのか声の大きさは20%増しだったかも。

先週の絶対/相対PATHの演習ということで、 最初にコマンドラインでのファイル・ディレクトリ生成・削除を説明し、 配布資料に説明した、ディレクトリ構造をコマンドラインで生成する演習。

cd PATH
dir PATH
type PATH
mkdir PATH
rmdir PATH
echo DATA > PATH
del PATH

C言語のファイル

ファイルに関連する話ということで、 最初にFILE型を説明し、fopen,fclose,fscanf,fprint などを解説。

fopenの説明では、テキストモード・バイナリモードなども交えて解説する。 第一引数のファイル指定では、Windowsのディレクトリ区切り文字に 対する注意点なども交えて解説を行う。

FILE* fp ;
if( (fp = fopen( "¥¥Windows¥¥PATH¥¥abc.txt" "rt" )) != NULL ) {
int x ;
while( fscanf( fp , "%d" , &x ) == 1 ) {
printf( "%d" , x ) ;
}
fclose( fp ) ;
}

公開授業週間

今週は公開授業週間という、他の先生の授業を見学することで 自分の授業方法の改善につなげよう….という授業見学を行う週。 私自身も、ベテランの先生と若手の先生の授業を見学させてもらった。 他の方の見学があると、なかなか緊張し、 私自身が教員としての最初の授業で、 手が微妙に震えていたことを思い出す。

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー