Power Shell で Excel を操作する(Excel 起動,ファイルオープン,保存,ファイルクローズ)
どうも,筆者です.
たまには,無駄なプログラム(スクリプト)を作成してみようと思う.今回は,「対象とする Excel ファイルを Power Shell から起動し,その後保存して閉じる」というものを作成する.
今回は,以下のような手順で処理する.
Excel アプリケーションの起動
Power Shell で Excel アプリケーションを起動するには,以下のようにする.
$excel = New-Object -ComObject Excel.Application $excel.Visible = $false # 画面上に表示させない $excel.DisplayAlerts = $true # 警告メッセージは表示する
Excel ファイルを開く
続いて,Excel ファイルを開くには,以下のようにする.
# 現在のディレクトリの絶対パスを取得 $currentPath = (Convert-Path .) # 対象の Excel ファイル名 $filename = "sample.xlsx" $book = $excel.Workbooks.Open($currentPath + "/" + $filename)
Excel ファイルを保存し閉じる
# 上書き保存 $book.Save() # ブックを閉じる $excel.Workbooks.Close()
Excel アプリケーションを終了する
Excel アプリケーションを終了するには,以下のようにする.
$excel.Quit()
後処理
Excel アプリケーションを終了しただけでは,プロセスが残っている.これは,今回の Power Shell で利用した変数を OS が保持しているためである.
このため,変数を破棄する必要がある.ここでは,$book と $excel を変数として利用したため,以下のようにして破棄する.
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
対象とする Excel ファイルのリストの取得
ある程度自動化したいため,対象とする Excel ファイルをリストにし,テキスト形式で保存する.このテキストファイルを読み込み順に処理する方法を考える.
例として,以下のようなテキストファイルを利用する.これを list.txt として保存する.
sample01.xlsx dummy sample02.xlsx dummy sample03.xlsx dummy sample04.xlsx dummy sample05.xlsx dummy sample06.xlsx dummy sample07.xlsx dummy sample08.xlsx dummy sample09.xlsx dummy sample10.xlsx dummy
list.txt を Power Shell で読み込むには,以下のようにする.
$excelFileList = @(Get-Content -Path ($currentPath + "/list.txt") | %{$_.split(" ")[0]})
「Get-Content」コマンド(Linux の cat コマンド)でファイルの中身を出力する.出力データを 1 行ずつ読み込み,スペースで区切り,最初のデータを配列の要素として取り出す. ここで,「%」は foreach のエイリアスである.また「$_」には,読み込んだ 1 行のデータが格納されている.
作成したスクリプト
作成したスクリプトを以下に示す.
try { # 現在のディレクトリの絶対パスを取得 $currentPath = (Convert-Path .) # Excelオブジェクト作成 $excel = New-Object -ComObject Excel.Application $excel.Visible = $false # 画面上に表示させない $excel.DisplayAlerts = $true # 警告メッセージは表示する # 対象ファイルの1列目を取り出す $excelFileList = @(Get-Content -Path ($currentPath + "/list.txt") | %{$_.split(" ")[0]}) foreach($filename in $excelFileList) { # ファイル名の出力 Write-Host $filename # 対象とするExcelファイル $book = $excel.Workbooks.Open($currentPath + "/" + $filename) # 上書き保存 $book.Save() # ブックを閉じる $excel.Workbooks.Close() # 300ms待つ Start-Sleep -m 300 } # Excelを閉じる $excel.Quit() } finally { # 変数の破棄 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($book) [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) }
追記1(2018/03/04)
Power Shell を起動する際に,実行権限を変更する必要があるため,以下のような bat ファイルを作成した.
@echo off echo execute Power Shell powershell -ExecutionPolicy RemoteSigned -file openClose_Excel.ps1
この bat ファイルを動かす場合は,引数を処理して,作業ディレクトリを指定する処理を加える必要がある.Power Shell で引数を取得する方法を以下に示す.
# 現在のディレクトリで初期化 Param ( $argv1 = "." ) # 引数を必須にする場合は,以下のようにする. #Param ( [parameter(mandatory = $true)]$argv1 = "." ) try { # 現在のディレクトリの絶対パスを取得 $currentPath = (Convert-Path $argv1) # 処理が続く ... }
SoftEther を利用した VPN サーバの構築
どうも,筆者です.今回は,前回セットアップした Raspberry Pi 3 Model B を利用して,VPN サーバを立てた.何度か試してみた結果,ようやくうまくいったので,ここに記録しておく.
VPN に関しては以下のサイトを参考にした.
システムのアップデート
まずは,おなじみのシステムのアップデートを行う.
sudo apt-get update sudo apt-get upgrade sudo apt-get dist-upgrade
初期設定
システムのアップデートが完了したら,初期設定を行っていく.まずは,swap 領域を無効にするため,以下のコマンドを入力する.
sudo swapoff --all # swap off にする sudo apt-get remove dphys-swapfile # swap 機能を無効にする.
次に,必要なエディタのインストールとその設定を行う.
sudo apt-get install -y vim # vim をインストール sudo update-alternatives --config editor # sudo で使用するエディタの設定をする.ここでは vim を選択 sudo vim /etc/vim/vimrc # vim の設定ファイルを修正する sudo ln -s /etc/vim/vimrc /root/.vimrc # 設定ファイルを root の .vimrc に反映 ln -s /etc/vim/vimrc ~/.vimrc # 同様に自分のローカルにも反映する
vim の設定ファイルに関しては,以前記事にしたので,そちらを参照のこと.
workspacememory.hatenablog.com
最後に,pi ユーザの変更とパスワードなしでの root 切り替えを無効にする設定を行う.これは,以下のサイトを参考にした.
sudo useradd -M tmp # 一時的なユーザ tmp の作成 sudo gpasswd -a tmp sudo # sudo 権限を付与 sudo passwd tmp # パスワードの設定 exit # 一旦抜ける ### tmp ユーザでログインする ### sudo usermod -l admin pi # pi ユーザのユーザ名を admin に変更 sudo usermod -d /home/admin -m admin # pi ユーザのホームディレクトリ名を admin に変更 sudo groupmod -n admin pi # pi ユーザのグループを admin に変更 exit # ログアウト ### admin ユーザでログインする ### sudo userdel tmp # tmp ユーザを削除 sudo passwd admin # admin ユーザのパスワードを変更 sudo rm /etc/sudoers.d/010_pi-nopasswd # パスワードなしで root 権限を取得で気ないようにするため,指定のファイルを削除
これで,自分の中では,初期設定は完了である.早速,本題の VPN 環境を構築していく.
SoftEther を利用した VPN 環境の構築
ここでは,VPN サーバの環境構築を行う.構築手順は,以下のようになる.
- bridge アダプタを利用可能にするため,bridge-utils をインストールする.
- softether のデータを Web からダウンロードして,インストールする.
- vpnserver をサービスへ登録し,自動起動する.
- Windows 側で VPN Server の設定を行う.
- ネットワーク環境の設定を行う.ここか一番ハマッた部分である.
- ルータ側でアドレス変換の設定を行い,外部から VPN サーバにアクセスできるようにする.
この順に処理を進める.
bridge-utils のインストール
vim のインストールと同様に,インストールするだけである.
sudo apt-get install -y bridge-utils
SoftEther のインストール
以下のサイトから,SoftEther をダウンロードしてくる.
それぞれ,選択肢を以下のように設定する.
- ダウンロードするソフトウェアを選択
- コンポーネントを選択
- プラットフォームを選択
- CPU を選択
- ARM EABI (32bit)
すべて選択すると,ダウンロード可能なファイルの一覧が表示される.今回は,
SoftEther VPN Server (Ver 4.24, Build 9651, beta) softether-vpnserver-v4.24-9651-beta-2017.10.23-linux-arm_eabi-32bit.tar.gz (5.40 MB)
をダウンロードした.wget コマンドを用いて,tar.gz ファイルをダウンロードする.
wget http://jp.softether-download.com/files/softether/v4.24-9651-beta-2017.10.23-tree/Linux/SoftEther_VPN_Server/32bit_-_ARM_EABI/softether-vpnserver-v4.24-9651-beta-2017.10.23-linux-arm_eabi-32bit.tar.gz
展開し,インストールを行う.
tar zxvf softether-vpnserver-v4.24-9651-beta-2017.10.23-linux-arm_eabi-32bit.tar.gz pushd vpnserver/ echo -e "1\n1\n1\n" | make popd sudo mv vpnserver/ /usr/local/ pushd /usr/local/vpnserver/ sudo chmod 600 * sudo chmod 700 vpncmd vpnserver popd
これでインストールは完了である.
サービスへの登録・自動起動の設定
次に,vpnserver をサービスに登録する.好みのエディタで「/etc/systemd/system/vpnserver.service」を新規に開く.そして,以下を記述し,保存する.
sudo vim /etc/systemd/system/vpnserver.service ### 以下を /etc/systemd/system/vpnserver.service に記述し保存する ### [Unit] Description=SoftEther VPN Server After=network.target network-online.target [Service] ExecStart=/usr/local/vpnserver/vpnserver start ExecStop=/usr/local/vpnserver/vpnserver stop Type=forking RestartSec=3s [Install] WantedBy=multi-user.target
その後,以下のコマンドでサービスを有効にし,自動起動の設定もする.
sudo systemctl daemon-reload sudo systemctl enable vpnserver.service sudo systemctl start vpnserver.service
Windows 側での設定
再び,以下のサイトから Windows 用のマネージャファイルをダウンロードし,インストールする.CPU が AMD の場合はよく分からない. SoftEther ダウンロード センター
それぞれ,選択肢を以下のように設定する.
- ダウンロードするソフトウェアを選択
- コンポーネントを選択
- プラットフォームを選択
- CPU を選択
マネージャを開き,順に設定する.この辺りの設定は,以下のサイトに図付で説明があるので,そちらを参考にする.
- 新しい接続設定から,接続先を設定する.ここでは,DHCP で割り当てられた IP アドレス 192.168.33.31 を設定した.
- 管理者パスワードを設定する.
- リモートアクセス VPN サーバーを選択する.
- 仮想 HUB 名を決める.ここでは,vpn とした.
- DDNS 名を決める.重複しないようにする必要があるため,自分が使いたいものが既に設定されている場合,使えない.
- 「L2TP サーバ機能を有効にする」にチェックを入れる.IPsec 事前共有鍵も設定する.
- 「VPN Azure を有効にする」にチェックを入れる.
- ユーザーを作成する.ログイン時に使用する.
- ローカルブリッジの設定は,ここでは行わない.(ブリッジ接続する~のまま置いておく)
すべて設定すると,VPN サーバ管理画面が表示される.次に,以下の手順で,tap デバイスの設定を行う.
- VPN サーバの管理画面において,左下にある「ローカルブリッジ設定」を選択する.
- 「仮想 HUB」として,先ほど決めた「vpn」を選択する.
- 「新しい tap デバイスとのブリッジ接続」を選択する.
- 新しい tap デバイス名に任意の名前を入力する.ここでは,softether とした.
- 「ローカルブリッジを追加」を選択する.
これで,Windows 側の設定は完了である.
ネットワーク環境の設定
一番ハマッたネットワークの設定である.最近の Raspberry Pi では,「/etc/dhcpcd.conf」にネットワークインタフェースの設定を記述するらしいが,ここでは,「/etc/network/interfaces」に記述する. 固定 IP を振りたかったが,どうしてもうまくできなかったため,dhcp として処理した.「/etc/network/interfaces」に以下を記述する.
# interfaces(5) file used by ifup(8) and ifdown(8) # Please note that this file is written to be used with dhcpcd # For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf' # Include files from /etc/network/interfaces.d: source-directory /etc/network/interfaces.d # The loopback network interface auto lo iface lo inet loopback # The primary network interface auto eth0 iface eth0 inet manual auto wlan0 iface wlan0 inet static address 192.168.33.8 netmask 255.255.255.0 gateway 192.168.33.1 wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf auto br0 iface br0 inet dhcp ### 固定 IP でも設定可能なはずであるが,うまく動作しなかったため,DHCP とした. ### # eth0 を tap_softether に割り当てる.この時,softether の部分は,先程の「tap デバイスの設定」で設定した名前にする. bridge_ports eth0 tap_softether bridge_maxwait 10
ここで,ちゃっかり,無線 LAN の設定をしている.「/etc/wpa_supplicant/wpa_supplicant.conf」の設定方法は,以下を参照のこと.
workspacememory.hatenablog.com
設定完了後,reboot をかける.問題がなければ,正常に起動するはずである.この時から,eth0 に割り当てられていた IP アドレスではログインできなくなっていた. そのため,予め設定しておいた無線 LAN 側の IP アドレスで,リモートアクセスする.また,マネージャツールも無線 LAN の IP アドレスを設定することで,管理画面に移行できる.
ssh -X admin@192.168.33.8
リモートログイン後,以下のコマンドで eth0 に tap デバイスが割り当てられているか確認する.
brctl show bridge name bridge id STP enabled interfaces br0 xyz.abcdefghijk012345 no eth0 tap_softether
ルータの設定
最後に,ルータの設定を行う.筆者は,「WHR-1166DHP3」を使用しているので,型番で調べて設定した.忘れそうなので,やり方を示しておく.ここでのソフトウェアのバージョンは,2.9 である. 「詳細設定」の画面に移動し,「セキュリティー」の「ポート変換」に移動する.以下のように IP アドレスとポートの対応付けを登録する.IP アドレスは,無線 LAN 側に割り当てたものを利用する.
- プロトコル TCPポート: 443, LAN 側の IP アドレス:192.168.33.8
- プロトコル TCPポート: 5555, LAN 側の IP アドレス:192.168.33.8
- プロトコル UDPポート: 500, LAN 側の IP アドレス:192.168.33.8
- プロトコル UDPポート: 4500, LAN 側の IP アドレス:192.168.33.8
以上で,すべての設定は完了である.疲れた.
Raspberry Pi 3 Model B のセットアップ
どうも,筆者です.
今回,Raspberry Pi を新たに購入した.イメージを落としてきて,microSD に焼くまでの作業はササっとできた.しかし,LAN ケーブルも HDMI ケーブルも見つからなかったため,無線 LAN による接続設定ができなかった. ここでは,microSD に設定ファイルを配置するという方法で,無線 LAN の設定を行うことを試みた.
イメージを microSD カードに焼くまで
ここは,苦労せずにできた.以下の環境下で,セットアップを行った.また,使用する OS イメージは,Raspbian Stretch Lite である.
OS: Ubuntu 14.04(仮想環境上)
# Step 1 sudo parted -l # このコマンドの結果により,microSD が /dev/sdb として認識されていることが分かった(/dev/sdb は環境により変わる). # Step 2 sudo umount /dev/sdb1 # アンマウントする # Step 3 sudo dd bs=1024K if=2017-09-07-raspbian-stretch-lite.img of=/dev/sdb # これでイメージを microSD に焼くことができる
もしくは,以下のサイトを参考に,「DD for Windows」というツールを用いて microSD にイメージを焼く.
http://techblog.clara.jp/2016/04/raspberry-pi-3-model-b_install_and_ssh_connect/
無線 LAN の設定
さて,ここからが本題である.といっても,調べたらたくさん出てきたので,ほとんど引用する形となる. 無線 LAN の設定は,「wpa_supplicant.conf」というファイルに保存される.
イメージを焼いた microSD 上にこの設定ファイルを配置しておけば,起動時に勝手に「/etc/wpa_supplicant/wpa_supplicant.conf」に上書きされる仕様になっているらしい. こちらのサイトを参考にして,設定ファイルを作成し,microSD 上に配置した.同時に,「ssh」というファイルも microSD 上に配置した.
ssh によるリモートログイン
最後に,ssh 接続するための IP アドレスを調査する.これだけは,調べてもいい方法が見つからなかったため,Ubuntu 側からネットワーク内のアドレスを調査する shell script を作成した.
#!/bin/bash networkAddr=192.168.33 seq 1 32 | while read ipAddr; do ping -c 1 -w 1 ${networkAddr}.${ipAddr} > /dev/null ret=$? if [ ${ret} -eq 0 ]; then macAddr=$(arp -a ${networkAddr}.${ipAddr} 2> /dev/null) echo ${networkAddr}.${ipAddr} ${macAddr^^} fi done
これを「ipAddr.sh」として保存し,実行すると,以下のようになる.
192.168.33.1 BUFFALO.SETUP (192.168.33.1) AT **:**:**:**:**:** [ETHER] ON ETH0 192.168.33.3 ? (192.168.33.3) AT **:**:**:**:**:** [ETHER] ON ETH0 192.168.33.12 ? (192.168.33.12) AT B8:27:EB:**:**:** [ETHER] ON ETH0 192.168.33.31 ? (192.168.33.31) AT B8:27:EB:**:**:** [ETHER] ON ETH0
Raspberry Pi 3 の MAC アドレスは B8:27:EB で始まるものであるので,12 か 31 となる.ここで,12 は以前設定した Raspberry Pi 3 であることが分かっているので,今回の接続先は 31 となる. 以上から,ターミナルを立ち上げて,
ssh pi@192.168.33.31
とコマンドを入力することで,Raspberry Pi に接続できる.
余談
他にも,MAC アドレスから IP アドレスを調べる方法として,以下のサイトが見つかった.ブロードキャストを使う方法は思いつかなかったので今度から使ってみようと思う.
Raspberry PiのIPアドレスをログインせずに調べるやり方|NEWS|株式会社INDETAIL(インディテール)
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)