見出し画像

0から始めるPHPプログラミング #1-6 VBAで入力チェックを実装する その6

こんにちは。

前回までで中級編が終わりました。

中級編は主にループ処理を使って拡張性を意識した実装にしたんでしたね。関数の使い方を学習し、さらに自分で関数を作ってみました。

今回から上級編、配列(はいれつ)というものを使用し、さらに拡張性・利便性に長けた実装にしてみます。

少し内容が難しくなりますが、ここまでの理解が浅いな~と思ったら、過去の記事を読み返してみてください。

ですが、厳密に理解することはないので、これまでも口を酸っぱくして伝えてきました通り、積極的に手を動かしていきましょう。

それでは、今回も始まりです!


入力チェックを実装する ~上級編~ 定数の説明

はい、前回やったソースは4度にわたるループ処理を関数化したところであれで完成です。

新しいマクロを用意しましょう。

010_新マクロ作成

入力チェックボタンを右クリックし「マクロの登録」をクリックします。

020_今のマクロ

このようなダイアログが表示されます。これまでにも何度か見てきましたね。上図の赤枠には今紐づいているマクロの名称が表示されています。

そこを書き換えましょう。

030_新マクロ名設定

「validation4」と入力し、右にある新規作成ボタンをクリックします。

040_新マクロソース初期状態

こんな風になればOK。

ここね、私も一度なったんですけど、もしかするとModule3のソースに追記する形で作成された方もいるかもしれません。その場合、一度このvalidation4のソースを削除し、ファイル閉じてください。

それから再度開いて、操作すれば上図のようになります。それでもならなかったら・・・

050_Module挿入操作

右クリックから挿入>標準モジュールをクリックすると自分で追加できます。


それでは早速実装を進めていきたいのですが、その前に今回は一体どんな方法で入力チェックを実現するのか?をご説明します。

これまでは[個人情報記入票]シートに対して、セルを決め打ちでチェックしたり、ループで順番に走査したりという方法をとっていました。

今回はもう1シート用意し、そちらにチェックしたい情報を定義します。そして、その情報を頼りに自動でチェックさせる、という方法でいきます。

これから定数・変数がたくさん登場しますが、とりあえずはこの上記の内容を実装するために必要なんだ、と受け取ってください。


では、早速実装していきましょう。

以下のソースを下図のように実装してください。

    '必須チェックテキスト
    Const REQUIRED_TEXT = "が入力されていません。" & vbCrLf
    '形式チェックテキスト
    Const FORMAT_TEXT = "で入力してください。" & vbCrLf
    '必須チェックメッセージ
    'Const REQUIRED_MESSAGE = "{0}が入力されていません。"
    '形式チェックメッセージ
    'Const FORMAT_MESSAGE = "{0}は{1}形式の{2}で入力してください。"
    '正常メッセージ
    'Const NO_PROBLEM_MESSAGE = "入力チェックを行いました。問題ありません。"
    '入力フォームシートの名称
    'Const FORM_SHEET_NAME = "個人情報記入票"
    'チェック情報シートの名称
    'Const CHECK_SHEET_NAME = "チェック情報"

060_実装

はい。見たことないのが出てきました。

まず触れておきたいのですが、コメントアウトしてるものがあるじゃないですか。これは説明の都合上、あとで開放するソースです。メッセージやシート名、処理の中で内容が変わらないものを宣言(定義)しています。

あとからソースを追加してもらうのは説明がわかりにくくなるので、用意だけしておきました。説明が進んだら開放してもらうので、コメントアウトのまま置いておいてください。


同じようなのが並んでいますが、一つ取り出してみてみましょう。

    '必須チェックテキスト
    Const REQUIRED_TEXT = "が入力されていません。" & vbCrLf

これは定数を宣言しています。以前1-3で「定数」という単語を一瞬出しました。そして

固定の文字列のことを定数といいます。
定数は後続記事でまた扱いますね。

ともお伝えしました。

はい、ここで定数(ていすう)の説明をします。

まず定数とはなんぞや?という点ですが、値が固定されて変化しない数(箱)という風に理解してください。

変数はこれまでループの中でも使ってきましたし、保持してる値が度々わりますよね。文字通り「変数」というわけです。

それに比べて定数は一度値を設定したらもうそれっきりです。処理の中で値を変化させる必要がない/変化させたくないときに使用します。

    '必須チェックテキスト
    Const REQUIRED_TEXT = "が入力されていません。" & vbCrLf

書き方ですが、

Const 定数名 = 値

です。頭につけたConst(こんすと)がこれは定数でっせ、という印です。

つぎに定数名。これ、全部大文字にしている点に注目してください。これはどうしてしているのか。定数がソース中に登場するときは上記の場合、

REQUIRED_TEXT 

これだけで出てきます。これが例えばrequiredTextという定数名だったら変数名と見分けがつかないですよね。だから変数なのか定数なのか判別できるようにすべて大文字にしています。

これは私が勝手に見分けを付けられるように大文字にしよう!と言っているのではなく、この世界の慣例として決まっています。

VBAはもちろん、JavaやJavaScript、PHPでもこれが採用されています。

先ほど

値が固定されて変化しない数(箱)という風に理解してください。(中略)定数は一度値を設定したらもうそれっきりです。処理の中で値を変化させる必要がない/変化させたくないときに使用します。

という風に説明しました。ですので、言語仕様として2度以上の設定はできなくなっています。ためしにEnd Subの前に・・・

REQUIRED_TEXT = ""

このソースを追記してみてください。

070_定数代入

こんな風にできればOK。ではこの状態で実行してみましょう。

080_実行ボタン

エクセル画面から入力チェックボタンを押してもいいですし、上図赤枠の実行ボタンを押しても良いです。

実行ボタンを押したとき、もしも・・・

090_マクロ選択

このようなダイアログが出てきたら、実行対象(validation4)を選択して実行ボタンを押下してください。

すると・・・

100_エラー発生

はい、来ましたね。エラーが発生しました。

定数には値を代入できません。と言われました。言語仕様として定数への2回目以降の代入は禁止されていますね。

変数の場合は宣言だけをして後で値を代入することが多いですが、定数の場合は

    '必須チェックテキスト
   Const REQUIRED_TEXT = "が入力されていません。" & vbCrLf

このように、宣言と値の代入をセットで行います。

ところで、これまで変数のときは必ずデータ型も一緒に定義してきましたよね。定数のデータ型は?と思われた方もいるかもしれません。ここは今回のあとがきでお伝えします。


余談だけど、先ほどのこのエラー。

100_エラー発生

ポップアップの中に「コンパイルエラー」ってあるじゃないですか。今書いてるソースって我々人間に理解できる形で書いていますよね。日本語や英語、数字、記号を駆使して。

ただ、これをコンピュータがそのまま理解して実行してくれているわけではないんです。実は内部でコンピュータ用の言語に変換してそれから実行してるんですね。

で、そのソースコードをコンピュータが理解できる形に変換することをコンパイルと言います。コンパイルをするタイミングで言語の禁則パターンがないかどうかもチェックしてくれるんですね。

今回はその禁則パターンに抵触したためエラーとなった、という流れです。

そして、ポップアップのOKボタンを押下すると・・・

110_エラー後デバッグ状態

このような状態になります。デバッグ状態です。

赤い矢印で示したソースが選択状態になっていますが、そこがエラーの原因だよ、というのを教えてくれています。

ただ、今は特にデバッグの用がないので、上の赤丸で囲ったボタンで停止しましょう。

長々となりましたが、定数の説明でした。

宣言した定数の使いどころは後で触れていきます。

追加いただいた

REQUIRED_TEXT = ""

このソースは削除しておいてください。

次行きましょう。


変数を宣言する

以下のソースを下図のように実装してください。

    '入力フォームのシート
    Dim formSheet As Worksheet
    'チェック情報のシート
    Dim checkSheet As Worksheet
    '項目名
    Dim itemName As String
    'セル番地
    Dim cellAddress As String
    '正規表現パターン
    Dim itemRegExpPattern As String
    '項目の形式
    Dim itemFormat As String
    '項目のデータ型
    Dim itemType As String
    '項目値
    Dim itemValue As String
    'メッセージ
    Dim message As String
    '正規表現
    Dim regExp As Variant
    'チェック対象の最終行
    Dim checkLastRow As Integer
    'チェック対象項目情報配列
    Dim checkItemArray As Variant

120_変数宣言実装

12個の変数が出てきました。見ていきましょう。

まずはこちら。

    '入力フォームのシート
   Dim formSheet As Worksheet

これまでにもお伝えしてきた通り、変数の宣言は

Dim 変数名 As データ型

という書き方をします。そして今回、初めて見るデータ型ですね。

Worksheetは読んでそのままワークシートです。つまり・・・

130_ワークシート

この赤枠で囲ったエリア。このことです。1つのシートです。これもプログラム中に保持できるということなんですね。最初の方で

今回はもう1シート用意し、そちらにチェックしたい情報を定義します。そして、その情報を頼りに自動でチェックさせる、という方法でいきます。

とお伝えしました。シートを追加する、ということで今回はシートが2つある状態になります。入力フォームのシートと、チェック情報を管理するシート、これはプログラムに対してどちらのシートかを区別させる必要があります。

Cells(1,1).value

と書いてあったら、「え?どのシートの?」となりますからね。

そのために宣言した変数が

    '入力フォームのシート
    Dim formSheet As Worksheet

こちらというわけです。

次にいきましょう。

入力フォームのシートが出てきたということは・・・

    'チェック情報のシート
    Dim checkSheet As Worksheet

はい、チェック情報を管理するシートについても変数を宣言します。


続きの変数も説明したいのですが、先に入力チェックの情報を管理するシートを追加した方が理解が捗るので、ここでシートを追加いただきます。

140_シート追加

[個人情報記入票]シートの横に追加ボタンがあると思います。バージョンによって若干表示が違うかもしれませんが、似たようなボタンがあるでしょう。

これをクリックしてください。

150_追加直後状態

はい、こんな感じになりましたかね。

シート名を変更しましょう。「Sheet1」にマウスカーソルを合わせてダブルクリックします。

160_シート名変更前

こんな風になるので、

170_シート名変更後

「チェック情報」と入力し、エンターキーを押下します。

次に、チェック情報のヘッダを入力します。

「ヘッダ」というのはこの場合「列名」のことですね。

180_ヘッダ入力

上図のように入力してください。

さらに、チェックする内容を記載するのですが、ここの仕様を伝えておきましょう。[チェック情報]シートに記載のある項目は必須チェックの対象として扱います

その上で、「正規表現」列(C列)に記載があればその内容で形式チェックを行います。

以下のように入力してください。

190_チェック情報記載

簡単に説明します。まずA列は見た通り項目名ですね。B列は項目があるセルの番地情報です。そしてC列は正規表現、形式チェックに必要です。D列の形式はエラーメッセージに出すためです。E列のデータ型も同様。

B列の12行目「g15」ってアルファベットが小文字になっているじゃないですか。これ、特に意味はないです。うっかりミス。大文字小文字どちらでも大丈夫です。

それでは変数の説明に戻ります。


続きの5つの変数を見てみましょう。

    '項目名
    Dim itemName As String
    'セル番地
    Dim cellAddress As String
    '正規表現パターン
    Dim itemRegExpPattern As String
    '項目の形式
    Dim itemFormat As String
    '項目のデータ型
    Dim itemType As String

この5つの変数は先ほど追加したシートに入力いただいたA~E列の内容とリンクします。ですので、処理の中で[チェック情報]シートに入力した値をこれらの変数に格納するパートがある、ということですね。

今はそれだけ頭に入れておけばいいです。


続く3つの変数を見てみましょう。

    '項目値
    Dim itemValue As String
    'メッセージ
    Dim message As String
    '正規表現
    Dim regExp As Variant

これらはこれまでの実装にも出てきましたね。

項目値はユーザの入力値を格納する変数、

メッセージはユーザにお知らせするメッセージを格納する変数、

正規表現は形式チェックに必要な機能を提供してくれるオブジェクトです。

※オブジェクト=ある事柄に関する処理を一括管理・提供する便利アイテム


残り2つの変数も見てみます。

    'チェック対象の最終行
    Dim checkLastRow As Integer
    'チェック対象項目情報配列
    Dim checkItemArray As Variant

一つ目の変数、checkLastRowはチェック対象項目の最終行を格納するための変数です。つまり、先ほど用意していただいた表を例にすると・・・

200_チェック対象最終行

ここ。

    'チェック対象の最終行
    Dim checkLastRow As Integer

これはデータ型がInteger(いんてじゃー)、つまり数値を格納できる変数です。なので、上図の場合15を入れます。

どうしてこの変数が必要か?は後でご説明します!


もうひとつの変数

    'チェック対象項目情報配列
    Dim checkItemArray As Variant

こちらはcheckItemArray、項目に対するチェック情報の配列です。つまり、

210_チェック対象配列格納範囲

この赤枠内の情報を格納する変数です。

Array(あれい)は配列という意味なので、~Arrayという変数名が出てきたら配列なんだなと理解してください。

で、肝心の配列とはなんぞや?という話ですよね


配列の解説

これまで扱ってきた変数はひとつの変数にひとつの値を入れてきました。いろいろ入れたいときは「&」を使って連結させたりしてきましたけど、結局単位としてはひとつでしかありません。

絵にするとこんな感じ。

220_変数箱イメージ

ひとつの変数、ひとつの箱。

じゃあ配列はどうかっていうと

230_配列箱イメージ

こういう感じなんですね。複数の箱が連なったものでひとつの変数とします。だから今までの変数だったら

Debug.Print itemName

と書けば「ふりがな*」なり「生年月日*」なりを取得できましたが、配列の場合は

Debug.Print checkItemArray(2)

という風に書かないと値を取得することができません。(2)と書いていますね。「2」の箱の値をちょうだい!ということです。つまり!

240_配列イメージ番号付き

こんな風に各箱に番号がついていると思ってください。

細かいことは使いながら体感していきましょう。

実はまだ説明していないことがあり、それが配列の種類なのですが、これは今回のあとがきで触れます。

これで変数宣言の説明は終わりです。


各変数の初期化を行う

続きの実装をします。

初期化が必要な変数は初期化をします。ここまでで宣言した変数たちは今はまだ「ただ変数がある」というだけの状態なんですね。

入力フォームシート用の変数とかチェック情報シート用の変数とか言いましたが、まだ変数とシートは無関係の状態です。それをこの先の実装でちゃんと使えるように設定します。

次のソースを下図のように実装してください。

    '入力フォームシートの設定
    Set formSheet = Worksheets("個人情報記入票")
    'チェック情報シートの設定
    Set checkSheet = Worksheets("チェック情報")
    '正規表現オブジェクトの実体化
    Set regExp = CreateObject("VBScript.RegExp")    

250_オブジェクトセット実装

さて、3つの変数に対して代入を行っていますね。3つ目の正規表現はこれまでにも出てきました。1つ目、2つ目はワークシートを名前で指定して、代入しています。

ここではそれぞれ頭にSetがついているじゃないですか。

通常の代入ではSetを先頭につけません。この違いを理解しておきましょう。これはどういう基準で付与のする/しないが変わるのかというと代入するものがオブジェクトかどうかです。

ただいきなり「オブジェクト」って言われてもピンときませんよね。先ほども

※オブジェクト=ある事柄に関する処理を一括管理・提供する便利アイテム

と、注釈をつけたものの。

オブジェクトかどうかを判断するにはその代入するものが変数や関数をもっているかどうか?を基準にしてみてください。

例えば3つ目のregExpは正規表現オブジェクトでしたね。これは過去にも使いましたが、

        '正規表現パターンの設定
       regExp.Pattern = "^\d{3}-\d{4}$"

このように正規表現パターンを設定したり(↑過去のソースね)

If Not (regExp.Test(Cells(6, 3).Value)) Then

そのパターンに指定された値が一致しているかを判定したりしてきました(↑これも過去のソース)。変数、関数を持っているといえます。

そして今回初出のWorksheetも当然、変数関数を持っています。

260_変数関数予測

ためしに「formSheet.」と打つと上図のように続けて入力できる変数や関数の情報が出てきます。出てこなかったらCtrl + スペースキーを押してみてください。

それに比べてitemNameは

270_変数関数予測なし

このように打ってもなにも候補が出てきません。ただの変数だからです。ただの入れ物、箱でしかないんですね。

で、この説明の流れでいくと正規表現関連の処理に使う変数も「regExp.」と打ったら次の入力候補が出てくるように思うのですが、実は出てきません。その点は今回のあとがきでご説明します。

Setを付けるかつけないかはこういう事情があります。

    '入力フォームシートの設定
    Set formSheet = Worksheets("個人情報記入票")
    'チェック情報シートの設定
    Set checkSheet = Worksheets("チェック情報")
    '正規表現オブジェクトの実体化
    Set regExp = CreateObject("VBScript.RegExp")  

あとは書き方ですが、Worksheets(シート名)と書くことで、そのシート情報を取得することができます。こういう実装をした場合、安易にシート名の変更をするとバグに繋がるので気を付けてください。


それでは続きのソースにいきます。以下のソースを下図のように実装してください。

    'チェック対象の最終行
    checkLastRow = checkSheet.Cells(checkSheet.Rows.Count, 1).End(xlUp).Row
    'チェック情報を配列に保持
    checkItemArray = checkSheet.Range("A2:E" & checkLastRow)

280_配列関係変数設定

ここも変数に値を設定しています。まず・・・

    'チェック対象の最終行
    checkLastRow = checkSheet.Cells(checkSheet.Rows.Count, 1).End(xlUp).Row

これ。ちょっと小難しいソース出てきましたよね。私もそう思います。これは何をしているかというと、[チェック情報]シートの1列目の中で最後に値がある行checkLastRow変数に代入しています。

checkSheet.Cells(checkSheet.Rows.Count, 1).End(xlUp).Row

よ~く注目してほしいんですけど、まず

checkSheet.Cells(checkSheet.Rows.Count, 1)

ここまで!ここまでがどのセルを示してるかというと・・・

290_最終行!

このセルです!1列目の一番最後の行!Cells関数は行、列と指定する!

checkSheet.Rows.Count

このソースはそのシートの行数を返してくれるので、1048576を返してくれます。つまり、

checkSheet.Cells(1048576, 1)

というソースと同義です。[チェック情報]シートの1048576行目の1列目のセル、を示しています。

で、後半の

.End(xlUp)

このソース。これは操作で言うと・・・

300_アップ!

こういうのと同じです。これで何が起きるかというと・・・

310_値上がる行でストップした

こうです。15行目に移動します。Ctrl + 矢印の操作は次に値があるところまでワープするんですね。

checkSheet.Cells(1048576, 1).End(xlUp).Row

この最後にある

.Row

このソースは「の、行」と解釈しましょう。

これで「15」を示しています。よって・・・

    'チェック対象の最終行
   checkLastRow = checkSheet.Cells(checkSheet.Rows.Count, 1).End(xlUp).Row  

この処理でcheckLastRowには(今回の場合)15が代入されるというわけです。

ちなみに、このソースはマクロを開発する際によく利用するんですが、私も日ごろから覚えているというわけではありません。いつも検索してネットの海からコピーしてきています(もちろん今回も)。

320_最終行取得の検索

同じ用で検索してるともうお気に入りのサイトが決まってくるんですよね~。あなたも実装で困ったらまずは検索する、その中で検索力も養っていってください。

現代人ならお手の物かもしれませんが、欲しい情報に素早くアクセスするというのも効率よくプログラミングを行うために必要なスキルです。


さて、代入処理がまだもうひとつ残っているのでしたね。

    'チェック情報を配列に保持
    checkItemArray = checkSheet.Range("A2:E" & checkLastRow)

配列のお出ましです!

Range関数はこれまでにも使ってきましたね。過去には・・・

    'ふりがな(児童)
    If Range("B4").Value = "" Then
        message = "ふりがなが入力されていません。" & vbCrLf
    End If

こんな風に使っていました。で、今回はシートをまず指定しているため、Rangeの前にcheckCheetを付けています。ドット「.」は「の」と解釈するので言葉にすると

checkSheet.Range("A2:E" & checkLastRow)

は、

[チェック情報]シートのセル番地A2からE15の範囲

ということになります。さらっと出しちゃいましたが、先ほどの最終行取得処理で

310_値上がる行でストップした

この15行目を取得したので実体としては

    'チェック情報を配列に保持
    checkItemArray = checkSheet.Range("A2:E15")

こういうことです。Range関数はこのようにコロンで繋げば範囲指定もできるんですね。つまり、上記ソースは・・・

331_選択範囲

この範囲を指定しています。そして、checkItemArrayに代入しています。

もうここまでで文字列を「&」で連結する、というのは何度も出てきていますが、

    'チェック情報を配列に保持
    checkItemArray = checkSheet.Range("A2:E" & checkLastRow)

こういう風にも使えるんですね。

checkLastRow変数がどうして必要だったかというと、楽をするためです。

もちろん今回の「15」という値は直接ソース内に書いてもいいけど、入力チェックの内容を変えるときにチェック数が増減すれば、併せてソースに直接指定した「15」も変えないといけません。

その際、この「15」を変え忘れると、チェック処理をしたつもりが実際はチェックの対象になっていなかった、なんてことが起こりうるので、自動で取得してくれるようにしたのでした。


ここまでで、変数の宣言、値の設定は完了です!

もう少しいきたいんだけど、次キリのいい場所を迎えるのがかなり先になりそうなので、今回はここまでとしましょう。


Ctrl + Sで保存しておいてください。


あとがき・ふりかえり

今回もありがとうございました~。

以下の4つのことを説明しますね。

・配列の補足
・ソースコード中の空白行について
・命名規則について
・省略可能なソースコード
・「regExp.」の続きに変数・関数の候補を出す方法

--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  

・配列の補足

今回は配列の変数にセル範囲を設定したところで終わりましたよね。

配列の説明時に

配列の場合は Debug.Print checkItemArray(2) という風に書かないと値を取得することができません。(2)と書いていますね。「2」の箱の値をちょうだい!ということです。

と、お伝えしました。ここはせっかくなので、体験してもらいましょう。

(あとがきのパートなので、手を動かさずに見てるだけでも大丈夫)

こういうソースを書いてみます。

    Debug.Print checkItemArray

340_配列をプリントしてみる1

配列をそのままデバッグプリントに指定しました。これで実行すると何が起こるかというと・・・

350_配列をプリントしてみる_エラー

はい、エラーになりました。配列ごとDebug.Printに指定するとエラーです。これは配列はDebug.Printが許容するデータ型の対象外だった、ということです。

細かいことを言うと、少し前にエラーを出したときは「コンパイルエラー」でしたが、今回は「実行時エラー」です。コンパイルを通過しても実行時に引っ掛かる、ということもあります。

さて、ではお次は

配列の場合は Debug.Print checkItemArray(2) という風に書かないと値を取得することができません。(2)と書いていますね。「2」の箱の値をちょうだい!ということです。

この例に倣って試してみましょう。

Debug.Print checkItemArray(2)

360_配列をプリントしてみる2

いざ実行・・・!

370_配列をプリントしてみる_エラー2

なんと!?私、嘘ついちゃったってこと・・・!?いやん!


はい、実はですね。本編中に説明したような

240_配列イメージ番号付き

こういう配列だったら最後に(2)をつけたらOKなんですが、実は今回のようなセルから範囲指定して作り出した配列はこれではダメなんです。

本編中に

実はまだ説明していないことがあり、それが配列の種類なのですが、これは今回のあとがきで触れます。

こんな風に言ってました。配列の種類について説明しましょう。

配列には1次元配列(いちじげんはいれつ)2次元配列(にじげんはいれつ)があります。(3次元配列もあるけどややこしくなるからここでは取り上げません)。

これは図解がわかりやすいので、絵をご覧ください。

まず、一次元配列。

390_一次元配列イメージ

次に二次元配列。

380_二次元配列イメージ

分かりますかね。一次元配列は1方向しかありません。それに比べ二次元配列は横、縦の2方向に箱があるんですね。

セルを範囲指定して作った配列は二次元配列です。つまり、値へのアクセスは(2)ではなく、(2 ,3)というような書き方をしないといけないんです。

で、先ほどのエラーメッセージをもう一度見てみましょう。

370_配列をプリントしてみる_エラー2

「インデックスが有効範囲にありません。」と言われています。

配列の、とある要素にアクセスするために使う(2)とか(2 ,3)の数字のことをインデックスと言います。

ということは、このエラーメッセージはインデックスの指定値が不正、ということなんですね。これらを踏まえて、次のような実装にしてみます。

    Debug.Print checkItemArray(5, 1)

400_配列をプリントしてみる3

するとすると・・・

410_出力成功

はい、「郵便番号」と表示されました。エラーは起こりませんでした。

郵便番号は~

420_郵便番号

はい!5行目の1列目にありますので、

    Debug.Print checkItemArray(5, 1)

このソースと一致しますね。

で、蛇足なんだけど、すみません。エクセルの表に即して、より厳密に絵で伝えるとすると・・・

550_二次元配列イメージ_excel準拠編

こういうことですね。

420_郵便番号

↑このイメージです。

上級編の実装中に一次元配列も登場しますので、次回以降をお楽しみに。


--  --  --  --  --  --  --  --  --  --  --  --  --  --  --

・ソースコード中の空白行について

今回の実装を見てみてほしいんですけどね、意図的に途中に空白行を入れています。

画像46

これはソースの内容が変わるところで入れてるんですね。最初のブロックは定数の宣言、一行開けて変数宣言のブロック、次に一行開けた先はオブジェクトの実体化をしています。

さらに一行開けて変数に代入という流れですね。

これもソースコードの可読性向上、美しいソースを書くためのお作法のようなものです。

やっぱり文字で埋まってるよりも適度に空白行がある方が読みやすいのでね、ご自身で実装されるときは意識してみてください。


--  --  --  --  --  --  --  --  --  --  --  --  --  --  --

・命名規則について

今回、定数は変数と見分けるために大文字で書くという話をしました。このようにある目的や慣例に従って、定数・変数・関数の名前を決めるルールのことを命名規則と言います。

以前1-2で、

「reqExp」の最初の「r」が小文字で途中の「E」が大文字なのはどうして?
う~ん、いい質問!でもここでは答えません。まだ覚えなくていい!後続記事では必ず触れるから楽しみにしててください。

という件がありました。これについて、説明しましょう。

命名規則に使用する記法にはいくつか種類があるのですが、これまでの説明では、ローワーキャメルケースを使っています。これからもこれを使っていきます。

itemNameregExpcheckItemArrayの名前に共通するのは、まず複数の名前を組み合わせているという点ですね。

さらに、その名前の最初のものは小文字始まりで、2つ目以降は大文字始まりで書いています。これがローワーキャメルケースです。

そして定数はスネークケースを使っています。

すみませんが、詳細はこちらのページを見てみてください。


--  --  --  --  --  --  --  --  --  --  --  --  --  --  --

・データ型の省略と型推測について

今回の本文内で定数の説明をしているところがありますが、その中で

ところで、これまで変数のときは必ずデータ型も一緒に定義してきましたよね。定数のデータ型は?と思われた方もいるかもしれません。ここは今回のあとがきでお伝えします。

このようにあとがきで伝えます、としていました。

データ型の省略と型推測について」というタイトルでご説明しようとしている流れからしてもうお分かりですね。

変数・定数宣言時のデータ型は省略可能です。

でも省略したらどのデータ型になるの?という疑問が湧くでしょう。

検証してみます!(以下、あとがきなので見てるだけでもヨシ)

まずは定数から。

    '定数 文字列型 データ型定義あり
    Const TEXT1 As String = "定数:宣言時にデータ型あり"
   
    '定数 文字列型 データ型定義なし
    Const TEXT2 = "定数:宣言時にデータ型なし"
   
    '定数 数値型 データ型定義あり
    Const INTEGER1 As Integer = 123
   
    '定数 数値型 データ型定義なし
    Const INTEGER2 = 456
   
    '定数 真偽型 データ型定義あり
    Const BOOLEAN1 As Boolean = True
   
    '定数 真偽型 データ型定義なし
    Const BOOLEAN2 = False
    
    Debug.Print ""

こういう定数群を用意してみます。文字列を代入する定数が2つ、数値を代入する定数が2つ、真偽値を代入する定数が2つです。

それぞれ、データ型の指定をあり/なしとしていますね。

440_定数のデータ型検証ソース

(一時的に今回の実装分は退避させています)

これで実行してローカルウィンドウから各定数のデータ型を確認してみます。実行してみると・・・

450_定数のデータ型結果

はい、定数の順番がむちゃくちゃで見にくいのですが、わかりますでしょうか。データ型を指定したものは当然その通りになっているとして、それぞれデータ型を指定しなかったものも適切な型となっています

これはどういうことかというと、VBA側がよしなに解釈・適応してくれているんですね。

あ、文字列が代入されてる。ってことはこの定数はString型だな

みたいな具合に。こういう事情があるので、わたしは定数のデータ型指定は省略しています。前出の通り、値が変わることもありませんので。


次、変数を見てみます。

(ここから少しマニアックな内容になるので、読んでて面倒になったら飛ばしてもらって大丈夫です汗汗汗)

さっきの定数は削除して、次に変数群を宣言してみます。

    '変数 文字列型 データ型定義あり
    Dim text3 As String: text3 = "変数:文字列型3"
   
    '変数 文字列型 データ型定義なし
    Dim text4: text4 = "変数:文字列型4"
   
    '変数 数値型 データ型定義あり
    Dim integer3 As Integer: integer3 = 123
   
    '変数 数値型 データ型定義なし
    Dim integer4: integer4 = 456
   
    '定数 真偽型 データ型定義あり
    Dim boolean3 As Boolean: boolean3 = True
   
    '定数 真偽型 データ型定義なし
    Dim boolean4: boolean4 = False
   
    '初期値代入無し
    Dim hoge
   
    Debug.Print ""

460_変数のデータ型検証ソース

はい、変数に関してはこれまでデータ型の指定を基本としてやってきたので、上図の短い赤線3つは「ほら、指定してないでしょ?」という意味で引いています。

そして注目すべきは長めの赤線!定数では宣言と同時に値の設定が必須でしたので、自然とVBA側のデータ型推測が働くのですが、変数は値の設定は宣言と同時に行わなくてもいいので、このような実装ができます。

ちなみに変数名の「hoge(ほげ)」は一時的な検証などの別にどんな名前でもいいときによく使う名前です。

あとすみません。今までやったことなかったからここでお伝えするのが初めてになるのですが、変数の宣言と同時に値を代入するには

Dim 変数名 As データ型 : 変数名 = 値

という書き方をします。宣言文の後にコロンをつけてそのうしろに代入式を書きます。

ここまでの流れからして、

どうせデータ型を指定しなくても代入してる値の型に合わせてくれるんでしょ?定数もそうだったし。

とお思いでしょう。見てみましょう!いざ、実行!

470_変数のデータ型結果

はい、定数のときとは若干異なる様相を呈しています。

まず、変数名の最後が「3」のものはそれぞれデータ型を指定しました。これらは素直に「String」「Integer」「Boolean」となっていますね。

問題は変数名の最後が「4」の方です。それぞれ「Variant/String」「Variant/Integer」「Variant/Boolean」となっています。

これはね、変数のデータ型としてはVariant、でも実際に入っている値のデータ型はString/Integer/Booleanという意味です。

そして、hogeに関しては「Variant/Empty」となっていますね。Empty(えんぷてぃ)空(から)という意味です。値がない、ということですね。型の推測ができないからこのような表示になっています。


ここで気になるのがデータ型を指定したものと指定しなかったものでどんな違いがあるんだと、そういうところですね。

(ここからがマニアック)

さっきのソースに

    text3 = 1
    text4 = 1
    'integer3 = "あいうえお"
    'integer4 = "あいうえお"

こういうソースを足してみます。

480_マニアックな検証

これの解説としてはtext3はString型だけど数字の1を入れようとしています。text4も入れる値は同じだけど、変数のデータ型としてはVariant型だ、という点がtext3とは異なります。

これを実行してみると・・・

490_マニアックな検証_文字列型

分かりますか。String型のtext3には文字としての1が設定されました。ダブルクォーテーションで囲まれている点に注目です。文字列はダブルクォーテーションで囲みますので。

これに比べてtext4に設定された値は数字の1です。ダブルクォーテーションで囲まれていません。そしてデータ型は「Variant/Integer」となっています。さっきまで、「Variant/String」だったのに。

これは変数としてのデータ型がVariant、なんでも許容できる型だからこのようなことが可能なんです。

そして、text3には数字の1を代入したけど、文字の1として格納されたのもVBAが機転を利かせてくれたんです。

text3「ぼく、String型だから・・・数字の1を渡されたけど、文字の1として受け取るね・・・涙」

↑こんな感じ。

Stringは機転を利かせてくれました。じゃあIntegerは?見てみましょう。

500_マニアックな検証_数値型

さっきまでコメントアウトしていたところを開放しました。

ここ、もしよかったら実行結果を予想してみてください。



実行してみると・・・

510_マニアックな検証_数値型エラー

はいきました~~~!!数値型の変数に"あいうえお"は代入できない!だからエラーです。

これは上記のポップアップで「デバッグ」をクリックするとわかるんだけど、エラーの原因は・・・

520_デバッグ

この行にあります。integer3はInteger型でしたから。

じゃあ「Variant/Integer」型であるinteger4は?試してみましょう。

530_マニアックな検証_数値型その2

先ほどエラーを出したソースはコメントアウトしました。

これで実行してみます。

540_マニアックな検証_数値型その2成功

はい!キタ━━━━(゚∀゚)━━━━!!

integer4のデータ型は「Variant/Integer」から「Variant/String」に変化し、"あいうえお"が代入されました。

細かい話だけど、こういう違いがあるんですね~。


で、ここまでの話を聞いたあなたは

え?長々と何の話だったの?てかこの説明なら全部Variant型でよくない?

とお思いではないでしょうか。

はい、長くなったのはごめんなさい。ただ、今後あなたがJavaC#C、C++などのデータ型を意識する言語に進まれる場合は必ず意識しないといけないことでしたので、少し深めに説明させていただきました。

そして、すべてVariant型でいいのではないか?という疑問はデータ型を厳しく設けることがバグを未然に防ぐことに繋がるのです。

今はまだわからなくてヨシ!!

ここまででも「PHPにデータ型は出てこないよ」とは申し上げてるのですが、厳密にはデータ型を意識しなくてもいい、ということです。ですから、データ型の概念を知っている方が有利に進む局面はあります。

ですので、なんとな~くでいいので、頭の片隅くらいには置いておいてください。


--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  

・「regExp.」の続きに変数・関数の候補を出す方法 

本文中の説明で、

変数がオブジェクトかどうかは変数名の後ろに「.」を付けて変数・関数の候補が出てくるかどうかを見るといい

という話をしました。で、実際に

260_変数関数予測

こういう風にして確認したんでしたね。・・・なんだけど、

560_候補出ない

このように「regExp.」と打っても予測変換が出てこないのでした(先ほどまでの検証ソースは削除して、もとに戻してます)。

ここで役に立つのが先ほどしたばかりのデータ型のお話です。

End Subにブレイクポイントを置いてデバッグしてみます。変数regExpのデータ型を確認してみましょう。

570_ブレイクポイントを最後に設置

で、実行します。

580_そしてデバッグ

はい、デバッグ状態になりました。この先に進む処理はないのですが、変数のデータ型を確認するためです。

ローカルウィンドウを見てみましょう。

590_ローカルウィンドウ

はい、型の欄に注目してください。「Variant/Object/IRegExp2」となっています。

先ほどの「データ型の省略と型推測について」の説明内で

変数のデータ型としてはVariant、でも実際に入っている値のデータ型はString/Integer/Booleanという意味です。

という風にお伝えしました。今回も同じ理屈なんです。

この正規表現オブジェクトの宣言部は

    '正規表現
    Dim regExp As Variant

このように書いていますが、実際に入ってくるのはもっとちゃんとした特定のデータ処理をするためのデータ型(=オブジェクト)なんですね。

ですから、Variant型で宣言したけれど、

590_ローカルウィンドウ

実際のデータ型はIRegExp2型です。「Variant/Object/IRegExp2」の真ん中に「Object」というのがありますが、これは実体としては「Object」型でもある、ということを示しています。

ちょっとややこしくなるかもしれないけど、変数regExpの宣言時に指定する型はObjectと書いても問題ありません。同じ動作をします。

Variant型だけど、実態はObject型の、その中でもIRegExp2型だ、ということです。

なので、この項タイトルの「「regExp.」の続きに変数・関数の候補を出す方法」の答えとしては、変数regExpのデータ型を最初からIRegExp2にしてしまえばちゃんと正規表現オブジェクトが持ってる変数・関数を候補に出すことができる、ということになります。

600_候補出た

ほら、上図の通りですね。

だったらどうして最初から「IRegExp2型」で宣言しないのか?

と思われたかもしれません。

まあこれは学習的な意味で、というのもあるのですが、世の中に数多あるデータ型のひとつひとつをすべて覚えておく必要は別にないんです。StringとかInteger、WorkSheetなどの主要なものだけを覚えておけばいいです。

だからこの正規表現みたいにどこかのライブラリに入っているけど、その詳細まではよく知らないものはVariant型として宣言します(Object型でもOK)。

もちろん、ソースコードとしてのクオリティや品質に気を配るなら「IRegExp2」型で宣言した方がより厳密なソースになります。

Variant型・Object型っていうのはポケモンでいうところのメタモンみたいなやつでして、何にでも変身できるけど、結局こいつ自体は何者でもない・なにもできない、ということなんですね。

(※メタモン:スライムみたいなふにゃふにゃの見た目で、他者の姿に変身できる。変身対象の技も使えるようになる)


先ほどお伝えした通り、最初から「IRegExp2」型として宣言するのもいいですが、ひとつひとつ厳密さにこだわっていたらコーディングのストレスにもなるので、使える便利なものは使っていきましょう、という側面もVariant型・Object型にはあります。

正規表現オブジェクトとして使用する変数regExpに「.」をつなげて変数・関数を出す方法としては以上です。

次回以降も変数regExpの宣言時の型はVariantのままで続けます。


あとすみません。今まで変数を初めて登場させる行為を「宣言」と言ってきたのですが、これ、「定義」と言ってもOKです。この先「定義」と言うかもしれませんが「ああ、宣言のことね」と同義と捉えてください。


おわり!


おわりに

おわりで~す。今回は配列の説明がメインになりました。あとがきが思いのほか長くなってしまいました汗汗。

上級編の実装に必要な変数たちは今回で揃いましたので、次回はゴリゴリ進めていきます。お楽しみに!


では、ゆっくり休憩をして次に進んでください!

ありがとうございました!


この記事が気に入ったらサポートをしてみませんか?