Vim と Emacs の設定ファイル
どうも,筆者です.
個人的に使っている Vim と Emacs の設定ファイルを載せておく.
Vim
" === 基本設定 === " グループの初期化 augroup myvimauto autocmd! augroup END syntax on " 文字コードを UTF-8 に設定 set fenc=utf-8 " 入力中のコマンドをステータスに表示 set showcmd " 保存時に行末のスペースを削除 autocmd myvimauto BufWritePre * %s/\s\+$//e " 色の設定 highlight StatusLine ctermbg=none ctermfg=grey highlight Comment ctermfg=LightGreen highlight Constant ctermfg=DarkRed highlight Identifier ctermfg=Cyan highlight Statement ctermfg=Brown highlight SpecialKey ctermbg=none ctermfg=darkgray highlight Directory ctermfg=grey " 入力モードに入ったら,その行を太字に, " 入力モードから出ると underline にするように設定 autocmd myvimauto VimEnter,ColorScheme * highlight CursorLine cterm=underline autocmd myvimauto InsertEnter * highlight CursorLine cterm=bold autocmd myvimauto InsertLeave * highlight CursorLine cterm=underline " === 見た目系 === " 行番号を表示 set number " ルーラーの表示 set ruler " 現在の行を強調表示 set cursorline " スマートインデント set smartindent " オートインデント set autoindent " スマートタブ set smarttab " 括弧入力時に対応する括弧を表示 set showmatch " ステータスラインを常に表示 set laststatus=2 " コマンドラインの高さ set cmdheight=1 " コマンドラインの補完 set wildmode=list:longest " 折り返し時に表示業単位で移動 nnoremap j gj nnoremap k gk nnoremap <Down> gj nnoremap <Up> gk nnoremap $ g$ nnoremap ^ g^ nnoremap <Home> g^ nnoremap <End> g$ nnoremap gj j nnoremap gk k nnoremap g$ $ nnoremap g^ ^ " 行末スペースをハイライト autocmd myvimauto VimEnter,WinEnter * match Error /\s\+$/ " ファイル名の表示 set statusline=%<%t\ %m%r%h%w\ [position=%04l,%04v][%p%%]\ [length=%L]\ %{'['.(&fenc!=''?&fenc:&enc).']\ ['.&fileformat.']'} " === TAB 系 === " 不可視文字を可視化 set list listchars=tab:>~ set backspace=indent,eol,start " 行頭以外の TAB の表示幅 set tabstop=4 " 行頭での TAB の表示幅 set shiftwidth=4 set softtabstop=4 " === 検索系 === " 検索文字列が小文字の場合は,大文字小文字を区別なく検索 set ignorecase " 検索文字に大文字が含まれている場合は区別して検索 set smartcase " 文字列入力時に,順次対象文字列にヒットさせる set incsearch " 検索時に最後まで行ったら最初に戻る set wrapscan " 検索語をハイライト set hlsearch " コマンドラインの補完をするときに強化されたものを使う set wildmenu " ESC 連打でハイライト解除 nmap <Esc><Esc> :nohlsearch<CR><Esc>
Emacs
;; Emacs-Lisp (require 'cl) ;;------------------------------------ ;; 標準機能の設定 ;;------------------------------------ ;; Welcome message の非表示 (setq inhibit-startup-message t) ;; TAB の表示幅 (setq default-tab-width 4) ;; インデントのオフセット (setq c-basic-offset 4) ;; メニューバーにファイルのフルパスを表示 (setq frame-title-format (format "%%f")) ;; 対応する括弧を表示 (show-paren-mode 1) ;; 現在の行に色を付ける (global-hl-line-mode t) ;; 行番号,列番号を表示 (line-number-mode t) (column-number-mode t) ;; 行番号表示 (require 'linum) (global-linum-mode t) ;; スクロールは 1 行ずつ (setq scroll-step 1) ;; 行末の空白を強調表示 (setq-default show-trailing-whitespace t) (set-face-background 'trailing-whitespace "#b14770") ;; 行末の空白の自動削除 (add-hook 'before-save-hook 'delete-trailing-whitespace) ;; 予約語の色分け (global-font-lock-mode t) ;;------------------------------------ ;; ファイル関連の設定 ;;------------------------------------ ;; バックアップファイル作成の無効化 (setq make-backup-files nil) ;; 自動保存ファイル作成の無効化 (setq auto-save-default nil) ;;------------------------------------ ;; 補完の設定 ;;------------------------------------ ;; 補完時に大文字小文字を区別しない (setq completion-ignore-case t) (setq read-file-name-completion-ignore-case t) ;; 補完可能なものを随時表示 (icomplete-mode 1) ;;------------------------------------ ;; インデントの一括修正 ;;------------------------------------ (defun all-indent () (interactive) (save-excursion (indent-region (point-min) (point-max)) ) ) (defun electric-indent () "Indent specified region. When resion is active, indent region. Otherwise indent whole buffer." (interactive) (if (use-region-p) (indent-region (region-beginning) (region-end)) (all-indent) ) ) ;; インデントの一括修正を行うショートカット(Ctrl + Alt + ]) (global-set-key (kbd "C-M-]") 'electric-indent)
C 言語によるトークン取り出し
どうも,筆者です.
今回は,区切り文字を指定し,文字列を取り出す関数を作成した.ただ単に取り出すだけなら,標準関数の token を使えばよい.しかし,これは,データを破壊しつつ処理を行う.また,区切り文字だけを取り出すことができない. そこで,token を自作することにした.ただ,構成上,余分にメモリが必要となる.致し方なし.
構成
ここでは,以下の項目を満たすものを考える.
- 文字列を指定した区切り文字で区切り,取り出す.
- 区切り文字も 1 文字として取り出す.
- 入力文字は小文字に変換する.
- 入力する文字は,以下の 4 パターン(処理確認用)
- 2 進数・・・ b から始まる 0,1 からなる文字列
- 8 進数・・・ 0 から始まる 0 ~ 7 からなる文字列
- 10 進数・・・ 0 のみ,または 0 以外から始まる 0 ~ 9 からなる文字列
- 16 進数・・・ 0x から始まる 0 ~ 9,a ~ f からなる文字列
作成したモジュールの機能一覧
ここでは,以下の 5 つのモジュールを作成した.それぞれのモジュールの働きを示す.
- checkNum
- 働き:数値処理
- 静的領域に確保されている文字列データを初期化する.
- 静的領域に確保されている文字列データに対象とする文字列を設定する.
- 2 進数かどうかを判定する.
- 8 進数かどうかを判定する.
- 10 進数かどうかを判定する.
- 16 進数かどうかを判定する.
- conversion
- 働き:文字列変換処理
- 指定した範囲にデータが存在するかどうかを判定する.
- 大文字を小文字に変換する.
- 文字列を 10 進数数値に変換する.
- 10 進数数値を文字列に変換する.
- splitStr
- 働き:文字列加工処理
- 静的領域に確保された構造体の要素を初期化する.
- 誓敵領域に確保された構造体の要素である,探索文字列と区切り文字列を設定する.
- 文字列を分割する.
- stack
- 働き:スタック制御処理
- スタック領域を初期化する.
- データをスタックに格納する.
- スタックからデータを取り出す.
- token
- 働き:文字列分割処理
- 文字列を区切り文字で分割する.
ヘッダー
以下に,実際に使用するヘッダーを示す.
共通ヘッダー
/* typedef.h */ #ifndef __TYPEDEF_H__ #define __TYPEDEF_H__ #include<stddef.h> typedef void VD; typedef unsigned char U1; typedef signed char I1; typedef unsigned short int U2; typedef signed short int I2; typedef unsigned long int U4; typedef signed long int I4; typedef char CHR; typedef char * STR; typedef int INTEGER; typedef enum { FALSE = 0, TRUE = 1 } BOOLEAN; /* 2 進数は,b010111... のように b から始まり 0,1 から成る文字列 8 進数は,0123723... のように 0 から始まり 0~7 から成る文字列 10 進数は,5387119... のように 数値 0~9 から成る文字列 16 進数は,0x456ba... のように 0x から始まり 0~9,a~f から成る文字列 */ #define MAX_STRING (128) /* 最大文字数 */ #define NULLSTR ('\0') /* NULL 文字の定義 */ #define HEX_SIMBOL ('x') /* 16 進数の識別子 */ #define BIN_SIMBOL ('b') /* 2 進数の識別子 */ #define PLUS_SIGN ('+') /* プラスの識別子 */ #define MINUS_SIGN ('-') /* マイナスの識別子 */ #define INVBASENUM (0) /* 基底の無効値 */ #define BINARY_NUM (2) /* 基底 2 */ #define OCTAL_NUM (8) /* 基底 8 */ #define DECIMALNUM (10) /* 基底 10 */ #define HEXADCMNUM (16) /* 基底 16 */ #define POSITIVESIGN (1) /* 正の符号 */ #define NEGATIVESIGN (-1) /* 負の符号 */ #endif
数値処理ヘッダー
/* checkNum.h */ #ifndef __CHECKNUM_H__ #define __CHECKNUM_H__ #include"typedef.h" #define MIN_NUM ('0') /* 数値の最小値 */ #define BIN_MAX_NUM ('1') /* 2 進数の最大値 */ #define MAX_NUM ('9') /* 数値の最大値 */ #define OCT_MAX_NUM ('7') /* 8 進数の最大値 */ #define MIN_ASCII ('a') /* 16 進数の文字 */ #define MAX_ASCII ('f') /* 16 進数の文字 */ /* 初期化関数 VD vdCheckStrInit(VD); */ VD vdCheckStrInit(VD); /* 文字列の設定 VD vdSetString(STR szStr); (a1i) szStr: 探索対象の文字列 */ VD vdSetString(STR szStr); /* 2 進数であるかどうかを判定 BOOLEAN bIsBinary(VD); return: TRUE -> 2 進数である,FALSE -> 2 進数ではない */ BOOLEAN bIsBinary(VD); /* 8 進数であるかどうかを判定 BOOLEAN bIsOctNum(VD); return: TRUE -> 8 進数である,FALSE -> 8 進数ではない */ BOOLEAN bIsOctNum(VD); /* 10 進数であるかどうかを判定 BOOLEAN bIsDcmNum(VD); return: TRUE -> 10 進数である,FALSE -> 10 進数ではない */ BOOLEAN bIsDcmNum(VD); /* 16 進数であるかどうかを判定 BOOLEAN bIsHexNum(VD); return: TRUE -> 16 進数である,FALSE -> 16 進数ではない */ BOOLEAN bIsHexNum(VD); #endif
文字列変換処理
/* conversion.h */ #ifndef __CONVERSION_H__ #define __CONVERSION_H__ #include"typedef.h" /* 指定した範囲にデータが存在するかどうかを確認する関数 BOOLEAN bExistRange(INTEGER iTargetVal, INTEGER iLowerVal, INTEGER iUpperVal); (a1i) iTargetVal: 対象とする値 (a2i) iLowerVal: 下限値 (a3i) iUpperVal: 上限値 return: TRUE -> 範囲内,FALSE -> 範囲外 */ BOOLEAN bExistRange(INTEGER iTargetVal, INTEGER iLowerVal, INTEGER iUpperVal); /* 大文字を小文字に変換 CHR chCapital2Small(CHR chData); (a1i) chData: 対象文字 return: 変換結果 */ CHR chCapital2Small(CHR chData); /* 10 進数変換 I4 i4ToDecimal(U1 u1StrLen, STR szStr, U1 u1Basis); (a1i) u1StrLen: 入力文字列の長さ (a2i) szStr: 入力文字列 (a3i) u1Basis: 基底 return: 10 進数の値 */ I4 i4ToDecimal(U1 u1StrLen, STR szStr, U1 u1Basis); /* 10 進数から変換 VD vdFromDecimal(I4 i4Data, U1 u1Basis, U1 *pu1StrLen, STR szStr); (a1i) i4Data: 変換前のデータ (a2i) u1Basis: 基底 (a3o) pu1StrLen: 出力文字列の長さ (a4o) szStr: 出力文字列 */ VD vdFromDecimal(I4 i4Data, U1 u1Basis, U1 *pu1StrLen, STR szStr); #endif
文字列加工処理
/* splitStr.h */ #ifndef __SPLITSTR_H__ #define __SPLITSTR_H__ #include"typedef.h" /* 初期化関数 VD vdSplitStrInit(VD); */ VD vdSplitStrInit(VD); /* 対象文字列と区切り文字列の設定 VD vdSetStrDel(STR szTargetStr, U1 u1NumOfDelimiter, STR szDelimiterList); (a1i) szTargetStr: 対象文字列 (a2i) u1NumOfDelimiter: 区切り文字の数 (a3i) szDelimiterList: 区切り文字のリスト */ VD vdSetStrDel(STR szTargetStr, U1 u1NumOfDelimiter, STR szDelimiterList); /* 文字列分割関数 BOOLEAN bSplitString(STR szSplitStr, U1 *pu1SplitStrLen, BOOLEAN bVartualExecution); (a1o) szSplitStr: 分割文字列 (a2o) *pu1SplitStrLen: 分割文字列の長さ (a3i) bVartualExecution: TRUE -> 仮想実行,FALSE -> 実際に実行 return: TRUE -> 文字列の終端に達していない FALSE -> 文字列を最後まで読んだ */ BOOLEAN bSplitString(STR szSplitStr, U1 *pu1SplitStrLen, BOOLEAN bVartualExecution); #endif
スタック制御処理
/* stack.h */ #ifndef __STACK_H__ #define __STACK_H__ #include"typedef.h" #define MAX_STACK_SIZE (100) /* スタックの初期化関数 VD vdInitStack(VD); */ VD vdInitStack(VD); /* 格納関数 BOOLEAN bPush(I2 i2Data); (a1i) i2Data: 格納する値 return: TRUE -> 成功,FALSE -> 失敗 */ BOOLEAN bPush(I2 i2Data); /* 取り出し関数 BOOLEAN bPop(I2 *pi2Val); (aio) pi2Val: 取り出す値 return: TRUE -> 成功,FALSE -> 失敗 */ BOOLEAN bPop(I2 *pi2Val); #endif
文字列分割処理
/* token.h */ #ifndef __TOKEN_H__ #define __TOKEN_H__ #include"typedef.h" /* トークン分割する関数 VD vdToken(STR szStr, U1 u1NumOfDelimiters, STR szDelimiterList) (a1i) szStr: 対象文字列 (a2i) u1NumOfDelimiters: 区切り文字数 (a3i) szDelimiterList: 区切り文字 */ VD vdToken(STR szStr, U1 u1NumOfDelimiters, STR szDelimiterList); #endif
ソースファイル
以下に,作成した C ファイルを示す.
check.c
#include"typedef.h" #include"checkNum.h" /* 計算対象の文字列 */ static CHR st_szString[MAX_STRING + 1]; /* 文字列の長さ */ static U1 st_u1StrLen; /* プロトタイプ宣言(conversion.h) */ extern BOOLEAN bExistRange(INTEGER iTargetVal, INTEGER iLowerVal, INTEGER iUpperVal); extern CHR chCapital2Small(CHR chData); /* 初期化関数 VD vdCheckStrInit(VD); */ VD vdCheckStrInit(VD) { U1 u1Cnt; st_u1StrLen = 0; for ( u1Cnt = 0 ; u1Cnt <= (U1)MAX_STRING ; u1Cnt++ ) { st_szString[u1Cnt] = (CHR)NULLSTR; } return; } /* 文字列の設定 VD vdSetString(STR szStr); (a1i) szStr: 探索対象の文字列 */ VD vdSetString(STR szStr) { U1 u1Cnt = 0; if ( NULL != szStr ) { /* ループを抜ける条件:終端文字に到達する,もしくは,カウントが配列サイズを超える */ while ( (u1Cnt < (U1)MAX_STRING) && ((CHR)NULLSTR != szStr[u1Cnt]) ) { st_szString[u1Cnt] = chCapital2Small(szStr[u1Cnt]); u1Cnt++; } } /* 終端文字を代入 */ st_szString[u1Cnt] = (CHR)NULLSTR; /* 文字列の長さを保存 */ st_u1StrLen = u1Cnt; return; } /* 2 進数であるかどうかを判定 BOOLEAN bIsBinary(VD); return: TRUE -> 2 進数である,FALSE -> 2 進数ではない */ BOOLEAN bIsBinary(VD) { BOOLEAN bRetVal; U1 u1Cnt; /* 先頭が 2 進数のシンボルでない,若しくは 1 文字だけの場合 */ if ( ((CHR)BIN_SIMBOL != st_szString[0]) || (st_u1StrLen < 2) ) { bRetVal = FALSE; } else { bRetVal = TRUE; for ( u1Cnt = 1 ; u1Cnt < st_u1StrLen ; u1Cnt++ ) { /* 0,1 のデータでない場合 */ if ( FALSE == bExistRange((INTEGER)st_szString[u1Cnt], (INTEGER)MIN_NUM, (INTEGER)BIN_MAX_NUM) ) { bRetVal = FALSE; break; } } } return bRetVal; } /* 8 進数であるかどうかを判定 BOOLEAN bIsOctNum(VD); return: TRUE -> 8 進数である,FALSE -> 8 進数ではない */ BOOLEAN bIsOctNum(VD) { BOOLEAN bRetVal; U1 u1Cnt; /* 先頭が 0 ではない,もしくは 1 文字もない場合 */ if ( ((CHR)MIN_NUM != st_szString[0]) || (st_u1StrLen < 1) ) { bRetVal = FALSE; } /* 0 だけの場合 */ else if ( (CHR)NULLSTR == st_szString[1] ) { bRetVal = FALSE; } /* 8 進数の可能性がある場合 */ else { bRetVal = TRUE; for ( u1Cnt = 1 ; u1Cnt < st_u1StrLen ; u1Cnt++ ) { /* 0~7 の数値でない場合 */ if (FALSE == bExistRange((INTEGER)st_szString[u1Cnt], (INTEGER)MIN_NUM, (INTEGER)OCT_MAX_NUM)) { bRetVal = FALSE; break; } } } return bRetVal; } /* 10 進数であるかどうかを判定 BOOLEAN bIsDcmNum(VD); return: TRUE -> 10 進数である,FALSE -> 10 進数ではない */ BOOLEAN bIsDcmNum(VD) { BOOLEAN bRetVal; U1 u1Cnt; /* 1 文字もない場合 */ if ( st_u1StrLen < 1 ) { bRetVal = FALSE; } else { bRetVal = TRUE; for ( u1Cnt = 0 ; u1Cnt < st_u1StrLen ; u1Cnt++ ) { /* 0~9 の数値でない場合 */ if (FALSE == bExistRange((INTEGER)st_szString[u1Cnt], (INTEGER)MIN_NUM, (INTEGER)MAX_NUM)) { bRetVal = FALSE; break; } } } return bRetVal; } /* 16 進数であるかどうかを判定 BOOLEAN bIsHexNum(VD); return: TRUE -> 16 進数である,FALSE -> 16 進数ではない */ BOOLEAN bIsHexNum(VD) { BOOLEAN bRetVal; U1 u1Cnt; /* 3 文字未満の場合 */ if ( st_u1StrLen < 3 ) { bRetVal = FALSE; } /* 先頭 2 文字が 0x 出ない場合 */ else if ( ((CHR)MIN_NUM != st_szString[0]) && ((CHR)HEX_SIMBOL != st_szString[1])) { bRetVal = FALSE; } else { /* 16 進数の可能性がある場合 */ bRetVal = TRUE; for ( u1Cnt = 2 ; u1Cnt < st_u1StrLen ; u1Cnt++ ) { /* 0~9,a~f の数値でない場合 */ if ( (FALSE == bExistRange((INTEGER)st_szString[u1Cnt], (INTEGER)MIN_NUM, (INTEGER)MAX_NUM)) && (FALSE == bExistRange((INTEGER)st_szString[u1Cnt], (INTEGER)MIN_ASCII, (INTEGER)MAX_ASCII)) ) { bRetVal = FALSE; break; } } } return bRetVal; }
conversion.c
#include"typedef.h" #include"conversion.h" #define SIMBOL_ZERO ('0') #define SIMBOL_A ('A') #define SIMBOL_Z ('Z') #define NUM_OF_DATALIST (16) static const CHR g_stc_szDataList[NUM_OF_DATALIST] = "0123456789abcdef"; /* 指定した範囲にデータが存在するかどうかを確認する関数 BOOLEAN bExistRange(INTEGER iTargetVal, INTEGER iLowerVal, INTEGER iUpperVal); (a1i) iTargetVal: 対象とする値 (a2i) iLowerVal: 下限値 (a3i) iUpperVal: 上限値 return: TRUE -> 範囲内,FALSE -> 範囲外 */ BOOLEAN bExistRange(INTEGER iTargetVal, INTEGER iLowerVal, INTEGER iUpperVal) { BOOLEAN bRetVal; if ( (iLowerVal <= iTargetVal) && (iTargetVal <= iUpperVal) ) { /* 範囲内 */ bRetVal = TRUE; } else { /* 範囲外 */ bRetVal = FALSE; } return bRetVal; } /* 大文字を小文字に変換 CHR chCapital2Small(CHR chData); (a1i) chData: 対象文字 return: 変換結果 */ CHR chCapital2Small(CHR chData) { CHR chOutData; if ( TRUE == bExistRange((INTEGER)chData, (INTEGER)SIMBOL_A, (INTEGER)SIMBOL_Z) ) { chOutData = (CHR)((INTEGER)chData + 32); } else { chOutData = chData; } return chOutData; } /* 10 進数変換 I4 i4ToDecimal(U1 u1StrLen, STR szStr, U1 u1Basis); (a1i) u1StrLen: 入力文字列の長さ (a2i) szStr: 入力文字列 (a3i) u1Basis: 基底 return: 10 進数の値 */ I4 i4ToDecimal(U1 u1StrLen, STR szStr, U1 u1Basis) { I4 i4RetVal = 0; U1 u1Cnt, u1Data; if ( (NULL != szStr) && (u1Basis <= (U1)NUM_OF_DATALIST) ) { for ( u1Cnt = 0 ; u1Cnt < u1StrLen ; u1Cnt++ ) { i4RetVal *= (I4)u1Basis; /* 対応する数値をリストから探索 */ for ( u1Data = 0 ; u1Data < NUM_OF_DATALIST ; u1Data++ ) { if ( g_stc_szDataList[u1Data] == szStr[u1Cnt] ) { break; } } i4RetVal += (I4)u1Data; } } return i4RetVal; } /* 10 進数から変換 VD vdFromDecimal(I4 i4Data, U1 u1Basis, U1 *pu1StrLen, STR szStr); (a1i) i4Data: 変換前のデータ (a2i) u1Basis: 基底 (a3o) pu1StrLen: 出力文字列の長さ (a4o) szStr: 出力文字列 */ VD vdFromDecimal(I4 i4Data, U1 u1Basis, U1 *pu1StrLen, STR szStr) { U1 u1LoopCnt; I1 i1Sign, i1Cnt; CHR szReverseStr[MAX_STRING + 1]; U4 u4CalcData; /* 負の数 */ if ( i4Data < 0 ) { u4CalcData = (U4)(-i4Data); i1Sign = (I1)NEGATIVESIGN; } /* 正の数 */ else { u4CalcData = (U4)i4Data; i1Sign = (I1)POSITIVESIGN; } u1LoopCnt = 0; if ( (NULL != pu1StrLen) && (NULL != szStr) ) { if ( ((U1)INVBASENUM < u1Basis) && (u1Basis <= (U1)NUM_OF_DATALIST) ) { /* 余りをインデックスとし,データを格納する */ i1Cnt = 0; while ( u4CalcData > 0 ) { szReverseStr[i1Cnt++] = g_stc_szDataList[u4CalcData % (U4)u1Basis]; u4CalcData /= (U4)u1Basis; } if ( (I1)NEGATIVESIGN == i1Sign ) { szStr[u1LoopCnt++] = (CHR)MINUS_SIGN; } /* シンボルの付加 */ switch ( u1Basis ) { /* 2 進数の場合 */ case BINARY_NUM: szStr[u1LoopCnt++] = (CHR)BIN_SIMBOL; break; /* 8 進数の場合 */ case OCTAL_NUM: szStr[u1LoopCnt++] = (CHR)SIMBOL_ZERO; break; /* 16 進数の場合 */ case HEXADCMNUM: szStr[u1LoopCnt++] = (CHR)SIMBOL_ZERO; szStr[u1LoopCnt++] = (CHR)HEX_SIMBOL; break; /* その他の場合 */ default: break; } /* 逆順のデータを順に格納していく */ while ( i1Cnt > 0 ) { szStr[u1LoopCnt++] = szReverseStr[--i1Cnt]; } } szStr[u1LoopCnt] = (CHR)NULLSTR; *pu1StrLen = u1LoopCnt; } return; }
splitStr.c
#include"typedef.h" #include"splitStr.h" typedef struct __split_param_t { U1 u1CurrentPos; /* 現在の文字列の位置 */ U1 u1StrLen; /* 文字列の長さ */ U1 u1NumOfDelimiter; /* 区切り文字の数 */ CHR szTargetStr[MAX_STRING + 2]; /* 対象とする文字列 */ CHR szDelimiterList[MAX_STRING]; /* 区切り文字のリスト */ } SPLIT_PARAM; static SPLIT_PARAM st_rSplitParam; /* 初期化関数 VD vdSplitStrInit(VD); */ VD vdSplitStrInit(VD) { U1 u1Cnt; st_rSplitParam.u1CurrentPos = 0; st_rSplitParam.u1StrLen = 0; st_rSplitParam.u1NumOfDelimiter = 0; for ( u1Cnt = 0 ; u1Cnt < (U1)MAX_STRING ; u1Cnt++ ) { st_rSplitParam.szTargetStr[u1Cnt] = (CHR)NULLSTR; st_rSplitParam.szDelimiterList[u1Cnt] = (CHR)NULLSTR; } st_rSplitParam.szTargetStr[MAX_STRING] = (CHR)NULLSTR; return; } /* 対象文字列と区切り文字列の設定 VD vdSetStrDel(STR szTargetStr, U1 u1NumOfDelimiter, STR szDelimiterList); (a1i) szTargetStr: 対象文字列 (a2i) u1NumOfDelimiter: 区切り文字の数 (a3i) szDelimiterList: 区切り文字のリスト */ VD vdSetStrDel(STR szTargetStr, U1 u1NumOfDelimiter, STR szDelimiterList) { U1 u1Cnt; u1Cnt = 0; /* 対象文字列の設定 */ if ( NULL != szTargetStr ) { while ( (u1Cnt < (U1)MAX_STRING) && ((CHR)NULLSTR != szTargetStr[u1Cnt]) ) { st_rSplitParam.szTargetStr[u1Cnt] = szTargetStr[u1Cnt]; u1Cnt++; } } st_rSplitParam.u1CurrentPos = 0; st_rSplitParam.u1StrLen = u1Cnt + 1; /* 区切り文字の設定 */ if ( (NULL != szDelimiterList) && (0 < u1NumOfDelimiter) ) { st_rSplitParam.u1NumOfDelimiter = u1NumOfDelimiter; for ( u1Cnt = 0 ; (u1Cnt < u1NumOfDelimiter) && (u1Cnt < (U1)MAX_STRING) ; u1Cnt++ ) { st_rSplitParam.szDelimiterList[u1Cnt] = szDelimiterList[u1Cnt]; } u1Cnt = st_rSplitParam.u1StrLen - 1; st_rSplitParam.szTargetStr[u1Cnt++] = szDelimiterList[0]; st_rSplitParam.szTargetStr[u1Cnt] = szDelimiterList[0]; } else { st_rSplitParam.u1NumOfDelimiter = 0; st_rSplitParam.szDelimiterList[0] = (CHR)NULLSTR; u1Cnt = st_rSplitParam.u1StrLen - 1; st_rSplitParam.szTargetStr[u1Cnt++] = (CHR)NULLSTR; st_rSplitParam.szTargetStr[u1Cnt] = (CHR)NULLSTR; } return; } /* 文字列分割関数 BOOLEAN bSplitString(STR szSplitStr, U1 *pu1SplitStrLen, BOOLEAN bVartualExecution); (a1o) szSplitStr: 分割文字列 (a2o) *pu1SplitStrLen: 分割文字列の長さ (a3i) bVartualExecution: TRUE -> 仮想実行,FALSE -> 実際に実行 return: TRUE -> 文字列の終端に達していない FALSE -> 文字列を最後まで読んだ */ BOOLEAN bSplitString(STR szSplitStr, U1 *pu1SplitStrLen, BOOLEAN bVartualExecution) { BOOLEAN bExeFlag; /* 実行フラグ */ BOOLEAN bRetVal; /* 戻り値 */ U1 u1DelCnt; /* 区切り文字のカウント */ U1 u1MaxCntOfList; /* 区切り文字の数 */ U1 u1StrPos; /* 文字列の位置 */ U1 u1StrLen; /* 文字列の長さ */ CHR chData; /* 対象とする文字列 */ u1StrPos = st_rSplitParam.u1CurrentPos; u1MaxCntOfList = st_rSplitParam.u1NumOfDelimiter; u1StrLen = st_rSplitParam.u1StrLen; bExeFlag = TRUE; if ( (NULL == szSplitStr) || (NULL == pu1SplitStrLen) ) { bRetVal = FALSE; } else { *pu1SplitStrLen = 0; /* 文字列の終端に到達する,若しくはフラグが降りるまでループ */ while ( (u1StrPos < u1StrLen) && (TRUE == bExeFlag) ) { chData = st_rSplitParam.szTargetStr[u1StrPos]; szSplitStr[*pu1SplitStrLen] = chData; (*pu1SplitStrLen)++; /* 今見ている文字が区切り文字かどうか確認 */ for ( u1DelCnt = 0 ; u1DelCnt < u1MaxCntOfList ; u1DelCnt++ ) { /* 区切り文字と一致した場合 */ if ( chData == st_rSplitParam.szDelimiterList[u1DelCnt] ) { bExeFlag = FALSE; break; } } u1StrPos++; } /* 上のループに入った場合 */ if ( 0 < (*pu1SplitStrLen) ) { bRetVal = TRUE; /* 区切り文字以外がある場合 */ if ( 1 < (*pu1SplitStrLen) ) { (*pu1SplitStrLen)--; u1StrPos--; } } /* 終了条件に達した場合 */ else { bRetVal = FALSE; } szSplitStr[*pu1SplitStrLen] = (CHR)NULLSTR; /* 仮想実行の場合は,次のトークンに移動しない */ if ( FALSE == bVartualExecution ) { st_rSplitParam.u1CurrentPos = u1StrPos; } } return bRetVal; }
stack.c
#include"typedef.h" #include"stack.h" static I2 st_i2Stack[MAX_STACK_SIZE]; static I2 st_i2IdxOfStack; /* スタックの初期化関数 VD vdInitStack(VD); */ VD vdInitStack(VD) { st_i2IdxOfStack = 0; return; } /* 格納関数 BOOLEAN bPush(I2 i2Data); (a1i) i2Data: 格納する値 return: TRUE -> 成功,FALSE -> 失敗 */ BOOLEAN bPush(I2 i2Data) { BOOLEAN bRetVal; /* スタックサイズを超えている場合 */ if ( st_i2IdxOfStack >= (I2)MAX_STACK_SIZE ) { bRetVal = FALSE; } else { st_i2Stack[st_i2IdxOfStack++] = i2Data; bRetVal = TRUE; } return bRetVal; } /* 取り出し関数 BOOLEAN bPop(I2 *pi2Val); (aio) pi2Val: 取り出す値 return: TRUE -> 成功,FALSE -> 失敗 */ BOOLEAN bPop(I2 *pi2Val) { BOOLEAN bRetVal; /* スタックが空の場合 */ if ( (st_i2IdxOfStack <= 0) || (NULL == pi2Val) ) { bRetVal = FALSE; } else { *pi2Val = st_i2Stack[--st_i2IdxOfStack]; bRetVal = TRUE; } return bRetVal; }
token.c
#include"typedef.h" #include"checkNum.h" #include"conversion.h" #include"splitStr.h" #include"token.h" #include"stack.h" #define I2_MINVAL (-32768) #define I2_MAxVAL (32767) /* 符号の確認 I1 i1ChkSign(STR *pszStr, U1 *pu1StrLen); (a1io) pszStr: 文字列の先頭アドレスへのポインタ (a2o) pu1StrLen: 文字列の長さ return: PLUS_SIGN -> POSITIVESIGN,MINUS_SIGN -> NEGATIVESIGN */ I1 i1ChkSign(STR *pszStr, U1 *pu1StrLen); /* 基底の取得 VD vdGetBaseNumber(U1 *pu1Basis, U1 *pu1Idx); (a1o) pu1Basis: 基底 (a2o) pu1Idx: 計算対象の文字列のインデックス */ VD vdGetBaseNumber(U1 *pu1Basis, U1 *pu1Idx); /* トークン分割する関数 VD vdToken(STR szStr, U1 u1NumOfDelimiters, STR szDelimiterList); (a1i) szStr: 対象文字列 (a2i) u1NumOfDelimiters: 区切り文字数 (a3i) szDelimiterList: 区切り文字 */ VD vdToken(STR szStr, U1 u1NumOfDelimiters, STR szDelimiterList) { /* 区切り文字の設定 */ CHR szSplitStr[MAX_STRING + 1]; STR pszSplitData; U1 u1SplitStrLen, u1Basis, u1StartIdx; I1 i1Sign; I4 i4Data; if ( (NULL != szStr) && (NULL != szDelimiterList) ) { /* 文字列分割の初期化 */ vdSplitStrInit(); /* 文字列の設定 */ vdSetStrDel(szStr, (U1)u1NumOfDelimiters, szDelimiterList); /* 対象文字列の終端に到達するまでループ */ while ( TRUE == bSplitString(szSplitStr, &u1SplitStrLen, FALSE) ) { /* 符号の決定 */ pszSplitData = &szSplitStr[0]; i1Sign = i1ChkSign(&pszSplitData, &u1SplitStrLen); /* 数値チェックの初期化 */ vdCheckStrInit(); /* 文字列の設定 */ vdSetString(pszSplitData); /* 基底を得る */ vdGetBaseNumber(&u1Basis, &u1StartIdx); if ( u1Basis > 0 ) { i4Data = (I4)i1Sign * i4ToDecimal(u1SplitStrLen - u1StartIdx, &pszSplitData[u1StartIdx], u1Basis); if ( ((I4)I2_MINVAL < i4Data) && (i4Data < (I4)I2_MAxVAL) ) { (VD)bPush((I2)i4Data); } } } } return; } /* 符号の確認 I1 i1ChkSign(STR *pszStr, U1 *pu1StrLen); (a1io) pszStr: 文字列の先頭アドレスへのポインタ (a2o) pu1StrLen: 文字列の長さ return: PLUS_SIGN -> POSITIVESIGN,MINUS_SIGN -> NEGATIVESIGN */ I1 i1ChkSign(STR *pszStr, U1 *pu1StrLen) { I1 i1Sign = 0; if ( (NULL != pszStr) && (NULL != (*pszStr)) && (NULL != pu1StrLen) ) { switch ( **pszStr ) { /* プラスの場合 */ case PLUS_SIGN: i1Sign = (I1)POSITIVESIGN; (*pszStr)++; (*pu1StrLen)--; break; /* マイナスの場合 */ case MINUS_SIGN: i1Sign = (I1)NEGATIVESIGN; (*pszStr)++; (*pu1StrLen)--; break; /* 符号がない場合 */ default: i1Sign = (I1)POSITIVESIGN; break; } } return i1Sign; } /* 基底の取得 VD vdGetBaseNumber(U1 *pu1Basis, U1 *pu1Idx); (a1o) pu1Basis: 基底 (a2o) pu1Idx: 計算対象の文字列のインデックス */ VD vdGetBaseNumber(U1 *pu1Basis, U1 *pu1Idx) { *pu1Idx = 0; /* 2 進数の場合 */ if ( TRUE == bIsBinary() ) { *pu1Basis = (U1)BINARY_NUM; *pu1Idx = 1; } /* 8 進数の場合 */ else if ( TRUE == bIsOctNum() ) { *pu1Basis = (U1)OCTAL_NUM; } /* 10 進数の場合 */ else if ( TRUE == bIsDcmNum() ) { *pu1Basis = (U1)DECIMALNUM; } /* 16 進数の場合 */ else if ( TRUE == bIsHexNum() ) { *pu1Basis = (U1)HEXADCMNUM; *pu1Idx = 2; } /* 上記以外の場合 */ else { *pu1Basis = (U1)INVBASENUM; } return; }
最後に礼として作成した main 関数を示す.
#include<stdio.h> #include"typedef.h" #include"token.h" #include"stack.h" #define NUM_OF_DELIMITERS (4) VD vdStrnCpy(STR szOutputStr, const STR szInputStr, INTEGER iMaxNum); INTEGER main(INTEGER iArgCnt, STR szArgVec[]) { I2 i2Data; /* 入力文字列 */ CHR szInputStr[MAX_STRING + 1]; /* 区切り文字リスト */ CHR szDelimiterList[NUM_OF_DELIMITERS] = " ,:;"; if ( iArgCnt < 2 ) { szInputStr[0] = '1'; szInputStr[1] = '\0'; } else { vdStrnCpy(szInputStr, szArgVec[1], MAX_STRING); } vdToken(szInputStr, NUM_OF_DELIMITERS, szDelimiterList); while ( TRUE == bPop(&i2Data) ) { printf("%d\n", i2Data); } return 0; } VD vdStrnCpy(STR szOutputStr, const STR szInputStr, INTEGER iMaxNum) { INTEGER iCnt; CHR chData; for ( iCnt = 0 ; iCnt < iMaxNum - 1; iCnt++ ) { chData = szInputStr[iCnt]; szOutputStr[iCnt] = chData; if ( (CHR)NULLSTR == chData ) { break; } } szOutputStr[iCnt] = (CHR)NULLSTR; return; }
Makefile
CC := gcc CLIBS := -O2 -Wall -Wextra PROG := execute INCDIR := include SRCDIR := src OUTDIR := objdep SRCS := $(shell ls $(SRCDIR)) OBJS := $(SRCS:%.c=$(OUTDIR)/%.o) DEPS := $(SRCS:%.c=$(OUTDIR)/%.d) all: $(PROG) -include $(DEPS) $(PROG): $(OBJS) $(CC) -o $@ $^ $(OUTDIR)/%.o: $(SRCDIR)/%.c @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi $(CC) -o $@ -c -MP -MMD -MF $(@:%.o=%.d) $(CLIBS) -I$(INCDIR) $< .PHONY: clean clean: rm -f $(PROG) $(OBJS) $(DEPS)
PowerShell による GUI の自動化
どうも,筆者です.
今回は,Windows に搭載されている PowerShell を利用したスクリプトについて説明する.
経緯
あるソフトウェアで,データを呼び出し,スクショを撮って画像として保存する必要があった.2~3 枚なら手でやろうと思うけれども,200 枚,300 枚もあると流石に自動化したくなる.また,ソフトウェアは,Windows 版の GUI のものしかない.そのため,Windows の GUI を自動化する方法を探すこととなった.
UI Automation
調べたところ,Windows の PowerShell に,「UI Automation」を導入することで GUI の自動化ができるらしい.
早速,http://uiautomation.codeplex.com/ からダウンロードしてきた.保存場所は,「C:\winPowerShell\UIAutomation」というディレクトリを作成し,その中に展開した. そのままでは,PowerShell のスクリプトを書いても実行できないらしい.そのため,管理者権限で「Windows PowerShell ISE」を起動させ,以下のコマンドで実行権限を変える.
Set-ExecutionPolicy RemoteSigned
ちなみに,はてなブログでは,ps1 でコードが PowerShell と認識されるらしい.便利になったものだ.
やりたいこと
さて,実際にコードを書いていくことになる.しかし,普段 GUI で行っている操作をいきなりスクリプトに落とそうとしても無理がある.そのため,色々調べて回った. 調べたところ,先程ダウンロードした UI Automation には,UIAutomationSpy.exe というスクリプトを書く際の補助ツールなるものがあるらしい. このツールを借りて,作業を行う.ここでは,以下のような流れで処理を行う.
- データがあるディレクトリまでの絶対パスと参照するデータ名を設定する.
- 起動させたいソフトウェアを絶対パスで指定し,起動する.
- ソフトウェアから,上記で示したデータを参照し,開く.
- Alt + PrintScreen により,画面をクリップボードに保存する.
- mspaint を実行し,ペイントを起動し,Ctrl + V でクリップボードの画像を貼り付ける.
- Ctrl + S で保存する.この時,新規保存となるので,保存先のパスと保存するファイル名を指定する.
- これをデータの数だけ繰り返す.
スクリプトを書く
実際に調べながらコードを書いてみた.ます,最初に記述すべき項目として,以下のものがある.
Import-Module C:\winPowerShell\UIAutomation\UIAutomation.dll
これは,UI Automation を利用することを PowerShell 側に知らせるコードである.後は自由に書くだけだが,筆者のように何も知らない人は調べるところから始まる.今回は,キー操作がメインとなるので,キーの一覧を探した.使いそうなものを以下に示しておく.
<# Left mouse button LBUTTON = 0x01, Right mouse button RBUTTON = 0x02, Control-break processing CANCEL = 0x03, BACKSPACE key BACK = 0x08, TAB key TAB = 0x09, CLEAR key CLEAR = 0x0C, ENTER key RETURN = 0x0D, SHIFT key SHIFT = 0x10, CTRL key CONTROL = 0x11, ALT key MENU = 0x12, PAUSE key PAUSE = 0x13, CAPS LOCK key CAPITAL = 0x14, ESC key ESCAPE = 0x1B, SPACEBAR SPACE = 0x20, PAGE UP key PRIOR = 0x21, PAGE DOWN key NEXT = 0x22, END key END = 0x23, HOME key HOME = 0x24, LEFT ARROW key LEFT = 0x25, UP ARROW key UP = 0x26, RIGHT ARROW key RIGHT = 0x27, DOWN ARROW key DOWN = 0x28, SELECT key SELECT = 0x29, PRINT key PRINT = 0x2A, EXECUTE key EXECUTE = 0x2B, PRINT SCREEN key SNAPSHOT = 0x2C, INS key INSERT = 0x2D, DEL key DELETE = 0x2E, HELP key HELP = 0x2F, 0 key VK_0 = 0x30, 1 key VK_1 = 0x31, 2 key VK_2 = 0x32, 3 key VK_3 = 0x33, 4 key VK_4 = 0x34, 5 key VK_5 = 0x35, 6 key VK_6 = 0x36, 7 key VK_7 = 0x37, 8 key VK_8 = 0x38, 9 key VK_9 = 0x39, A key VK_A = 0x41, B key VK_B = 0x42, C key VK_C = 0x43, D key VK_D = 0x44, E key VK_E = 0x45, F key VK_F = 0x46, G key VK_G = 0x47, H key VK_H = 0x48, I key VK_I = 0x49, J key VK_J = 0x4A, K key VK_K = 0x4B, L key VK_L = 0x4C, M key VK_M = 0x4D, N key VK_N = 0x4E, O key VK_O = 0x4F, P key VK_P = 0x50, Q key VK_Q = 0x51, R key VK_R = 0x52, S key VK_S = 0x53, T key VK_T = 0x54, U key VK_U = 0x55, V key VK_V = 0x56, W key VK_W = 0x57, X key VK_X = 0x58, Y key VK_Y = 0x59, Z key VK_Z = 0x5A, Left Windows key LWIN = 0x5B, Right Windows key RWIN = 0x5C, Applications key APPS = 0x5D, Numeric keypad 0 key NUMPAD0 = 0x60, Numeric keypad 1 key NUMPAD1 = 0x61, Numeric keypad 2 key NUMPAD2 = 0x62, Numeric keypad 3 key NUMPAD3 = 0x63, Numeric keypad 4 key NUMPAD4 = 0x64, Numeric keypad 5 key NUMPAD5 = 0x65, Numeric keypad 6 key NUMPAD6 = 0x66, Numeric keypad 7 key NUMPAD7 = 0x67, Numeric keypad 8 key NUMPAD8 = 0x68, Numeric keypad 9 key NUMPAD9 = 0x69, Multiply key MULTIPLY = 0x6A, Add key ADD = 0x6B, Separator key SEPARATOR = 0x6C, Subtract key SUBTRACT = 0x6D, Decimal key DECIMAL = 0x6E, Divide key DIVIDE = 0x6F, F1 key F1 = 0x70, F2 key F2 = 0x71, F3 key F3 = 0x72, F4 key F4 = 0x73, F5 key F5 = 0x74, F6 key F6 = 0x75, F7 key F7 = 0x76, F8 key F8 = 0x77, F9 key F9 = 0x78, F10 key F10 = 0x79, F11 key F11 = 0x7A, F12 key F12 = 0x7B, NUM LOCK key NUMLOCK = 0x90, SCROLL LOCK key SCROLL = 0x91, #>
これは,実際に実装されているものらしい.これを利用しつつコードを書く.
パラメータの設定
まずは,パラメータの設定からはじめる.ここでは,ファイル名の末尾に連番の数字を振ってデータを識別する.
# データファイルがあるディレクトリパス $dataPath = "C:\Users\...\dataDir" # 画像を保存するディレクトリパス $savePath = "C:\Users\...\imgDir" # 起動するソフトウェア $exePath = "C:\Users\...\hoge.exe"; # ファイル名の開始と終了 $startFileNum = 1; $endFileNum = 500;
ソフトウェアの起動
次に,ソフトウェアを起動するコードを書く.ここでは,最大化して表示する.
######################################################## # 指定したパスにあるプログラムを起動し、最大化して表示 # ######################################################## $process = Start-Process $exePath -PassThru -windowstyle Maximized | Get-UiaWindow;
この「Start-Process」でプロセスを起動する.Get-UiaWindow は,起動したソフトウェアをアクティブにするものだという認識だ(細かいことは知らない).
対象とするデータを開く
そして,ソフトウェアから対象とするデータを開く.ここでは,「hoge001.dat」,「hoge002.dat」という風に名前がついているものとする.連番を $idx で指定する.また,「Alt → r → u」で読み込み画面が開くものとする(通常のソフトは Ctrl + O のはず,これが特殊なだけ).
# 対象のファイルを設定 $idx = 2; # 番号 $padNum = $idx.ToString("000"); # 0 詰め 3 桁の数値とする(2 なら 002, 10 なら 010) $targetFile = "hogehoge" + $padNum + ".dat"; # 開くファイル $imgFile = "foo" + $padNum + ".png"; # 保存するファイル # ここでは,Alt を押した後,r → u の順でキーを押すと,開く画面が出る $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを押す $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_R) | Out-Null; # r キーを押す $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_U) | Out-Null; # u キーを押す
おなじみの「開く」という画面が出る.ここでは,$dataPath で指定したパスを入力し,対象とするファイルを開く. まず,「開く」という画面をアクティブにする.ここで,「UIAutomationSpy.exe」を使用した.アクティブにするには,
$openWnd = Get-UiaWindow -Name '開く';
とすればよいらしい.代入する変数名は何でもよい.次に,パスを設定する.パスは,F4 キーを押して入力モードにし,Ctrl + A で全選択,Delete キーで削除,その後,$dataPath で指定したパスを書き込み,Enter を押すという流れになる.これを,コードで書くと以下のようになる.
$openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null; # F4 キーを押す $openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # Ctrl キーを押し続ける(KeyDown に注目) $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null; # A キーを押す(全選択状態となる) $openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # Ctrl キーを離す(KeyUp) $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null; # Delete キーを押す(削除) $openWnd.Keyboard.TypeText($dataPath) | Out-Null; # テキストを書き込む $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # Enter を押す(RETURN として定義)
最後に,$targetFile で指定したファイル名を「ファイル名(N):」の欄に記入する必要がある.これが面倒であった.UIAutomationSpy.exe には,available patterns という項目があり,ここに,「ValuePattern」や「TextPattern」など,設定するための項目が記されている.今回は, http://uiautomation.codeplex.com/wikipage?title=Object%20model&referringTitle=Documentation で使い方を調べた.英語だが,何とかなる.その結果,以下のようなコードが書ける.
# リスト中にあるアイテムを選択して開く $selectItem = $openWnd | Get-UiaEdit -Name 'ファイル名(N):'; $selectItem.Value = $targetFile; $selectItem.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # 0.5 秒停止 Start-Sleep -m 500
画面をクリップボードに保存
開いたデータの画面を PrintScreen でクリップボードにコピーする.
# 表示された データファイルを画像として保存 $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを押す $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::SNAPSHOT) | Out-Null; # PrintScreen キーを押す $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを離す
ペイントに貼り付け
ペイントは「mspaint」で起動できるので,絶対パスの指定はいらない.
# ペイントを起動 $paintWnd = Start-Process mspaint -PassThru -windowstyle Maximized | Get-UiaWindow; # クリップボードにある画像を貼り付ける $paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_V) | Out-Null; $paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null;
名前を付けて保存
# 保存 $paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_S) | Out-Null; $paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $openWnd = Get-UiaWindow -Name '名前を付けて保存'; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null; $openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null; $openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null; $openWnd.Keyboard.TypeText($savePath) | Out-Null; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; $saveProcess = $openWnd | Get-UiaEdit -Name 'ファイル名:' $saveProcess.Value = $imgFile; $saveProcess.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # ペイントを閉じる $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_X) | Out-Null; # 0.5 秒停止 Start-Sleep -m 500
スクリプト全体
以上のスクリプトを for 文を用いてループ処理にかければよい.
Import-Module C:\winPowerShell\UIAutomation\UIAutomation.dll # データファイルがあるディレクトリパス $dataPath = "C:\Users\...\dataDir" # 画像を保存するディレクトリパス $savePath = "C:\Users\...\imgDir" # 起動するソフトウェア $exePath = "C:\Users\...\hoge.exe"; # ファイル名の開始と終了 $startFileNum = 1; $endFileNum = 500; ######################################################## # 指定したパスにあるプログラムを起動し、最大化して表示 # ######################################################## $process = Start-Process $exePath -PassThru -windowstyle Maximized | Get-UiaWindow; # 対象のファイルを設定 for ($idx = $startFileNum ; $idx -le $endFileNum ; $idx++) { $padNum = $idx.ToString("000"); # 0 詰め 3 桁の数値とする(2 なら 002, 10 なら 010) $targetFile = "hogehoge" + $padNum + ".dat"; # 開くファイル $imgFile = "foo" + $padNum + ".png"; # 保存するファイル # ここでは,Alt を押した後,r → u の順でキーを押すと,開く画面が出る $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを押す $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_R) | Out-Null; # r キーを押す $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_U) | Out-Null; # u キーを押す $openWnd = Get-UiaWindow -Name '開く'; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null; # F4 キーを押す $openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # Ctrl キーを押し続ける(KeyDown に注目) $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null; # A キーを押す(全選択状態となる) $openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # Ctrl キーを離す(KeyUp) $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null; # Delete キーを押す(削除) $openWnd.Keyboard.TypeText($dataPath) | Out-Null; # テキストを書き込む $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # Enter を押す(RETURN として定義) # リスト中にあるアイテムを選択して開く $selectItem = $openWnd | Get-UiaEdit -Name 'ファイル名(N):'; $selectItem.Value = $targetFile; $selectItem.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # 0.5 秒停止 Start-Sleep -m 500 # 表示された データファイルを画像として保存 $process.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを押す $process.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::SNAPSHOT) | Out-Null; # PrintScreen キーを押す $process.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; # Alt キーを離す # ペイントを起動 $paintWnd = Start-Process mspaint -PassThru -windowstyle Maximized | Get-UiaWindow; # クリップボードにある画像を貼り付ける $paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_V) | Out-Null; $paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; # 保存 $paintWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_S) | Out-Null; $paintWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $openWnd = Get-UiaWindow -Name '名前を付けて保存'; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::F4) | Out-Null; $openWnd.Keyboard.KeyDown([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_A) | Out-Null; $openWnd.Keyboard.KeyUp([WindowsInput.Native.VirtualKeyCode]::CONTROL) | Out-Null; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::DELETE) | Out-Null; $openWnd.Keyboard.TypeText($savePath) | Out-Null; $openWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; $saveProcess = $openWnd | Get-UiaEdit -Name 'ファイル名:' $saveProcess.Value = $imgFile; $saveProcess.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::RETURN) | Out-Null; # ペイントを閉じる $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::MENU) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_F) | Out-Null; $paintWnd.Keyboard.KeyPress([WindowsInput.Native.VirtualKeyCode]::VK_X) | Out-Null; # 0.5 秒停止 Start-Sleep -m 500 }
これで,スクリプトが完成した.これを,guiAuto.sp1 とでも命名し,適当なディレクトリに保存する.その後,Windows PowerShell ISE を起動し,「(ファイルがあるパス)\guiAuto.sp1」とするとスクリプトが走る.
今後
今回は,調べつつただ書いただけのものができた.関数も使えるらしいので,次やるときは,関数を利用して,機能ごとに分けたものを作成したい.
C 言語による C 言語ファイル操作
どうも,筆者です.
今回は,C 言語を用いて,C 言語ファイルの簡易的な編集を行う.プログラムの勉強であり,実用性は皆無である.
目的
ここでは,C 言語のソースコードを読み込んで,コメントとそれ以外のコードに分けるという操作を行う. ここで,
- ソースコード内のタブは読み飛ばす.
- スペースは保持する.
- コメントは,そのまま出力する.
- コメント以外のコードは,指定した文字列以内に収まるように,出力する.この時,改行コードは無視する.
という条件で処理を行う.すなわち,ある程度決まった長さで,コードを区切り,出力するプログラムとなる.
サンプル
今回は,楕円の中心,長軸の長さ,短軸の長さ,回転角の情報を読み取り,構造体に代入するというプログラムを例に,出力結果を示す.
void readPhantom(char *fileName, int numberOfPhantoms, PHANTOM *phantom, int *result){ FILE *fp; int iCnt; double paramReal[5]; if(*result < 0){ *result = -1; } else if(NULL == (fp = fopen(fileName, "r"))){ fprintf(stderr, "%s can't open\n", fileName); *result = -1; } else{ *result = 1; for(iCnt = 0; iCnt < numberOfPhantoms; iCnt++){ if( fscanf(fp, "%lf %lf %lf %lf %lf", ¶mReal[0], ¶mReal[1], ¶mReal[2], ¶mReal[3], ¶mReal[4]) != 5 ){ fprintf(stderr, "%s: format is illegal\n", fileName); *result = -1; iCnt = numberOfPhantoms; } else{ phantom[iCnt].x0 = paramReal[0]; phantom[iCnt].y0 = paramReal[1]; phantom[iCnt].A = paramReal[2]; phantom[iCnt].B = paramReal[3]; phantom[iCnt].alpha = paramReal[4]; } } fclose(fp); } }
これをプログラムに投げると,
void readPhantom(char *fileName, int numberOfPhantoms, PHANTOM *phantom, int *result){FILE *fp;int iCnt;double paramReal[5];if(*result < 0){*result = -1;}else if(NULL == (fp = fopen(fileName, "r"))){fprintf(stderr, "%s can't open\n", fileName);*result = -1;}else{*result = 1;for(iCnt = 0; iCnt < numberOfPhantoms; iCnt++){if(fscanf(fp, "%lf %lf %lf %lf %lf", ¶mReal[0], ¶mReal[1], ¶mReal[2], ¶mReal[3], ¶mReal[4]) != 5){fprintf(stderr, "%s: format is illegal\n", fileName);*result = -1;iCnt = numberOfPhantoms;}else{phantom[iCnt].x0 = paramReal[0];phantom[iCnt].y0 = paramReal[1];phantom[iCnt].A = paramReal[2];phantom[iCnt].B = paramReal[3];phantom[iCnt].alpha = paramReal[4];}}fclose(fp);}}
こうなる.もちろん,コンパイルもできる.
プログラム
たいした処理はしていないが,プログラムがあるので,載せておく.
#include<stdio.h> #include<stdlib.h> #define MAX_STRING_LENGTH (128) typedef unsigned char UI1; void printString(int iNum, char sz_buf[]); int main(void){ int iCnt, iDummy; char chReadChar, chComment; char sz_buf[MAX_STRING_LENGTH]; chReadChar = chComment = 0; iCnt = iDummy = 0; while(EOF != scanf("%c", &chReadChar)){ /* ヘッダーの出力 */ if('#' == chReadChar){ if(iCnt > 0){ sz_buf[iCnt] = '\0'; printString(iCnt, sz_buf); printf("\n"); iCnt = 0; } /* # から始まるものは,改行が出力するまで書き出す */ while('\n' != chReadChar){ printf("%c", chReadChar); iDummy = scanf("%c", &chReadChar); } printf("\n"); continue; } /* コメントの判定 */ if('/' == chReadChar){ iDummy = scanf("%c", &chReadChar); if(('*' != chReadChar) && ('/' != chReadChar)){ if(iCnt + 2 >= (int)MAX_STRING_LENGTH){ sz_buf[iCnt] = '\0'; printString(iCnt, sz_buf); iCnt = 0; } sz_buf[iCnt++] = '/'; sz_buf[iCnt++] = chReadChar; } else{ /* 複数行コメントの場合 */ sz_buf[iCnt] = '\0'; printString(iCnt, sz_buf); printf("\n"); iCnt = 0; if('*' == chReadChar){ printf("/"); printf("*"); chComment = 1; while(1 == chComment){ iDummy = scanf("%c", &chReadChar); if('\t' == chReadChar){ chReadChar = ' '; } printf("%c", chReadChar); if('*' == chReadChar){ iDummy = scanf("%c", &chReadChar); if('\t' == chReadChar){ chReadChar = ' '; } printf("%c", chReadChar); if('/' == chReadChar){ chComment = 0; printf("\n"); } } } } else{ /* 1 行コメントの場合 */ printf("//"); iDummy = scanf("%c", &chReadChar); while('\n' != chReadChar){ printf("%c", chReadChar); iDummy = scanf("%c", &chReadChar); } printf("\n"); } } } else if('"' == chReadChar){ sz_buf[iCnt] = '\0'; printString(iCnt, sz_buf); iCnt = 0; printf("%c", chReadChar); iDummy = scanf("%c", &chReadChar); while('"' != chReadChar){ printf("%c", chReadChar); iDummy = scanf("%c", &chReadChar); } printf("%c", chReadChar); } else{ /* 改行文字とタブは読み飛ばす */ if(('\n' == chReadChar) || ('\t' == chReadChar)){ continue; } /* NULL文字を挿入し,文字列を出力 */ if(iCnt + 2 >= (int)MAX_STRING_LENGTH){ sz_buf[iCnt] = '\0'; printString(iCnt, sz_buf); iCnt = 0; } /* 読み込んだ文字列を格納 */ sz_buf[iCnt++] = chReadChar; } } /* 残った文字列を出力 */ sz_buf[iCnt] = '\0'; printf("%s\n", sz_buf); iCnt = iDummy; return 0; } void printString(int iNum, char sz_buf[]){ int iCnt, iSpaceCnt, isSpacePoint; iSpaceCnt = 0; /* スペースの数を探索 */ for(iCnt = 0; iCnt < iNum - 1; iCnt++){ if(' ' == sz_buf[iCnt]){ iSpaceCnt++; isSpacePoint = iCnt; } } /* スペースが 1 つ以上ある場合 */ if(iSpaceCnt > 1){ /* 最後の文字だけ別に出力(途中で文字が切れるのを防ぐため) */ /* int iCnt, iSpaceCnt, isSpacePoint;for(iCnt = 0; iCnt < iNum - 1; iCnt++){if(' ' == sz_bu f[iCnt]){iSpaceCnt++;isSpacePoint = iCnt;}} とならないようにするため(sz_bu という変数として認識される) */ sz_buf[isSpacePoint] = '\0'; printf("%s\n", sz_buf); printf("%s", &sz_buf[isSpacePoint + 1]); } /* スペースが 1 つの場合 */ else{ /* そのまま出力 */ printf("%s", sz_buf); } return; }
実験
ここでは,前回作成した CSV ファイルを読み出すプログラムを入力し,出力を確認する.まず,ものとプログラムは,以下のようになっている.前回のものをくっつけただけである.
#include<stdio.h> #include<stdlib.h> typedef enum { RETURN_OK = 0, ARGUMENT_ERROR, FILE_OPEN_ERROR, MALLOC_ERROR } RETURN_TYPE; /* iCountRowCol: 行数と列数をカウントする char *pszFileName: 読み込むファイル名 unsigned int *piRow: 行数 unsigned int *piCol: 列数 return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iCountRowCol(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol){ FILE *prFile; int iRet; unsigned int uiRow, uiCol; char chData; iRet = RETURN_OK; /* 引数のチェック */ if((NULL == pszFileName) || (NULL == puiRow) || (NULL == puiCol)){ fprintf(stderr, "argument error\n"); iRet = ARGUMENT_ERROR; goto EXIT_COUNT_ROWCOL; } else{ *puiRow = *puiCol = 0; } if(NULL == (prFile = fopen(pszFileName, "r"))){ fprintf(stderr, "Error: %s cannot open\n", pszFileName); iRet = FILE_OPEN_ERROR; goto EXIT_COUNT_ROWCOL; } uiRow = uiCol = 0; while(EOF != fscanf(prFile, "%c", &chData)){ /* カンマの場合 */ if(',' == chData){ uiCol++; } /* 改行コードの場合 */ if('\n' == chData){ uiCol++; uiRow++; } } uiCol /= uiRow; *puiRow = uiRow; *puiCol = uiCol; fclose(prFile); EXIT_COUNT_ROWCOL: return iRet; } /* iMallocInt: int 型の malloc を行う unsigned int uiSize: 確保するサイズ int **ppiArray: 確保先のアドレス return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iMallocInt(unsigned int uiSize, int **ppiArray){ unsigned int uiCnt; int iRet; iRet = RETURN_OK; /* 引数チェック */ if(NULL == ppiArray){ iRet = ARGUMENT_ERROR; goto EXIT_MALLOC_INT; } /* malloc */ if(NULL == (*ppiArray = (int *)malloc(sizeof(int) * uiSize))){ fprintf(stderr, "malloc error\n"); iRet = MALLOC_ERROR; goto EXIT_MALLOC_INT; } else{ for(uiCnt = 0; uiCnt < uiSize; uiCnt++){ (*ppiArray)[uiCnt] = 0; } } EXIT_MALLOC_INT: return iRet; } /* freeData: 確保した領域を開放する void *pvData: 確保した領域の先頭アドレス */ void freeData(void *pvData){ if(NULL != pvData){ free(pvData); } return; } /* iReadData: データの読み込み char *pszFileName: 読み込むファイル名 unsigned int uiRow: ファイルの行数 unsigned int uiCol: ファイルの列数 int *piArray: データの格納先 return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iReadData(char *pszFileName, unsigned int uiRow, unsigned int uiCol, int *piArray){ FILE *prFile; char chData; int iRet; unsigned int uiCntRow, uiCntCol, uiCharCnt; char achInputDataList[11]; iRet = RETURN_OK; /* 引数のチェック */ if(NULL == pszFileName){ iRet = ARGUMENT_ERROR; goto EXIT_READ_DATA; } /* ファイルオープン */ if(NULL == (prFile = fopen(pszFileName, "r"))){ fprintf(stderr, "Error: %s cannot open\n", pszFileName); iRet = FILE_OPEN_ERROR; goto EXIT_READ_DATA; } /* ファイルの読み込み */ for(uiCntRow = 0; uiCntRow < uiRow; uiCntRow++){ for(uiCntCol = 0; uiCntCol < uiCol; uiCntCol++){ uiCharCnt = 0; achInputDataList[0] = '\0'; /* 1 文字ずつ読み込む */ while(EOF != fscanf(prFile, "%c", &chData)){ /* 「,」または「\n」が来た場合,ループを抜ける */ if((',' == chData) || ('\n' == chData)){ break; } /* 今回は,数値の部分だけ保存 */ if(((int)'0' <= (int)chData) && ((int)chData <= (int)'9')){ achInputDataList[uiCharCnt++] = chData; } } achInputDataList[uiCharCnt] = '\0'; piArray[uiCntRow * uiCol + uiCntCol] = atoi(achInputDataList); } } fclose(prFile); EXIT_READ_DATA: return iRet; } /* iReadCSVFile: CSV ファイルからデータを読み込む char *pszFileName: 読み込むファイル名 unsigned int *piRow: 行数 unsigned int *piCol: 列数 int **ppiArray: 確保先のアドレス return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iReadCSVFile(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol, int **ppiArray){ int iRet; unsigned int uiSize = 0; iRet = iCountRowCol(pszFileName, puiRow, puiCol); if(RETURN_OK != iRet){ fprintf(stderr, "Error: iCountRowCol\n"); goto EXIT_READ_CSV_FILE; } uiSize = (*puiRow) * (*puiCol); iRet = iMallocInt(uiSize, ppiArray); if(RETURN_OK != iRet){ fprintf(stderr, "Error: iMallocInt\n"); goto EXIT_READ_CSV_FILE; } iRet = iReadData(pszFileName, *puiRow, *puiCol, *ppiArray); if(RETURN_OK != iRet){ fprintf(stderr, "Error: iReadData\n"); goto EXIT_READ_CSV_FILE; } EXIT_READ_CSV_FILE: return iRet; } void printData(unsigned int uiRow, unsigned int uiCol, int *piArray){ unsigned int uiCntRow, uiCntCol; for(uiCntRow = 0; uiCntRow < uiRow; uiCntRow++){ for(uiCntCol = 0; uiCntCol < uiCol; uiCntCol++){ printf("%d ", piArray[uiCntRow * uiCol + uiCntCol]); } printf("\n"); } } int main(int iArgCnt, char **ppchArgVec){ int iRet; unsigned int uiRow, uiCol; int *piArray; if(iArgCnt == 2){ iRet = iReadCSVFile(ppchArgVec[1], &uiRow, &uiCol, &piArray); if(iRet == RETURN_OK){ printData(uiRow, uiCol, piArray); freeData((void *)piArray); } } return 0; }
次に,プログラムにより出力したコードを示す.
#include<stdio.h> #include<stdlib.h> typedef enum {RETURN_OK = 0,ARGUMENT_ERROR,FILE_OPEN_ERROR,MALLOC_ERROR} RETURN_TYPE; /* iCountRowCol: 行数と列数をカウントする char *pszFileName: 読み込むファイル名 unsigned int *piRow: 行数 unsigned int *piCol: 列数 return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iCountRowCol(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol){FILE *prFile;int iRet;unsigned int uiRow, uiCol;char chData;iRet = RETURN_OK; /* 引数のチェック */ if((NULL == pszFileName) || (NULL == puiRow) || (NULL == puiCol)){fprintf(stderr, "argument error\n");iRet = ARGUMENT_ERROR;goto EXIT_COUNT_ROWCOL;}else{*puiRow = *puiCol = 0;}if(NULL == (prFile = fopen(pszFileName, "r"))){fprintf(stderr, "Error: %s cannot open\n", pszFileName);iRet = FILE_OPEN_ERROR;goto EXIT_COUNT_ROWCOL;}uiRow = uiCol = 0;while(EOF != fscanf(prFile, "%c", &chData)){ /* カンマの場合 */ if(',' == chData){uiCol++;} /* 改行コードの場合 */ if('\n' == chData){uiCol++;uiRow++;}}uiCol /= uiRow;*puiRow = uiRow;*puiCol = uiCol;fclose(prFile);EXIT_COUNT_ROWCOL:return iRet;} /* iMallocInt: int 型の malloc を行う unsigned int uiSize: 確保するサイズ int **ppiArray: 確保先のアドレス return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iMallocInt(unsigned int uiSize, int **ppiArray){unsigned int uiCnt;int iRet;iRet = RETURN_OK; /* 引数チェック */ if(NULL == ppiArray){iRet = ARGUMENT_ERROR;goto EXIT_MALLOC_INT;} /* malloc */ if(NULL == (*ppiArray = (int *)malloc(sizeof(int) * uiSize))){fprintf(stderr, "malloc error\n");iRet = MALLOC_ERROR;goto EXIT_MALLOC_INT;}else{for(uiCnt = 0; uiCnt < uiSize; uiCnt++){(*ppiArray)[uiCnt] = 0;}}EXIT_MALLOC_INT:return iRet;} /* freeData: 確保した領域を開放する void *pvData: 確保した領域の先頭アドレス */ void freeData(void *pvData){if(NULL != pvData){free(pvData);}return;} /* iReadData: データの読み込み char *pszFileName: 読み込むファイル名 unsigned int uiRow: ファイルの行数 unsigned int uiCol: ファイルの列数 int *piArray: データの格納先 return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iReadData(char *pszFileName, unsigned int uiRow, unsigned int uiCol, int *piArray){FILE *prFile;char chData;int iRet;unsigned int uiCntRow, uiCntCol, uiCharCnt;char achInputDataList[11];iRet = RETURN_OK; /* 引数のチェック */ if(NULL == pszFileName){iRet = ARGUMENT_ERROR;goto EXIT_READ_DATA;} /* ファイルオープン */ if(NULL == (prFile = fopen(pszFileName, "r"))){fprintf(stderr, "Error: %s cannot open\n", pszFileName);iRet = FILE_OPEN_ERROR;goto EXIT_READ_DATA;} /* ファイルの読み込み */ for(uiCntRow = 0; uiCntRow < uiRow; uiCntRow++){for(uiCntCol = 0; uiCntCol < uiCol; uiCntCol++){uiCharCnt = 0;achInputDataList[0] = '\0'; /* 1 文字ずつ読み込む */ while(EOF != fscanf(prFile, "%c", &chData)){ /* 「,」または「\n」が来た場合,ループを抜ける */ if((',' == chData) || ('\n' == chData)){break;} /* 今回は,数値の部分だけ保存 */ if(((int)'0' <= (int)chData) && ((int)chData <= (int)'9')){achInputDataList[uiCharCnt++] = chData;}}achInputDataList[uiCharCnt] = '\0';piArray[uiCntRow * uiCol + uiCntCol] = atoi(achInputDataList);}}fclose(prFile);EXIT_READ_DATA:return iRet;} /* iReadCSVFile: CSV ファイルからデータを読み込む char *pszFileName: 読み込むファイル名 unsigned int *piRow: 行数 unsigned int *piCol: 列数 int **ppiArray: 確保先のアドレス return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iReadCSVFile(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol, int **ppiArray){int iRet;unsigned int uiSize = 0;iRet = iCountRowCol(pszFileName, puiRow, puiCol);if(RETURN_OK != iRet){fprintf(stderr, "Error: iCountRowCol\n");goto EXIT_READ_CSV_FILE;}uiSize = (*puiRow) * (*puiCol);iRet = iMallocInt(uiSize, ppiArray);if(RETURN_OK != iRet){fprintf(stderr, "Error: iMallocInt\n");goto EXIT_READ_CSV_FILE;}iRet = iReadData(pszFileName, *puiRow, *puiCol, *ppiArray);if(RETURN_OK != iRet){fprintf(stderr, "Error: iReadData\n");goto EXIT_READ_CSV_FILE;}EXIT_READ_CSV_FILE:return iRet;}void printData(unsigned int uiRow, unsigned int uiCol, int *piArray){unsigned int uiCntRow, uiCntCol;for(uiCntRow = 0; uiCntRow < uiRow; uiCntRow++){for(uiCntCol = 0; uiCntCol < uiCol; uiCntCol++){printf("%d ", piArray[uiCntRow * uiCol + uiCntCol]);}printf("\n");}}int main(int iArgCnt, char **ppchArgVec){int iRet;unsigned int uiRow, uiCol;int *piArray;if(iArgCnt == 2){iRet = iReadCSVFile(ppchArgVec[1], &uiRow, &uiCol, &piArray);if(iRet == RETURN_OK){printData(uiRow, uiCol, piArray);freeData((void *)piArray);}}return 0;}
少し読みにくいが,これでもちゃんとコンパイルがとおり,実行結果も一致する.次回何か書くことがあったら,もっと実用的なものを作成したい.
CSV ファイルを C 言語で読み込む
どうも,筆者です.
今回は,CSV ファイルを C 言語で読み込むことを考える.
考え方
カンマ(,)か改行コード(\n)が出てくるまで,データを char 型の配列に保持する.カンマ(,)か改行コード(\n)が出てきたら,atoi または,atof 関数で数値に変換する. この時,行数と列数が分からないので,一度ファイルの終端コード(EOF)が出てくるまで読み続ける.その後,先頭に戻って再度処理をするという流れになる.
プログラム
まず,戻り値の処理をするため,ヘッダに列挙型を定義する.
/* defineConstant.h */ #ifndef __DEFINECONSTANT_H__ #define __DEFINECONSTANT_H__ typedef enum { RETURN_OK = 0, ARGUMENT_ERROR, FILE_OPEN_ERROR, MALLOC_ERROR } RETURN_TYPE; #endif
次に,行数と列数をカウントするプログラムを作成する.
#include"defineConstant.h" /* iCountRowCol: 行数と列数をカウントする char *pszFileName: 読み込むファイル名 unsigned int *piRow: 行数 unsigned int *piCol: 列数 return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iCountRowCol(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol){ FILE *prFile; int iRet; unsigned int uiRow, uiCol; char chData; iRet = RETURN_OK; /* 引数のチェック */ if((NULL == pszFileName) || (NULL == puiRow) || (NULL == puiCol)){ fprintf(stderr, "argument error\n"); iRet = ARGUMENT_ERROR; goto EXIT_COUNT_ROWCOL; } else{ *puiRow = *puiCol = 0; } if(NULL == (prFile = fopen(pszFileName, "r"))){ fprintf(stderr, "Error: %s cannot open\n", pszFileName); iRet = FILE_OPEN_ERROR; goto EXIT_COUNT_ROWCOL; } uiRow = uiCol = 0; while(EOF != fscanf(prFile, "%c", &chData)){ /* カンマの場合 */ if(',' == chData){ uiCol++; } /* 改行コードの場合 */ if('\n' == chData){ uiCol++; uiRow++; } } uiCol /= uiRow; *puiRow = uiRow; *puiCol = uiCol; fclose(prFile); EXIT_COUNT_ROWCOL: return iRet; }
その後,必要なサイズを malloc する関数を定義する.ここでは,int 型で確保する.同時に,領域を開放する関数も書く.
/* iMallocInt: int 型の malloc を行う unsigned int uiSize: 確保するサイズ int **ppiArray: 確保先のアドレス return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iMallocInt(unsigned int uiSize, int **ppiArray){ unsigned int uiCnt; int iRet; iRet = RETURN_OK; /* 引数チェック */ if(NULL == ppiArray){ iRet = ARGUMENT_ERROR; goto EXIT_MALLOC_INT; } /* malloc */ if(NULL == (*ppiArray = (int *)malloc(sizeof(int) * uiSize))){ fprintf(stderr, "malloc error\n"); iRet = MALLOC_ERROR; goto EXIT_MALLOC_INT; } else{ for(uiCnt = 0; uiCnt < uiSize; uiCnt++){ (*ppiArray)[uiCnt] = 0; } } EXIT_MALLOC_INT: return iRet; } /* freeData: 確保した領域を開放する void *pvData: 確保した領域の先頭アドレス */ void freeData(void *pvData){ if(NULL != pvData){ free(pvData); } return; }
最後に,データを解析し,読み込んでいく.
/* iReadData: データの読み込み char *pszFileName: 読み込むファイル名 unsigned int uiRow: ファイルの行数 unsigned int uiCol: ファイルの列数 int *piArray: データの格納先 return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iReadData(char *pszFileName, unsigned int uiRow, unsigned int uiCol, int *piArray){ FILE *prFile; char chData; int iRet; unsigned int uiCntRow, uiCntCol, uiCharCnt; char achInputDataList[11]; iRet = RETURN_OK; /* 引数のチェック */ if(NULL == pszFileName){ iRet = ARGUMENT_ERROR; goto EXIT_READ_DATA; } /* ファイルオープン */ if(NULL == (prFile = fopen(pszFileName, "r"))){ fprintf(stderr, "Error: %s cannot open\n", pszFileName); iRet = FILE_OPEN_ERROR; goto EXIT_READ_DATA; } /* ファイルの読み込み */ for(uiCntRow = 0; uiCntRow < uiRow; uiCntRow++){ for(uiCntCol = 0; uiCntCol < uiCol; uiCntCol++){ uiCharCnt = 0; achInputDataList[0] = '\0'; /* 1 文字ずつ読み込む */ while(EOF != fscanf(prFile, "%c", &chData)){ /* 「,」または「\n」が来た場合,ループを抜ける */ if((',' == chData) || ('\n' == chData)){ break; } /* 今回は,数値の部分だけ保存 */ if(((int)'0' <= (int)chData) && ((int)chData <= (int)'9')){ achInputDataList[uiCharCnt++] = chData; } } achInputDataList[uiCharCnt] = '\0'; piArray[uiCntRow * uiCol + uiCntCol] = atoi(achInputDataList); } } fclose(prFile); EXIT_READ_DATA: return iRet; }
これで,CSV ファイルからデータを読み込み,格納することができる.後は,これをまとめて行う関数を定義する.
/* iReadCSVFile: CSV ファイルからデータを読み込む char *pszFileName: 読み込むファイル名 unsigned int *piRow: 行数 unsigned int *piCol: 列数 int **ppiArray: 確保先のアドレス return: 正常終了 -> 0 異常終了 -> 0 以外 */ int iReadCSVFile(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol, int **ppiArray){ int iRet; unsigned int uiSize = 0; iRet = iCountRowCol(pszFileName, puiRow, puiCol); if(RETURN_OK != iRet){ fprintf(stderr, "Error: iCountRowCol\n"); goto EXIT_READ_CSV_FILE; } uiSize = (*puiRow) * (*puiCol); iRet = iMallocInt(uiSize, ppiArray); if(RETURN_OK != iRet){ fprintf(stderr, "Error: iMallocInt\n"); goto EXIT_READ_CSV_FILE; } iRet = iReadData(pszFileName, *puiRow, *puiCol, *ppiArray); if(RETURN_OK != iRet){ fprintf(stderr, "Error: iReadData\n"); goto EXIT_READ_CSV_FILE; } EXIT_READ_CSV_FILE: return iRet; }
確認
このファイルを readCSVFile.c として保存し,正しく動作するか確認する.main 関数は,以下のようにした.
#include<stdio.h> #include<stdlib.h> #include"defineConstant.h" extern int iReadCSVFile(char *pszFileName, unsigned int *puiRow, unsigned int *puiCol, int **ppiArray); extern void freeData(void *pvData); void printData(unsigned int uiRow, unsigned int uiCol, int *piArray); int main(int iArgCnt, char **ppchArgVec){ int iRet; unsigned int uiRow, uiCol; int *piArray; if(iArgCnt == 2){ iRet = iReadCSVFile(ppchArgVec[1], &uiRow, &uiCol, &piArray); if(iRet == RETURN_OK){ printData(uiRow, uiCol, piArray); freeData((void *)piArray); } } return 0; } void printData(unsigned int uiRow, unsigned int uiCol, int *piArray){ unsigned int uiCntRow, uiCntCol; for(uiCntRow = 0; uiCntRow < uiRow; uiCntRow++){ for(uiCntCol = 0; uiCntCol < uiCol; uiCntCol++){ printf("%d ", piArray[uiCntRow * uiCol + uiCntCol]); } printf("\n"); } }
CSV ファイルは,以下のようなものを作成した.
01,02,03,04,05,06,07,08,09,10 11,12,13,14,15,16,17,18,19,20 21,22,23,24,25,26,27,28,29,30 31,32,33,34,35,36,37,38,39,40 41,42,43,44,45,46,47,48,49,50 51,52,53,54,55,56,57,58,59,60 61,62,63,64,65,66,67,68,69,70 71,72,73,74,75,76,77,78,79,80 81,82,83,84,85,86,87,88,89,90 91,92,93,94,95,96,97,98,99,100
実行結果は,以下のようになった.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
正しく動作している.