サクラエディタで使える便利な正規表現3選

システムの開発・保守・運用の作業を行うにあたって、サクラエディタを使って置換やgrepを行うことは少なくないと思います。
今回は、置換やgrepで使える便利な正規表現を3つ紹介したいと思います。

1.全角文字の指定

サクラエディタの正規表現では、文字コードを16進数で指定することが可能です。
そこで、以下のように指定すると、「全角文字1文字」を表現することができます。
(正規表現に「dregonig.dll Ver.3.06 with Onigmo 5.15.0」を使用している場合)

[^\x00\x00-\x7F\x00]

「\x00\x00-\x7F\x00」はASCIIコードで定義された半角文字を意味しているのですが、それを[^…]の形式で否定することで「全角文字1文字」を表現しています。

2.参照機能

置換で使える機能です。
これは、ヒットした文字列を置換後に参照できるという機能で、この機能を使用することで文字の入れ替えや柔軟な置換条件の指定が可能になります。

置換前の文字列の指定で「()」で囲った部分については「${1}」で参照できます。
「()」で囲った部分が複数存在する場合は、2つ目の「()」は「${2}」、3つ目の「()」は「${3}」…といった形で参照できます。

例を挙げると、以下のように使用できます

・置換前の文字列

hoge,fuga,piyo

・置換の指定

置換前…(.+),(.+),(.+)
置換後…${2},${3},${1}

・置換後の文字列

fuga,piyo,hoge

3.指定した文字列を含まない行の抽出

これはgrepで使用できる表記方法で、Linuxのgrepの-vオプションと同じように指定した文字列を含まない行を抽出することができます。
以下のように表記することで、それが可能になります。
(hogeは任意の文字列を示す)

^((?!hoge).)*$

詳しい説明は割愛しますが、正規表現の表記法の一つである「否定先読み」を応用した書き方となっています。

デシジョンテーブルを使用したテストケース設定

条件分岐が複雑な場合、テストケースの設定に漏れが発生して、テストでバグを洗い出しきれなくなることがあります。
そのような場合は、デシジョンテーブルを書いてテストケースを明示的に洗い出すのが有効になります。

デシジョンテーブルの書き方について、例を挙げて説明していきます。
今回も、在庫管理システムの在庫発注処理の例を使用します。
発注するべき条件(在庫切れ、若しくは人気上昇)を満たしている時に、発注関数を呼び出し、発注量や発注日等を計算するという仕様を想定します。
また、最後の発注日から3日以上経過しないと新たに発注できない、という仕様を想定します。
フローチャートは以下の通りです。

この例の場合、以下のようにデシジョンテーブルを書くと、これらの条件からテストケースを洗い出すことができます。
「入力」欄に条件の組み合わせを、「出力」欄に各々の条件の組み合わせの結果を書きます。

発注経過日のように境界値分析が必要になる箇所は、「境界値以下(代表値2)」「境界値と同じ(3)」「境界値を超える(代表値4)」の3パターンを洗い出した方が良いです。
分岐を網羅するだけであれば「境界値以下」「境界値と同じ」の2パターンのみで十分なのですが、コーディングの誤り(IF文の「≧」を「=」としてしまう誤り)で境界値と同じ場合のみ条件が真になるという実装になってしまっていることがあるため、上記3パターンでテストを行うのが無難です。
(本当にこのような誤りを見たことがあります)

また、今回は12パターンのテストケースを洗い出していますが、テストケースが多すぎてテストする時間が取れない場合は、デシジョンテーブルを見ながら不要なテストパターンを抽出するのが有効となります。
今回の例で言うと、発注経過日が3日未満の場合については、在庫切れフラグや人気上昇フラグが発注有無に関与しないことを確認できれば良いので、テストケース2やテストケース3は削っても問題ないでしょう。発注経過日が3日を超える場合についても、前述のようなコーディング誤りがないことを確認できれば良いので、テストケース10やテストケース11は削っても問題ないと思います。

ホワイトボックステストのテストケース設定

ホワイトボックステストのテストケース設定の方法は、単体テストのテストケースを考えることになった新人に良く教えています。
十分に網羅性のあるテストケースを作らないと、後の工程にバグを残し、プロジェクトの進捗に影響を及ぼすことにもなりかねません。
(単体テストで見つけるべきバグが結合テストで頻発してしまい、結合テストの項目を消化できない、ということはプロジェクトはよくあることです)

今回は、普段新人に教えていることを記事化します。

ホワイトボックステストのテストケースの設定方法としては、以下のものがあります。

命令網羅全ての命令を1回以上実行
判定条件網羅(分岐網羅)全ての経路を1回以上通過
条件網羅判定条件中の全ての条件について真/偽それぞれ実行
判定条件/条件網羅判定条件網羅+条件網羅
複数条件網羅判定条件中の条件の全ての組み合わせを網羅

以下は、在庫管理システムの在庫発注処理の例です。
発注するべき条件(在庫切れ、若しくは人気上昇)を満たしている時に、発注関数を呼び出し、発注量や発注日等を計算するという仕様を想定します。

適切なテストケースを設定するためには、単に処理を通ったかどうかだけでなく、業務仕様通りの出力がされているかを確認できるテストケースを設定する必要があります。
大規模なシステムではカバレージツールをテストケースの網羅性を客観的に判断するために使用すると思うのですが、大抵のカバレージツールでは、C0カバレージ(命令網羅)とC1カバレージ(判定条件網羅)しか計測できません。
上記の例で言うと、「人気上昇した時に正常に発注量や発注日等を計算できるか」という観点でテストできませんし、関数の中の計算式によっては「在庫切れと人気上昇が同時発生した時にも正常に計算できるか」という観点でのテストも必要になるかもしれません。
このように、例えカバレージが100%だとしてもそれだけでは十分なテストを行えていると言うことはできず、業務仕様を確認できるかどうかという視点で条件網羅や複数条件網羅のテストケースが必要になることもあります。

「モジュール強度とモジュール結合度」の図解

情報処理技術者試験では、モジュール強度・モジュール結合度は頻出です。
モジュール強度は強ければ強いほど、モジュール結合度は弱ければ弱いほど、プログラム変更の影響範囲を限定できるため、良い設計と言えます。

しかし、モジュール強度やモジュール結合度は参考書では文章のみで解説されることが多く、なかなかイメージできない方も多いのではないのでしょうか。
そこで、モジュール強度とモジュール結合度について、以下に図示してみました。

小売店のシステムを模した例としています。
また、「モジュール」は「mdr」と表記しています。

この図を見れば、モジュール強度が弱い場合やモジュール結合度が強い場合に、プログラム変更の影響範囲が広がりやすくなってしまうことをイメージしやすくなるのではないでしょうか。

・モジュール強度が弱く、プログラム変更の影響範囲が広がる例
 「売上履歴管理機能」に改修が入り、返品についても管理できるようになった。
 モジュール強度が連絡的強度以下の場合、「売上履歴管理機能」の改修が同一モジュール内の別の機能に影響を及ぼす可能性があり、影響調査や修正の工数が増大する。

・モジュール結合度が強いと困る例
 「売上履歴管理mdr」に改修が入り、「在庫管理mdr」とは終了時の挙動が別々となった。
 モジュール結合度が外部結合以下の場合、モジュールの結合方法を変更せざるを得なくなり、影響調査や修正の工数が増大する。

java:javaからのOSコマンド呼び出しと注意点

javaのプログラムからOSのコマンドを実行したい場合は、Runtimeクラスのexecメソッドで実現できます。
 
しかし、OSのコマンドは別プロセスで立ち上がるので注意が必要です。
ProcessクラスのwaitForメソッドでプロセスの終了を待たないと、処理が前後してしまい意図しない挙動となることがあります。
また、destroyメソッドでプロセスを明確に終了させ、資源を回収することも重要です。
 
以下は、プロセスの終了を待たないと意図しない挙動となることを確認するためのテストコードです。
javaのプログラムでは、OSのコマンドを使用して、3秒のスリープ後に hoge.txt を aフォルダ から bフォルダ へ移動させています。
waitForメソッドを使用しなかった場合、javaのプログラムが終了した時点で hoge.txt の bフォルダ への移動が完了していない(javaのプログラムが立ちあげたプロセスが終了していない)ことを確認できます。
逆に、waitForメソッドを使用した場合は、javaのプログラムが終了した時点で hoge.txt の bフォルダ への移動が完了している(javaのプログラムが立ちあげたプロセスが終了している)ことを確認できます。
 

【テストコード】

・RuntimeTest.java

【実行用バッチ】

・RuntimeTest.bat

【「p.waitFor();」と「p.destroy();」をコメントアウトした場合の実行結果】

【「p.waitFor();」と「p.destroy();」を有効にした場合の実行結果】