作業中のメモ

よく「計算機」を使って作業をする.知らなかったことを中心にまとめるつもり.

Vim と Emacs の設定ファイル

どうも,筆者です.

個人的に使っている VimEmacs の設定ファイルを載せておく.

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 のものしかない.そのため,WindowsGUI を自動化する方法を探すこととなった.

UI Automation

調べたところ,WindowsPowerShell に,「UI Automation」を導入することで GUI の自動化ができるらしい.

早速,http://uiautomation.codeplex.com/ からダウンロードしてきた.保存場所は,「C:\winPowerShell\UIAutomation」というディレクトリを作成し,その中に展開した. そのままでは,PowerShellスクリプトを書いても実行できないらしい.そのため,管理者権限で「Windows PowerShell ISE」を起動させ,以下のコマンドで実行権限を変える.

Set-ExecutionPolicy RemoteSigned

ちなみに,はてなブログでは,ps1 でコードが PowerShell と認識されるらしい.便利になったものだ.

やりたいこと

さて,実際にコードを書いていくことになる.しかし,普段 GUI で行っている操作をいきなりスクリプトに落とそうとしても無理がある.そのため,色々調べて回った. 調べたところ,先程ダウンロードした UI Automation には,UIAutomationSpy.exe というスクリプトを書く際の補助ツールなるものがあるらしい. このツールを借りて,作業を行う.ここでは,以下のような流れで処理を行う.

  1. データがあるディレクトリまでの絶対パスと参照するデータ名を設定する.
  2. 起動させたいソフトウェアを絶対パスで指定し,起動する.
  3. ソフトウェアから,上記で示したデータを参照し,開く.
  4. Alt + PrintScreen により,画面をクリップボードに保存する.
  5. mspaint を実行し,ペイントを起動し,Ctrl + V でクリップボードの画像を貼り付ける.
  6. Ctrl + S で保存する.この時,新規保存となるので,保存先のパスと保存するファイル名を指定する.
  7. これをデータの数だけ繰り返す.

スクリプトを書く

実際に調べながらコードを書いてみた.ます,最初に記述すべき項目として,以下のものがある.

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 言語のソースコードを読み込んで,コメントとそれ以外のコードに分けるという操作を行う. ここで,

  1. ソースコード内のタブは読み飛ばす.
  2. スペースは保持する.
  3. コメントは,そのまま出力する.
  4. コメント以外のコードは,指定した文字列以内に収まるように,出力する.この時,改行コードは無視する.

という条件で処理を行う.すなわち,ある程度決まった長さで,コードを区切り,出力するプログラムとなる.

サンプル

今回は,楕円の中心,長軸の長さ,短軸の長さ,回転角の情報を読み取り,構造体に代入するというプログラムを例に,出力結果を示す.

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", &paramReal[0], &paramReal[1], &paramReal[2], &paramReal[3], &paramReal[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", &paramReal[0],
&paramReal[1], &paramReal[2], &paramReal[3], &paramReal[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 

正しく動作している.