Excel VBAでIEを操作して電車の運行情報をしゃべらせるマクロその3
はじめに
今回もきのうの続きです。
きのう、ソースコートの全体を載せたので、順番をかんがえながら説明をしていきます。
まずは下準備
マクロの本題にはいるまえに「おまじない」が2つ必要です。
このマクロでは参照設定が必要です
参照設定が必要なライブラリは2つあります。
Microsoft Internet Controls Microsoft HTML Object Library
この2つはExcel VBAでIEをあつかうにはかならず必要になってくるライブラリを参照する設定です。
VBE上で「ツール」→「参照設定」とたどっていってください。
ひらいたダイアログでMicrosoft Internet Controls
とMicrosoft HTML Object Library
にチェックをいれて、「OK」をクリックしてください。そうすると、設定が完了します。
この2つを参照設定することによって、VBE上でプロパティやメソッドの候補がでてきたり、Excel VBAでIEを操作したり、HTML文書(HTMLDocumentオブジェクト)を取得・操作することができるようになります。
マクロ内で使用するWindows API関数を宣言します
#If VBA7 Then Private Declare PtrSafe Function GetInputState Lib _ "user32" () As LongPtr #Else Private Declare Function GetInputState Lib _ "user32" () As Long #End If #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" _ (ByVal ms As LongPtr) #Else Private Declare Sub Sleep Lib "kernel32" _ (ByVal ms As Long) #End If
Windows API関数は、Windowsの機能をVBAなどのプログラミング言語からよびだすための関数であり、プログラムの先頭で宣言してからつかいます。
ここではGetInputState
関数とSleep
関数を宣言しています。
HTML文書を取得するファンクション・プロシージャF_GetHTMLDoc
のなかで、ExcelでIEを操作する際につかいます。
具体的なつかいかたはあとで説明します。
API関数を宣言するにはDeclare
ステートメントをつかいます。
Function GetInputState
とSub Sleep
はよびだす関数名を表記しています。
Lib "user32"
とLib "kernel32"
と、関数をふくむライブラリ(DLL)名を表記しています。
ふつう、32ビット版のExcelをつかっている場合は、
Private Declare Function GetInputState Lib "user32" () _ As Long Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
という表記です。
しかし、ここでは32ビット版と64ビット版の両方のExcelに対応する表記方法をつかっています。
64ビット版のExcelの場合は、
Private Declare PtrSafe Function GetInputState Lib _ "user32" () As LongPtr Private Declare PtrSafe Sub Sleep Lib "kernel32" _ (ByVal ms As LongPtr)
になります。
32ビット版との宣言方法の違いは、Declare
のあとにPtrSafe
をつけること、32ビット版のLong
型は64ビット版ではLongPtr
型で宣言すること、です。
32ビット版のExcelと64ビット版のExcelの場合のきりかえのためにVBA7
という「条件付きコンパイル定数」と#If〜〜〜Then〜〜〜#Else
ディレクティブをつかいます。
#If VBA7 Then ' 64ビット版のWindows API関数の宣言 #Else ' 32ビット版のWindows API関数の宣言 #End If
64ビット版のExcelのVBAのバージョンは「7」で、32ビット版のExcelのバージョンは「6」ですので、それを利用して場合分けをしています。
ちなみに、とくに理由がなければOSがWindows 10などの64ビットOSだとしても、Excelは32ビットにしておいたほうがいいと思います。
32ビット版Excelと64ビット版Excelの実質的なちがいは32ビット版のアドインが使えるかどうかだけであとは違いがありません。
ほとんど違いがなくて、万が一のときにアドインを使うかどうかということなので、使いたいときにアドインを使えないとこまるので、32ビット版Excelのほうがいいと思います。
64ビット版Excelでこの宣言をおこなうと、32ビット版の宣言の部分が赤字で表記されます。
わたしの環境は32ビット版Excelなので、赤字表記にはなりません。
しかし、赤字で表記されるからといって、エラーが発生するわけではありませんので、安心して使うことができます。
メインプロシージャについて
メインプロシージャは、この画像の「運行情報取得マクロ自動起動」ボタンに登録する、このマクロの主要なプロシージャです。
メインプロシージャは、一定の時間間隔でExcelに電車の運行情報をしゃべらせるマクロです。
ソースコード
Public Sub S_SpeakTrainInfo() Dim TargetTime As Date TargetTime = Now + TimeValue("1:00:00") Call S_SpeakTrainInfo_Core Application.OnTime TargetTime, "S_SpeakTrainInfo" End Sub
解説
このプロシージャでは、肝心の機能の「Excelに電車の運行情報をしゃべらせる」機能については、サブプロシージャを呼び出すだけです。
ここでは、一定の時間間隔でサブプロシージャをくりかえし呼び出すだけになってて、面倒な「Excelに電車の運行情報をしゃべらせる」機能はサブプロシージャまかせになってます。
まずは日付型の変数TargetTime
を宣言して、この変数に処理をくりかえす時間間隔を格納します。
Now
は現在の日付・時刻を取得する関数です。
TimeValue
は引数に文字列で表示された時刻の日付型のデータ、TimeValue("1:00:00")
の場合は1時間という日付型のデータを返します。
TargetTime = Now + TimeValue("1:00:00")
で、変数に現在から1時間後の時刻を格納します。
Call S_SpeakTrainInfo_Core
は、このプログラムのコア(中核)となる「Excelに電車の運行情報をしゃべらせる」サブプロシージャを呼び出して実行します。
Application.OnTime
メソッドは、指定した時間TargetTiime
に指定したプロシージャS_SpeakTrainInfo
をよびだします。
一定間隔で自動的に「Excelに電車の運行情報をしゃべらせる」にはすこしだけ工夫が必要です。
メインプロシージャをよびだした瞬間からかならず1時間後の時刻を取得して、1時間後に再度メインプロシージャそのものを呼び出します(再帰的呼び出し)。
これは意識的に無限ループをつくっていることになります。
1時間ごとにメインプロシージャを呼び出すことを無限にくりかえします。
ふつうの場合、無限ループはよくないプログラムの例としてよく出されますが、この場合はあえて無限にくりかえす(1時間ごとにサブプロシージャを呼び出す)プログラムなので、例外的に悪くはないプログラムです。
しかし、このままでは永遠におわらないプログラムであることはたしかです。
でも、カンタンにこのプログラムを終了する方法があります。
Excelそのものを終了しましょう。
そうすることでリセットされ、このプログラムの実行はなかったことになります。
おわりに
今回は、参照設定とWindows API関数の宣言、メインプロシージャの解説をしました。
VBAらしいことは、Application.OnTime
メソッドくらいでしたね。
どんなプログラムでもそうだと思いますが、ある程度の目的をもったプログラムは、かならず前処理が必要になってきます。
その前処理と本題のプロシージャはきりはなしてることが普通だとおもいます。
次回は本題のプロシージャに入っていきたいとおもいます。
Excel VBAでIEを操作して電車の運行情報をしゃべらせるマクロその2
はじめに
今回はきのうの続きです。
きのうは、マクロをまず書きはじめるまえに、あたまの中で(紙の上で、あるいはVBEにコメントを残すかたちで)考えるべきことを書きました。
要は、全体を把握しながら細部のプログラミングをすすめましょう、ということです。
じゃないと、「あれ、今なにやってるんだろ?」ということになりかねません。
全体像の把握力というか、イメージする力はプログラミングにも必要ですね。
プログミングの全体像と流れをVBEでコメントにしてみる
昨日、おおまかな流れを頭のなかでかんがえたので、それをコメントにしてみました。
Sub Excelに電車の運行情報をしゃべらせるマクロ() Call Excelに電車の運行情報をしゃべらせるマクロの中核 _ となるマクロ ' 一定の間隔でこのマクロを呼び出す命令文 End Sub Sub Excelに電車の運行情報をしゃべらせるマクロの中核 _ となるマクロ() Call 電車の運行情報をスクレイピングするマクロ Call Excelにセルの内容をしゃべらせるマクロ End Sub Sub 電車の運行情報をスクレイピングするマクロ() Call 山手線の運行情報をスクレイピングするマクロ Call 東急線の運行情報をスクレイピングするマクロ ' ブックを保存する命令文 End Sub Sub 山手線の運行情報をスクレイピングするマクロ() ' HTML文書 = HTML文書を取得する関数 ' HTML文書を分析する命令文 ' 必要な要素を取得する命令文 ' ワークシートに情報を落とし込む命令文 End Sub Sub 東急線の運行情報をスクレイピングするマクロ() ' HTML文書 = HTML文書を取得する関数 ' HTML文書を分析する命令文 ' 必要な要素を取得する命令文 ' ワークシートに情報を落とし込む命令文 End Sub Function HTML文書を取得する関数 As HTML文書 ' ExcelでIEを操作する命令文 ' IEでHTML文書を取得する命令文 End Function Sub Excelにセルの内容をしゃべらせるマクロ() ' セルの内容をしゃべらせる命令文 End Sub
これはあくまでもブログでの説明用です。じっさいに書く書かないはべつにして、あたまの中ではこう考えています。
そして、わたしが自分でコメントを書くときは、サブプロシージャやファンクションプロシージャの名前は最初から英語で、コメントは日本語で書きます。
ここからプログラムを具体化していきます。
具体化の手順のまえに全体のソースコードを確認
Option Explicit #If VBA7 Then Private Declare PtrSafe Function GetInputState Lib _ "user32" () As LongPtr #Else Private Declare Function GetInputState Lib _ "user32" () As Long #End If #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" _ (ByVal ms As LongPtr) #Else Private Declare Sub Sleep Lib "kernel32" _ (ByVal ms As Long) #End If Private Const YAMANOTE_LINE As String = _ "https://transit.yahoo.co.jp/traininfo/detail/21/0/" Private Const TOKYU As String = _ "http://www.tokyu.co.jp/i/unten_i.cgi" Public Sub S_SpeakTrainInfo() Dim TargetTime As Date TargetTime = Now + TimeValue("1:00:00") Call S_SpeakTrainInfo_Core Application.OnTime TargetTime, "S_SpeakTrainInfo" End Sub Public Sub S_SpeakTrainInfo_Core() Call S_ScrapeTrainInfo Call S_SpeakCellValue End Sub Public Sub S_ScrapeTrainInfo() With Worksheets("TrainInfo") .Activate .Range(.Columns(1), .Columns(3)).Delete End With Dim IE As InternetExplorer Set IE = CreateObject("InternetExplorer.Application") Call S_ScrapeTrainInfo_Yahoo(IE, YAMANOTE_LINE) Call S_ScrapeTrainInfo_Tokyu(IE, TOKYU) IE.Quit: Set IE = Nothing ThisWorkbook.Save End Sub Private Sub S_ScrapeTrainInfo_Yahoo _ (ByVal IE As InternetExplorer, _ ByVal URL As String) Dim Doc As HTMLDocument Set Doc = F_GetHTMLDoc(IE, URL, 10) With Worksheets("TrainInfo") Dim myRow As Long: myRow = 1 .Cells(myRow, 1).Value = _ Doc.getElementsByClassName("title")(0) _ .innerText & "運行情報" .Cells(myRow + 1, 1).Value = _ Doc.getElementsByClassName("subText")(0) _ .innerText If Not Doc.getElementsByClassName("normal")(0) _ Is Nothing Then .Cells(myRow + 2, 1).Value = _ Doc.getElementsByClassName("normal")(0) _ .innerText Else .Cells(myRow + 2, 1).Value = _ Doc.getElementsByClassName("trouble")(0) _ .innerText End If .Columns(1).AutoFit End With Set Doc = Nothing End Sub Private Sub S_ScrapeTrainInfo_Tokyu _ (ByVal IE As InternetExplorer, _ ByVal URL As String) Dim Doc As HTMLDocument Set Doc = F_GetHTMLDoc(IE, URL, 10) With Worksheets("TrainInfo") .Cells(1, 3).Value = _ Doc.getElementsByTagName("div")(0).innerText .Cells(2, 3).Value = _ Doc.getElementsByTagName("div")(1).innerText .Hyperlinks.Add _ Anchor:=.Cells(3, 3), _ Address:=URL, _ TextToDisplay:="東急線運行情報サイト" With .Columns(3) .ColumnWidth = 50 .AutoFit End With .Range(.Rows(1), .Rows(3)).AutoFit End With Set Doc = Nothing End Sub Private Function F_GetHTMLDoc _ (ByVal IE As InternetExplorer, _ ByVal URL As String, _ ByVal mySecond As Long, Optional _ ByVal Flag As Boolean = False) _ As HTMLDocument IE.Navigate URL IE.Visible = Flag Dim myTime As Date myTime = Now + TimeSerial(0, 0, mySecond) Do While IE.Busy = True Or _ IE.ReadyState <> READYSTATE_COMPLETE If GetInputState() = True Then DoEvents Sleep 1 If Now > myTime Then IE.Refresh myTime = Now + TimeSerial(0, 0, mySecond) End If Loop myTime = Now + TimeSerial(0, 0, mySecond) Do While IE.Document.ReadyState <> "complete" If GetInputState() = True Then DoEvents Sleep 1 If Now > myTime Then IE.Refresh myTime = Now + TimeSerial(0, 0, mySecond) End If Loop Set F_GetHTMLDoc = IE.Document End Function Public Sub S_SpeakCellValue() With Worksheets("TrainInfo") .Cells(1, 1).Speak .Cells(2, 1).Speak .Cells(3, 1).Speak .Cells(1, 3).Speak .Cells(2, 3).Speak End With End Sub
おわりに
きょうはここまで。次回は今回掲載したソースコードと具体化の手順をこまかくみていきましょう。
Excel VBAでIEを操作して電車の運行情報をしゃべらせるマクロその1
はじめに
ExcelでIEを自動的に操作して、Webサイトから電車の運行情報(平常通り運行しているか、事故・遅延情報はないかということ)を取得して、その内容をExcelに一定の時間間隔でしゃべらせてみたいと思います。
スクレイピングの大前提
プログラミング言語を用いて、Webサイトから情報を取得することをスクレイピング(Scraping)といいます。
当然のことながら、情報を取得したいWebサイトの内容が変更されれば、つくったマクロは無価値になり、あらたに作り直さなければならなくなります。
スクレイピングでは、Webサイトの内容が変更されていないことが前提となります。
サイトの内容の変更に追随するマクロもつくれないわけではないですが、最初はまず基本的なものから考えていきましょう。
電車の運行情報をしゃべらせる流れ
ざっと思い浮かぶだけでも、次のような処理が必要ですね。
- 取得するべきサイトを確認する
- ExcelでIEを操作する
- IEでHTML文書を取得する
- HTML文書を分析する
- 必要な要素を取得する
- ワークシートに情報を落とし込む
- シートの内容をしゃべらせる
これらを順番にみていきましょう。
取得するべきサイトを確認する
まずはカンタンなサイトを2つ選んでみました。
山手線の運行情報 - Yahoo!路線情報
欲しい電車の運行情報のサイトの構造が複雑な場合、Yahoo!路線情報を利用するのがいいでしょうね。
ほしい情報は現在、事故・遅延に関する情報はありません。
という文字列です。
この文字列を取得できるかどうかを確認していきます。
東急線運行情報
数ある電車の運行情報のサイトのなかでも、きわめてシンプルな構造のサイトです。
http://www.tokyu.co.jp/i/unten_i.cgiwww.tokyu.co.jp
ほしい情報は東急各線は、平常通り運転しています。
という文字列です。
この文字列を取得できるかどうかを確認していきます。
プログラミングの大きな流れをかんがえる
流れでいうと、3段階にわかれます。
頭のなかでやってもいいし、紙にかいてもいいし、VBEにコメントをかいてもいいと思います。
でも、なにも考えずにいきなり1行目からコードを書きはじめても、とっちらかってしまって、作業効率はおちるだけだと思います。
専門家は「上流工程」ということばをつかうようですが、ノンプログラマだからといって、上流工程を軽視して、いきなりコーディングをはじめていいわけではありません。
HTML文書を取得する
Excelで直接HTML文書を取得できるわけではないので、ExcelでIEを操作して、間接的にHTML文書を取得します。
ExcelでIEを操作する
ExcelでIEをうごかして、Webサイトを表示したり、IEの裏側でおこなっている操作を自動でおこなったりすることです。
やったことのないひとにとっては、まずここが「壁」になります。
しかし、なれてくれば単なる作業になります。
IEでHTML文書を取得する
ExcelでIEをうごかして、Webサイトを表示した時点で、HTML文書を取得したも同然ですから、そんなにむずかしい話ではありません。
もちろん、JavaScriptで動的に構成され、つぎからつぎへと変化するようなWebサイトのなかには取得がむずかしいサイトもあります。
さいしょは取得するのがカンタンそうなWebサイトのにおいをかぎわける必要がありそうです。
Webサイトをスクレイピングする
ここからがWebサイトごとにちがう実際のスクレイピング作業です。
HTML文書を分析する
ここはHTMLを実際に分析する作業と、それをVBAのコードにおとしこむ作業にわかれます。
必要な要素を取得する
HTML文書の分析結果をVBAのコードにおとしこんだものが実行されれば、自動的に「返り値」として要素を取得することができます。
ワークシートに情報を落とし込む
ここはExcelをつかっている強みで、取得した要素はシート上のセルに情報をカンタンに格納することができます。
特別になにか用意しなくても、セルに情報を展開すればいいので、こんなラクなことはありません。
シートの内容をしゃべらせる
Excelには、シート上のデータをしゃべらせる機能がありますので、カンタンにしゃべらせることができます。
おわりに
きょうはここまで。次回からはソースコードをみていきたいと思います。
PythonをVBAと比較しながら覚えようPart2
- はじめに
- 値の種類
- 演算子
- おわりに
はじめに
PythonをVBAと比較しながら覚えようのPart2です。
今回は「値」と「演算子」が中心ですね。
値の種類
値は、大きく数値、文字列、論理値にわかれます。
数値
PythonとVBAでは、あつかうことのできる数値に違いがあります。
Pythonにおける数値
Pythonでは、整数型・浮動小数点数型・2進数型・8進数型・16進数型・複素数型の数値を扱うことができます。
数値の型 | 数値の範囲 | 説明 |
---|---|---|
整数型 | 小数点以下がない数値 | 0と負の数を含む |
浮動小数点数型 | 小数点以下がある数値 | 0と負の数を含む |
2進数型 | 0と1の数字で表す | 0bを先頭につける |
8進数型 | 0〜8の数字で表す | 0oを先頭につける |
16進数型 | 0〜9・A〜Fで表す | 0xを先頭につける |
複素数型 | 実部+虚部 | 虚部にはj をつける |
ほんとはほかにもありますが、最初はこれでかまわないと思います。
整数
整数には正の数と負の数を含みますが、厳密には、マイナスの符号-
とプラスの符号+
は、それぞれ負の数、正の数を表す演算子です。-3
、+5
という表現も可能です。
浮動小数点数
浮動小数点数で、整数部が0
、あるいは小数点以下が0
のときは、.99
、10.
のように0
を省略できます。
また、整数と浮動小数点数との計算結果は、浮動小数点数になります。整数同士の割り算では、割り切れても結果は浮動小数点数になります。
大きな桁数の数値には指数表記も可能です。たとえば、12300000
は1.23e+7
、0.00096
は9.6e-4
です。eは大文字でもかまいません。e+7
は10の7乗、e-4
は10のマイナス4乗です。
2進数・8進数・16進数
2進数で0b0101
は10進数の5
、8進数で0o011
は10進数の9
、16進数で0xFF
は10進数の255
を表します。
虚数とは、2乗すると-1
のになる、実世界には存在しない想像上の数値(Imaginary Number)の単位です。複素数は虚数部分を含む数値です。人間の想像力はすごいですね。
VBAにおける数値
VBAでは、バイト型・整数型・長整数型・単精度浮動小数点型・倍精度浮動小数点型・通貨型・日付型の数値を扱うことができます。
VBAに多くの数値型があって、Pythonのほうが数値型がすくないのは意外ですね。実は、Pythonの組み込み型には数値型がすくないですが、datetime
という標準ライブラリがあって、日付や時間は値の種類ではなく、オブジェクトの種類であり、プロパティ(Pythonではアトリビュート)とメソッドで操作します。
VBAのバイト型・整数型・長整数型が、Pythonの整数型にあたります。
数値の型 | 数値の範囲 | 説明 |
---|---|---|
バイト型(Byte) | 0〜255 | 符号なし8ビットの整数 |
整数型(Integer) | -32,768~32,767 | 長整数型よりも範囲が狭い |
長整数型(Long) | -2,147,483,648~2,147,483,647 | 整数型よりも範囲が広い |
単精度浮動小数点型(Single) | -3.402823E38~-1.401298E-45(負)、1.401298E-45~3.402823E38(正)の範囲 | 倍精度浮動小数点型よりも範囲が狭い |
倍精度浮動小数点型(Double) | -1.79769313486232E308~-4.94065645841247E-324(負)、4.94065645841247E-324~1.79769313486232E308(正)の範囲 | 単精度浮動小数点型よりも範囲が広い |
通貨型(Currency) | -922,337,203,685,477.5808~922,337,203,685,477.5807 | 金額を扱う・整数型よりも扱える範囲が広い |
日付型(Date) | 西暦100年1月1日~西暦9999年12月31日 | 「1900/1/1 0:00」が内部値「1」となり、1日が「1」ずつ増える値をとる |
こうしてみると、VBAではそれぞれの型で扱える範囲をこまかく限定しているのがわかりますね。むかし、PCの性能がよくなかった頃の影響でしょうか。いまではその影響を考える必要がないくらい、PCの性能があがっていますが。
したがって、今では整数型・単精度浮動小数点型を使う積極的な理由はありません。長整数型・倍精度浮動小数点型を使うのがよいでしょう。
PythonとVBAの違い
Pythonでは、ふつうの数値にくわえて、2進数・8進数・16進数や複素数があつかえます。いっぽう、VBAでは用途によってこまかく数値の型を変更することができます。
演算子
数値や文字の演算に使います。
数値演算子
数値演算子も、PythonとVBAとで若干違いがあります。
Pythonの数値演算子
演算子 | 例 | 説明 |
---|---|---|
+ |
a + b | 足し算 |
- |
a - b | 引き算 |
* |
a * b | 掛け算 |
/ |
a / b | 割り算 |
// |
a // b | aをbで割った商の整数値(小数点以下を切り捨て) |
% |
a % b | aをbで割って、割り切れなかった余り(剰余) |
** |
a ** n | aをn回掛けた値(べき乗) |
ここにない演算には、mathモジュールが必要です。
VBAの数値演算子
演算子 | 例 | 説明 |
---|---|---|
+ |
a + b | 足し算 |
- |
a - b | 引き算 |
* |
a * b | 掛け算 |
/ |
a / b | 割り算 |
¥ |
a ¥ b | aをbで割った商の整数値(小数点以下を切り捨て) |
Mod |
a Mod b | aをbで割って、割り切れなかった余り(剰余) |
^ |
a ^ n | aをn回掛けた値(べき乗) |
Mod
だけが異質ですね。なにか理由があるのでしょうか。
文字列
シングルクォートとダブルクォート
Pythonでは、文字列をシングルクォートまたはダブルクォートで囲み、'シングルクォート'
、"ダブルクォート"
というように表現します。
VBAでは、文字列をダブルクォートで囲み、"ダブルクォート"
というように表現します。
VBAでのシングルクォートは、コメント行の開始を意味しますので、比較しながら覚える場合は注意が必要です。
エスケープシーケンス
Pythonのエスケープシーケンス
エスケープシーケンス | 説明 |
---|---|
\n | 改行(ラインフィード) |
\t | 水平タブ |
\r | キャリッジリターン |
\" | ダブルクォート |
\' | シングルクォート |
\\ | バックスラッシュ |
Windowsでは、\
は¥
で表示されます。
VBAのエスケープシーケンス
エスケープシーケンス | 説明 |
---|---|
vbLf | 改行(ラインフィード) |
vbTab | 水平タブ |
vbCr | キャリッジリターン |
vbCrLf | キャリッジリターン・ラインフィード |
"" | ダブルクォート |
シングルクォートはふつうにダブルクォートの中に埋め込むことができます | |
¥¥ | 円マーク |
複数行の文字列
Pythonにおける複数行の文字列
クォートを3回つづけて、'''〜'''
あるいは"""〜"""
のように囲むと、複数行の文字列を作ることができます。
poem = '''すすめの子 そこのけそこのけ お馬がとおる'''
すすめの子 そこのけそこのけ お馬がとおる
また、\n
を挿入すると、その部分で改行することもできます。
poem = 'すすめの子\nそこのけそこのけ\nお馬がとおる'
VBAにおける複数行の文字列
改行したい文字列と文字列を、エスケープシーケンスvbCrLf
で連結します。
ただし、セルのなかでは、エスケープシーケンスvbLf
で連結します。
Dim Poem As String 'セル以外の場合 Poem = "すずめの子" & vbCrLf & "そこのけそこのけ" & vbCrLf & "お馬がとおる" 'セル内の場合 Poem = "すずめの子" & vbLf & "そこのけそこのけ" & vbLf & "お馬がとおる"
すすめの子 そこのけそこのけ お馬がとおる
文字列の演算子
Pythonの文字列の演算子
演算子 | 例 | 説明 |
---|---|---|
+ |
'a' + 'b' | 文字列a とb の連結。文字列がab になる |
* |
'abc' * n | 文字列abc をn回繰り返す。nが2ならば、'abcabc'となる |
print('a' + 'b') print('abc' * 2)
ab abcabc
VBAの文字列の演算子
演算子 | 例 | 説明 |
---|---|---|
& |
"a" & "b" | 文字列a とb の連結。文字列がab になる |
VBAには、文字列を繰り返す演算子はありません。しかし、指定した1文字を繰り返すString
関数、指定した2文字以上を繰り返すREPT
関数、指定した数のスペースからなる文字列を返すSpace
関数があります。
MsgBox "a" & "b" MsgBox String(2, "ABC") MsgBox WorksheetFunction.Rept("abc", 2) MsgBox Space(5)
ab AA abcabc '空白文字が5回繰り返されている
数値と文字列を連結する
Pythonでは、数値と文字列を連結したい場合は、あらかじめ明示的に数値を文字列型に変換する必要があります。変換にはstr()
を使います。
数値と文字列をそのまま連結するとタイプエラー(TypeError)になります。
VBAでは、数値と文字列をそのまま連結しても問題ありません。VBAが自動的に型変換してくれます。
文字の取り出し
id = 'abc123xyz' print(id[0]) # 先頭から1文字目 print(id[2]) # 先頭から3文字目 print(id[-1]) # 後ろから1文字目 print(id[4:]) # 5文字目から最後まで print(id[4:4+4]) # 5文字目から4文字 print(id[:-7]) # 後ろから数えて7文字目の手前まで
a c z 23xyz 23xy c123xyz
文字列から文字を取り出すには[]を利用します。文字の位置は1文字目を0
と数え、マイナスは後ろから数えます。-1
が最後の文字です。
Dim id As String id = "abc1234xyz" MsgBox Left(id, 1) '先頭から1文字目 MsgBox Mid(id, 3, 1) '先頭から3文字目 MsgBox Right(id, 1) '後ろから1文字目 MsgBox Mid(id, 5) '5文字目から最後まで MsgBox Mid(id, 5, 4) '5文字目から4文字 MsgBox Right(id, 7) '後ろから数えて7文字目の手前まで
a c z 23xyz 23xy c123xyz
Pythonでは、文字列を文字の配列としてとらえて文字を取り出していますが、VBAでは、Left
関数・Mid
関数、Right
関数で取り出しています。また、VBAでは文字の位置は1文字目を1
と数えています。
論理値
論理値は、PythonでもVBAでもTrue
とFalse
です。
Pythonでは、1
がTrue
、0
がFalse
です。
VBAでは、-1
がTrue
、0
がFalse
です。
比較演算子
Pythonの比較演算子
演算子 | 例 | 説明 |
---|---|---|
== |
a == b | aとbが等しいとき、True |
!= |
a != b | aとbが等しくないとき、True |
> |
a > b | aがbより大きいとき、True |
>= |
a >= b | aがb以上のとき、True |
< |
a < b | aがbより小さいとき、True |
<= |
a <= b | aがb以下のとき、True |
aとbが等しいときは==
を使い、等しくないときは!=
を使います。
VBAの比較演算子
演算子 | 例 | 説明 |
---|---|---|
= |
a = b | aとbが等しいとき、True |
<> |
a <> b | aとbが等しくないとき、True |
> |
a > b | aがbより大きいとき、True |
>= |
a >= b | aがb以上のとき、True |
< |
a < b | aがbより小さいとき、True |
<= |
a <= b | aがb以下のとき、True |
aとbが等しいときは=
を使い、等しくないときは<>
を使います。
右結合・左結合
Pythonの比較演算子には右結合・左結合といった概念がなく、常に両隣の値を比較して、その論理積が式の値となるため、次のような式が可能です。
age = 16 if 13 <= age <= 20: print('True')
VBAで同じことをやろうとすると、次のような式になります。
Dim Age As Long Age = 16 If (13 <= Age) And (Age <= 20) Then MsgBox "True" End If
Pythonの論理演算子
Pythonには、and
、or
、not
、という3つの論理演算子があります。
演算子 | 例 | 説明 |
---|---|---|
and | a and b | 論理積。aかつbの両方がTrueのときTrue。一方でもFalseならFalse |
or | a or b | 論理和。aまたはbのどちらか一方でもTrueならばTrue。両方ともFalseならばFalse |
not | not a | 否定。aがTrueならばFalse。aがFalseならばTrue |
# 論理積 >>> True and True True >>> True and False False # 論理和 >>> True or True True >>> True or False True >>> False or False False # 否定 >>> not True False >>> not False True # 変数aが50以上かつ100以下のときTrue >>> a = 80 >>> (a >= 50) and (a <=100) True >>> a = 110 >>> (a >= 50) and (a <=100) False >>> a = 'NG' ; b = 'OK' >>> (a == 'OK') or (b == 'OK') True >>> True + False 1 >>> True + True 2 >>> 1 and 1 1 >>> 1 or 0 1 >>> 2 or 3 # 左項を採用 2 >>> 2 and 3 # 右項を採用 3
Pythonのビット演算子
ビット演算子は2進数の値をビットごとに演算します。ビット演算ではビットの合成や打ち消し(ビットの立て落とし)ができるので、画像の合成などで活用されます。
論理積・論理和・排他的論理和
演算子 | 例 | 説明 |
---|---|---|
& |
a&b |
論理積(AND)。両ビットともに1 のとき1 |
| |
a|b |
論理和(OR)。どちらかのビットが1 ならば1 |
^ |
a^b |
排他的論理和(XOR)。比較したビットの値がことなるとき1 |
~ |
~a |
ビット反転(NOT)。ビットの1 、0 を反転させる |
左シフト・右シフト
演算子 | 例 | 説明 |
---|---|---|
<< |
a<<1 |
左シフト。ビットを左にずらす。値は2倍になる |
>> |
a>>1 |
右シフト。ビットを右にずらす。値は1/2になる |
直接の意味としては、コンピュータの処理能力の低かった時代にかけ算・わり算のかわりに高速な左シフト・右シフトをつかったり、ハードウェア寄りの処理をおこなうときに用いたりするのですが、必要にせまられなければ、いまどき使うこともないでしょう。
>>> a = 0b001011 >>> a 11 >>> a << 1 # 左に1桁シフト 22 >>> a = 0b001011 >>> bin(a << 1) '0b10110'
ビットマスク
必要な部分を1
にした値とAND
する(論理積を求める)ことで必要なビットをぬきだすことができます。
下3桁のビットマスク
>>> a = 0b100110 >>> bin(a & 0b111) '0b110'
途中のビットのビットマスク
>>> a = 0b10101 >>> bin((a>>1) & 0b11) '0b10'
0b10101
を0b1010
にしておいて0b11
とビットマスク演算をすることで、もとの数値の3桁目と2桁目の10
を取り出せます。
VBAの論理演算子・ビット演算子
VBAの論理演算子は、ビット演算にもつかいます。
IF
文などにつかわれるAnd
、Or
、Not
のつかいかたは簡単ですのでここでは省略します。
VBAのビット演算でいちばんよく使われるのは、ビット落としなどをつうじて、ファイルの属性変更(読み取り専用属性の解除)をおこなうときなどです。
演算子 | 例 | 説明 |
---|---|---|
And |
a And b | 論理積。両ビットともに1 のとき1 |
Or |
a Or b | 論理和。どちらかのビットが1 ならば1 |
Not |
Not a | ビット反転。ビットの1 、0 を反転させる |
Xor |
a Xor b | 排他的論理和。ビットが一致しないときは1 |
Eqv |
a Eqv b | 論理等価。ビットが一致するときは1 |
Imp |
a Imp b | 論理包含。(Not A) Or B 。 |
Eqv
とImp
はほとんど使われないので、おぼえる必要もないと思います。
?True And True True ?True And False False ?True Or True True ?True Or False True ?False Or False False ?Not True False ?Not False True ?True + False -1 ?True + True -2 ' 数値の論理演算はビット演算になる ?1 and 1 1 ?1 or 0 1 ?2 or 3 '"10" or "11" 論理和を求める 3 '"11" ?2 and 3 '"10" and "11" 論理積を求める 2 '"10"
なお、VBAで左シフト・右シフトをおこなうときは、BITLSHIFT
関数、BITRSHIFT
関数をつかいます。
型変換
Pythonでは、値の型については最低限のことを知っておけばそれで足ります。
VBAでは、値の型については通常の型以外に、なんでも入るVariant
型、総称オブジェクト型のObject
型、などについても知っておく必要があります。
VBAでの型変換については、自動キャスト(型変換)がおこなわれるので、あまり意識する必要はないといえます。
型を調べる
Pythonでは、値の型はtype()
でしらべられます。
>>> type(1) <class 'int'>
これは数値1
の型がint型であることをしめしています。
>>> n = 12.3 ; name = '山田' >>> type(n) <class 'float'> >>> type(name) <class 'str'>
浮動小数点数の型はfloat
、文字列の型はstr
です。
VBAでは、値の型はTypeName
関数やVarType
関数でしらべられます。
Dim a As Integer a = 1 MsgBox TypeName(a) ' Integer(文字列)を返す MsgBox VarType(a) ' 2(vbInteger)を返す
TypeName
関数の戻り値
文字列 | 内容 |
---|---|
WorkBook | ブック |
Worksheet | シート |
Range | セル |
Chart | グラフ |
TextBox | テキストボックス(ActiveXコントロール) |
Label | ラベル(ActiveXコントロール) |
CommandButton | コマンドボタン |
Object | オブジェクト |
Unknown | 種類が不明なオブジェクト |
Nothing | オブジェクト変数の初期値 |
VarType
関数の戻り値
値 | 定数 | 内容 |
---|---|---|
0 | vbEmpty | 値(未初期化) |
1 | vbNull | 値(無効な値) |
2 | vbInteger | 整数型 |
3 | vbLong | 長整数型(long) |
4 | vbSingle | 単精度浮動小数点数型(Single) |
5 | vbDouble | 倍精度浮動小数点数型(Double) |
6 | vbCurrency | 通貨型(Currency) |
7 | vbDate | 日付型(Date) |
8 | vbString | 文字列型 |
9 | vbObject | オートメーションオブジェクト |
10 | vbError | エラー型 |
11 | vbBoolean | ブール型(Boolean) |
12 | vbVariant | バリアント型 (Variant) (バリアント型配列にのみ使用) |
13 | vbDataObject | 非オートメーションオブジェクト |
17 | vbByte | バイト型 |
8192 | vbArray | 配列(Array) |
数値を文字列に変換する
数値を文字列と連結しようとするとエラーになりますので、str()
をつかって数値を文字列に変換します。
>>> len = 10 * 1.23 >>> ans = '長さ' + str(len) + 'cm' >>> ans '長さ12.3cm' >>> ans = '5<10は' + str(5<10) + 'です。' >>> ans '5<10はTrueです。'
VBAではつぎのようにします。
Dim len As Single len = 10 * 1.23 Dim ans As String ans = "長さ" & CStr(len) & "cm" MsgBox ans ' 「長さ12.3cm」を返す Dim ans As String ans = "5<10は" & CStr(5 < 10) & "です。" MsgBox ans '5<10はTrueです。
いろいろな型変換
関数名 | 内容 |
---|---|
str() | 文字列型への型変換 |
int() | 整数型への型変換 |
float() | 浮動小数点数への型変換 |
bool() | 論理値への型変換 |
bin() | 2進数の文字列に変換 |
oct() | 8進数の文字列に変換 |
hex() | 16進数の文字列に変換 |
>>> int('250') * 3 # 整数に型変換 750 >>> float('1.5') + 0.2 # 浮動小数点数に型変換 1.7 # 浮動小数点数を整数化すると小数点以下は切り捨てられる >>> int(12.9) 12 >>> bin(10) '0b1010' >>> oct(10) '0o12' >>> hex(10) '0xa'
VBAの場合はつぎのようになります
関数名 | 内容 |
---|---|
CBool | ブール型(Boolean)へのデータ変換 |
CByte | バイト型(Byte)へのデータ変換 |
CCur | 通貨型(Currency)へのデータ変換 |
CDate | 日付型(Date)へのデータ変換 |
CDbl | 倍精度浮動小数点数(Double)へのデータ変換 |
CInt | 整数型(Integer)へのデータ変換 |
CLng | 長整数型(Long)へのデータ変換 |
CSng | 単精度浮動小数点数型(Single)へのデータ変換 |
CVar | バリアント型(Variant)へのデータ変換 |
CStr | 文字列型(String)へのデータ変換 |
MsgBox CInt("250") * 3 ' 750を表示 MsgBox CSng(”1.5”) + 0.2 ' 1.7を表示 '浮動小数点数を整数化するときに直接「CInt」関数をつかってはならない MsgBox CInt(RoundDown(Val("12.9")) '2進数の数値に変換 '512以上の数値の場合にエラー発生 MsgBox WorksheetFunction.Dec2Bin(10)'1010を表示 '8進数の数値の変換 '-536870912〜536870911の範囲外の数値の場合エラー発生 MsgBox WorksheetFunction.Dec2Oct(10) '12を表示 '16進数の数値に変換 MsgBox WorksheetFunction.Dec2Hex(10)'Aを表示
変数
- 値を一時的に保管する箱のようなもの
- 箱の中身は変更することができる
- 変数をつかって式が書ける(実際はこちらの役割のほうが大きい)
変数の作成
Pythonの変数には宣言が不要。
値を代入することで変数が作成されます。
=
を代入演算子という。
>>> width = 20.0 >>> height = 10.0 >>> area = width * height / 2 >>> print(area) 100.0
VBAの変数には宣言が必要。
=
は代入演算子であり、等価演算子(Pythonでは==
)でもある。
Dim width As Single: width = 20.0 Dim height As Single: height = 10.0 Dim area As Single: area = width * height / 2 MsgBox area '100が表示される
変数名のつけかた
Pythonでは
- 半角英数と
_
(アンダーバー)でつけます。 - ひらがなやカタカナや漢字もつかえますが、一般的にはつかいません。
- 慣例として変数名は小文字でつけます。
- 慣例として定数名は大文字でつけます。
- 慣例として変数名は何をしめす値かがわかる名前をつけるようにします。
- わかりやすい変数名にするために、複数の小文字の単語と単語を
_
(アンダーバー)でつなぐ命名法がよくつかわれています。
VBAでは
- 文字 (英数字、漢字、ひらがな、カタカナ) と
_
(アンダーバー) でつけます。 - 日本語の変数名もよくつかわれます。
- 変数名の先頭の文字は、英字・漢字・ひらがな・カタカナのいずれかでなければなりません。
- わかりやすい変数名にするために、単語と単語の区切りを大文字にする命名法がよくつかわれます。
つかえない変数名
Pythonでは
- 変数名の1文字目には、数字・演算子・記号・予約語は使えません。
VBAでは
- 変数名の1文字目には、数字・演算子・記号・予約語は使えません。
大文字と小文字
Pythonでは
- 大文字と小文字は区別されます。
point_a
とpoint_A
は似た名前の別の変数です。
VBAでは
- 大文字と小文字は区別されません。
point_a
とpoint_A
という変数は同じスコープの中では同時に宣言することはできません。
変数の型
Pythonでは
- 変数の値には型がありますが、変数には型はありません。
- 文字列が入っている変数に数値を代入してもエラーにはなりません。
VBAでは
- 変数には型がありますが、どんな型の値でもいれられるバリアント型(Variant)の変数があります。
- オブジェクトならなんでもいれられる総称オブジェクト型(Object)もあります。
- 変数の型を宣言しない場合、すべての変数はバリアント型(Variant)になります。
- 一般的には
Option Explicit
を宣言して、変数の型宣言を強制する場合がおおいです。
定数
- 値を保管する箱のようなもの
- 箱の中身を変更することはできない
- 定数をつかって式が書ける
Pythonの場合
Pythonには定数がありません。慣習的に大文字で書いた変数を他言語でいう定数として扱います。
TRIANGLE_WIDTH = 100
VBAの場合
VBAの定数はConst
ステートメントで宣言します。
Const TRIANGLE_WIDTH As Integer = 100
複合代入演算子
まず、VBAには複合代入演算子はありません。
Pythonでは
>>> age = 10 >>> age = age + 1 >>> age 20
を次のように書きかえることができます。
>>> age = 19 >>> age += 1 >>> age 20
age = age + 1
とage += 1
とは同じ操作を意味します。
+=
のことを複合代入演算子といいます。
複合代入演算子の種類
演算子 | 例 | 説明 |
---|---|---|
+= | a += b | a = a + b。aにbをたした値を代入 |
-= | a -= b | a = a - b。aからbをひいた値を代入 |
*= | a *= b | a = a * b。aにbをかけた値を代入 |
/= | a /= b | a = a / b。aをbでわった値を代入 |
//= | a //= b | a = a // b。aをbでわった整数値を代入 |
%= | a %= b | a = a % b。aをbでわった余りを代入 |
**= | a **= b | a = a ** b。aをb回かけあわせた値を代入 |
+=を使って文字列を連結する
Pythonでは
>>> who = '猫' >>> text = '' >>> text += '我が輩は' >>> text += who >>> text += 'である。' >>> text '我が輩は猫である。'
VBAには複合代入演算子がないので
Dim who As String: who = "猫" Dim text As String: text = "我が輩は" text = text & who text = text & "である。" MsgBox text '我が輩は猫である。
変数に値を代入するとは
>>> walllet_1 = 100 >>> wallet_1 100 >>> wallet_2 = wallet_1 >>> walllet_2 100 >>> wallet_1 100
wallet_1
の値をwallet_2
に代入しても、wallet_1
の値は100
のままです。これはPythonでも、VBAでもおなじです。
これを「値渡し」といいます。「値」のコピーを渡すイメージですね。
これに対して「参照渡し」という引数の渡し方もあります。
この違いは「参照渡し メモリ番地」という語で検索するとたくさん説明がでてきますよ。
おわりに
「演算子」では意外とPythonとVBAに違いがでてきましたね。
Pythonはなんでもできるスクリプト言語、VBAはExcelというオブジェクトを操作するのに適した言語、という違いがでてきてだんだんと比較するのがむずかしくなってきたな、という印象です。
「Windows PC + JISキーボード」から「Mac + USキーボード」に変えてよかった点
きっかけ
きっかけは「iPhone」でした。
それまでガラケーをつかっていたのが、はじめてのスマホとして2013年に「iPhone 4s」を購入。
以来、ずっとiPhoneをつかってます。
その母艦として、当初は2万円で購入した「ThinkPad Eシリーズ」の中古をつかっていたのですが、Windows版「iTunes」と「iCloud」の、原因不明な誤動作になやまされ、ネットで「MacBook」について検索した結果、「MacBook Pro」の購入にふみきりました。
結論
用途によって使い分けましょう。
わたしも今はMacがメインですが、いつWindowsがメインになるか、わかりません。
いまのところ、自宅でつかうのがWindowsメインになるとはおもいませんが。
変遷
「MacBook Pro」を購入するまでは、「Windows PC」一辺倒で、直近は「ThinkPad Eシリーズ」をつかっていました。
2014年に「MacBookPro 15inch 2014」を購入。その当時は、自宅ではあまりVBAプログラミングをおこなっていなかったので、おもに「iPhone」の母艦として、ネットサーフィンの道具としてつかっていました。
また、「Office for Mac 2011」を「MacBook Pro」に導入しただけでなく、「MacBook Pro」に「Parallels」を導入。「MacBook Pro」上でWindowsをつかえるようにしました。当然、「Microsoft Excel」もつかえるようにしました。
しかし、自宅で「Microsoft Excel」をつかうのは「家計簿」をつけるのがおもな用途だったので、「Parallels」もあまり起動しませんでした。
2017年にWordpressのブログを書きはじめて、自宅でVBAプログラミングをやるようになって、「ThinkPad X1 Carbon(2017モデル)」を購入。
メインでつかっていた「MacBook Pro」はサブマシンに降格。「ThinkPad X1C」をメインでつかうようになりました。会社でも自宅でもVBEでVBAプログラミングをおこなうようになり、特に不満もありませんでした。
サブマシンに降格した「MacBook Pro」は、おもに「YouTube」や動画配信サイトの動画視聴にのみ、つかっていました。
しかし、会社のPCのモニターは19インチ、自宅の「ThinkPad X1C」は14インチと、モニターの大きさには歴然とした差があり、そこに不満をいだくようになってきました。
そこで、2018年に「Python」と「はてなブログ」をはじめるにあたり、モニターが15インチと「ThinkPad」よりも大きい「MacBook Pro」に「Python」の環境を構築するのと同時に、メインマシンを「MacBook Pro」に変更しました。
MacとWindowsのちがい
プログラミングや画像・映像・音楽制作などをせずに、ネットサーフィンやメール、YouTubeなどの動画視聴をおこなっているかぎり、特段ちがいはありません。
むしろ、Windowsをしらないなら、Macのほうがつかいやすいでしょう。
特にトラックパッドはMacのほうがつかいやすい。Windows PCはマウスなしでは絶対に操作できませんが、Macにはマウスは不要です。
WindowsのトラックパッドがMacと遜色ないというのはウソですから、信じない方がいいです。
「ThinkPad X1 Carbon(2017モデル)」のトラックパッドは、それまでのWidows PCからすると素晴らしい出来上がりですが、それ以上にトラックポイントの出来上がりがすばらしいので、トラックパッドを使う気にはなりません。
Windowsユーザにとって、Macがつかいにくい理由はただひとつ。
「Windowsに慣れていること」です。「Windowsとちがうこと」がただひとつの理由です。
つまり、「Windowsの操作性が保証されること」が必要なかたは、MacやChromeBookなどには手を出さないほうがいいでしょう。
Macをつかってはいけないひと
- Windows版しかないソフトウェアを多用する方
- PCゲームをする方
- PCを自作する方
などは、Macには不向きです。
USキーボードの特徴
「かな」刻印がない
「かな」入力の方には致命的な欠点になりえますが、「ローマ字」入力の方にはキートップがすっきりしていて、わかりやすいでしょう。
キーの数がすくない
「変換」や「無変換」、「ローマ字」キーなどのキーがないので、キー全体の数がすくなくなってます。その分、スペースキーが長くなったり、キーボードの右側のキーが不自然にちいさくなってることもなくなってます。
キーの大きさは、そのおかげで自然な大きさになってると感じます。
日本語入力の起動が「Command + Space」である
「漢字」キーがないため、日本語入力の起動キーは「Command + Space」に割り当てられています。
これは面倒なので、「Karabiner Elements」というソフトウェアをつかって、「右Command」キーに日本語入力オンを、「左Command」キーに日本語入力オフを設定しました。
もともと、Windows PCのときも、「変換」キーに日本語入力オンを、「無変換」キーに日本語入力オフを割り当てていたので、それに近い感覚で操作できるようになりました。
「Parallels」上の「Windows」では「alt-ime-ahk」というソフトウェアをつかって、似た感覚で操作できるようにしています。
ちなみに、「Parallels」上の「Windows」でのデフォルトの日本語入力のオンオフは、Alt + ~
(オルト + チルダ)です。「日本語Windows上でUSキーボードをつかう設定」に変更すると、そうなります。
記号の配置が合理的
あとから作られた日本語キーボードは、キーの数がおおいため、むりやり記号類をおしこめた印象がありますが、USキーボードは合理性を感じます。
ですので、日本語キーボードをつかっている方がUSキーボードに慣れるのは比較的カンタンですが、逆はむずかしいと思います。
あまりつかわない「チルダ」と「バッククォート」は左上に、おなじくあまりつかわない「\
」と「|
」は右上にあります。
いちばんよくつかう「カンマ」「ピリオド」「スラッシュ」「<
」「>
」「?
」は同じ位置にあります。
「セミコロン」と「コロン」、「シングルクォート」と「ダブルクォート」、「[
」と「]
」、「{
」と「}
」は、Shift
を押す押さないの感覚がおなじです。
「イコール」と「プラス」、「マイナス」と「アンダーバー」の関係も利用頻度からいえば、USキーの配置のほうがだんだん合理的におもえてきます。
日本語キーボードからUSキーボードにのりかえて、感覚的にいちばんとまどうことは「右かっこ」と「左かっこ」の配置でしょう。
日本語キーボードでは、「右かっこ」と「左かっこ」はそれぞれ数字の「8
」キーと「9
」キーに配置されていますが、USキーボードでは、、「右かっこ」と「左かっこ」はそれぞれ数字の「9
」キーと「0
」キーに配置されています。
つまり、日本語キーボードとUSキーボードでは、「右かっこ」と「左かっこ」の配置が1つずれているのです。
まったく違うのなら、かえってとまどうことはすくないと思います。「1つずれているだけ」という配置のちかさがかえって混乱をよぶと思います。
Macにしてよかった点(メリット)
操作しててたのしい
ThinkPadを操作しててたのしいとおもうことはありませんが、Macは思い入れがあるぶん、たのしいですね。
画面がおおきくて見やすい
14インチと15インチのたった1インチの違いなのですが、圧倒的にMacのほうが見やすいです。
ThinkPadのほうが横長で縦に短いためによけいそう感じるのかもしれません。
トラックパッドの操作性
上にも書きましたが、とにかく操作しやすいです。これに慣れたら、Windows PCに戻る気はしません。
正直「Windowsが必要なら、Parallels上でWindowsを動かせばいいじゃん」とおもってます。
そうすれば、Macのトラックパッドをつかえますからね。
Apple製品との相性がよい
とくに日本では「iPhone」がおおいだけにこのメリットは大きいとおもいます。
これからの時代、個人利用ではPCよりもスマホがメインになってくるとおもいます。
そういうライトなつかいかたならば、MacBookで十分でしょう。
わるかった点(デメリット)
これといってデメリットは感じません。
しかし、これからWindowsからMacへの移行をかんがえている方のためにかんがえてみると、
- コストがかかる(ハードウェア + Parallelsとその上でうごくWindows代)
- 会社ではWindowsというところがおおい
というところですが、わたしにとってはデメリットではなかったですね。
おわりに
個人的な感想では、
Mac使いには、Windowsも使えるひとがおおい
Windows使いには、Windowsしか使ったことがないひとが圧倒的におおい
という印象ですね。
VBAでカタカナをひらがなに変換するマクロ - 不渡届その4
はじめに
- ファイルはYahoo! ボックスに公開しているので、ここからダウンロードして自由につかってください。
- このファイルには「不渡届その1・その2・その3」で作成したマクロを含みます。
- 動作確認は、Offie 365 Solo + Windows 10でおこなっています。
- 動作は無保証です。
カタカナをひらがなに変換したい
不渡届では、法人名または個人名の「フリガナ」を書くと、「索引(かしら字)」というものを書かなければなりません。
「ハテナブログ」というフリガナに対して「は」というひらがな1文字を書くということです。
「ハテナブログ」 → 「は」
それをいちいち手書きすると面倒くさいので、VBAで自動的に表示してくれ、というリクエストがあり、開発することになりました。
変換するためのルール
- 法人名または個人名のフリガナの頭文字1文字をひらがなに変換する、というルール
- 濁音(「ば」など)、半濁音(「ぱ」など)は清音(「は」など)に変換する、というルール
- 「ヴ」を「う」に変換するというルール
この3つのルールにもとづいて、フリガナをひらがな1文字に変換します。
カタカナとひらがなについて研究する
カタカナをひらがなに変換するには、なんらかの関連性が必要ですが、パソコンであつかう文字にはそれぞれ文字コードがありますので、それを確認します。
文字コードを確認する2つの方法
いきなりですが、Excelには文字コードを確認する方法は2つあります。
1つはむかしながらの方法であるCODE
関数をつかう方法。これはWorksheetFunction
オブジェクトにふくまれていないので、VBAでつかうときはEvaluate
メソッドとともに実行する必要があります。
もう1つはExcel2013からの方法であるUNICODE
関数をつかう方法です。こちらはWorksheetFunction
オブジェクトにふくまれますので、VBAでつかうときはWorksheetFunction
オブジェクトとともに実行します。
CODE
関数も、UNICODE
関数も、文字列の1文字目の文字コードを取得します。
ここでは、UNICODE
関数をつかう方法で説明していきます。
ですので、Excel2013以降でないと、実行できません。
Excel2010で試したいときは、CODE
関数をつかって試してみてくださいね。
文字と文字コードを相互に変換する
UNICODE
関数をつかうと、UNICODE("ア")=12450
となって、文字を文字コードに変換できます。
文字を文字コードに変換して、カタカナとひらがなの関連性について研究するとしても、最終的に文字コードから文字に変換できなければなりません。
そのときは、文字コードを文字に変換するUNICHAR
関数をつかいます。
UNICHAR
関数をつかうと、UNICHAR(12450)="ア"
となって、文字コードを文字に変換することができます。
ちなみに、Excel2010ではUNICHAR
関数ではなく、CHAR
関数をつかいます。
カタカナとひらがなのそれぞれの文字の文字コードをしらべてみる
UNICODE
関数をつかって文字コードをしらべてみます。
上の図は、ぜんぶのカタカナとひらがなの文字コードをExcel上で一覧表にしたものです。
カタカナについては
列名 | 内容 | 列名 | 内容 |
---|---|---|---|
A列 | カタカナの清音の文字 | B列 | その文字コード |
C列 | カタカナの濁音の文字 | D列 | その文字コード |
E列 | カタカナの半濁音の文字 | F列 | その文字コード |
ひらがなについては
列名 | 内容 | 列名 | 内容 |
---|---|---|---|
H列 | ひらがなの清音の文字 | I列 | その文字コード |
J列 | ひらがなの濁音の文字 | K列 | その文字コード |
L列 | ひらがなの半濁音の文字 | M列 | その文字コード |
カタカナとひらがなの関連性については
列名 | 内容 |
---|---|
O列 | カタカナの清音の文字コードの数値から、ひらがなの清音の文字コードの数値を引いた差の数値 |
一覧表からわかったこと
- カタカナの清音の文字コードの数値から、ひらがなの清音の文字コードの数値を引いた差の数値は
96
である - 濁音の文字コードは清音の文字コードに
1
をたしたものである - 半濁音の文字コードは濁音の文字コードに
1
をたしたものである - 半濁音の文字コードは清音の文字コードに
2
をたしたものである
以上の性質を利用して、マクロを作成します。
変換するためのルールを具体化してみる
- カタカナの文字列から
UNICODE
関数でその1文字目の文字コードを取得して、その文字コードから96
をひいた数値をUNICHAR
関数の引数にして、ひらがなの1文字を取得する - カタカナの濁音・半濁音は清音にしてからひらがなに変換する
- カタカナの「ヴ」は「ゔ」にできないで、「う」に変換する
この3つを具体的にみていきましょう。
カタカナをひらがなに変換する
これが「変換の大原則」です。
セル「J7」に入力されたフリガナをもとに、セル「B8」にひらがなの頭文字を入力します。
ソースコード
Private Sub Worksheet_Change(ByVal Target As Range) Select Case True Case Not Intersect(Target, Range("J7")) Is Nothing Dim Katakana As Range Dim Hiragana As Range Set Katakana = Range("J7") Set Hiragana = Range("B8") If Len(Katakana.Value) = 0 Then Hiragana.Value = "" Else Dim myCode As Long myCode = _ WorksheetFunction.Unicode(Katakana.Value) Hiragana.Value = F_NormalConverter(myCode) End If Set Hiragana = Nothing Set Katakana = Nothing End Select End Sub Private Function F_NormalConverter(ByVal myCode As Long) _ As String F_NormalConverter = _ WorksheetFunction.Unichar(myCode - 96) End Function
解説
Worksheet_Change
サブルーチンをつかって、フリガナを入力するセル「J7」の値が変化したときに、セル「B8」の値を変化させます。
セル「J7」にはフリガナを入力するので、オブジェクト変数Katakana
にセル「J7」を代入します。
セル「B8」にはひらがなの頭文字を代入するので、オブジェクト変数Hiragana
にセル「B8]を代入します。
なれてないうちは、「文字列型変数」にセル「J7」・「B8」の値を代入しがちですが、VBAになれてきたら、ぜひとも「オブジェクト型変数」を活用しましょう。
オブジェクト型変数のほうが、プロパティとメソッドをつかうことができるので、自由がきいて使いやすいです。
Katakana
の値をDELETE
キーで消去したときは、Hiragana
の値を消去します。
そうでないときは、フリガナが入力されている状態なので、Katakana
セルの値をUNICODE
関数で文字コードに変換して、その値を変数myCode
に代入します。
myCode
はカタカナの文字コードの値なので、その値から96
を引くと、ひらがなの文字コードの値になります。その値をF_NormalConverter
ファンクション・プロシージャに代入しします。
F_NormalConverter
ファンクション・プロシージャでは、myCode - 96
の値をUNICHAR
関数の引数にして、ひらがなの文字列を取得します。取得したその値はF_NormalConverter
ファンクション・プロシージャの返り値になりますので、それをHiragana
セルに代入します。
カタカナの濁音・半濁音は清音にしてからひらがなに変換する
これはカタカナの文字列を文字コードにもとづいて分類し、変換の大原則に還元してやるということです。
これは一覧表をみて考えるとわかりやすいと思います。
カタカナの濁音・半濁音は清音にするということから始まります。
一覧表をみると、アイウエオ順と文字コードの順番は一致しています。
変換の大原則にしたがう場合
拗音の「ァ」から「カ」までは「変換の大原則」にしたがって変換することができます。これは文字コードが12449
から12459
までです。
同様に拗音の「ッ」の場合は文字コードが12483
であり、「ツ」の場合は文字コードが12484
であり、「ナ」から「ノ」の場合は文字コードが12490
から12494
であり、「マ」から「ン」の場合は文字コードが12510
から12531
です。
いずれの場合も、文字コードの数値から96
を引いた数値をUNICHAR
関数の引数にして、ひらがなの文字をもとめます。
濁音を清音に変換する場合
濁音の文字コードが偶数の場合と奇数の場合で処理がことなります。
濁音の文字コードが偶数の場合
「ガ」から「ヂ」までは、濁音を清音に変換してから、ひらがなに変換します。
その前に「ガ」の文字コードをみると12460
であり、「か」の文字コードをみると12459
です。
ほかの文字も同様であり、濁音の場合の文字コードは「偶数」であり、清音の場合の文字コードは「奇数」です。
したがって、文字コードの数値を「2」で割ってあまりが「0」の場合は濁音の文字コードですので、1
を引いてやると、清音の文字コードになります。
割り算をしてあまりを求めるには、MOD
演算子をつかいます。
Private Function F_TwoMod_1(ByVal myCode As Long) As Long If myCode Mod 2 = 0 Then myCode = myCode - 1 F_TwoMod_1 = myCode End Function
このファンクション・プロシージャをかましてやることによって、「変換の大原則」にしたがって変換することができます。
濁音の文字コードが奇数の場合
「ヅ」から「ド」までの場合、「ヅ」の文字コードをみると12485
であり、ほかの文字も同様であり、濁音の場合の文字コードは「奇数」であり、清音の場合の文字コードは「偶数」です。
したがって、文字コードの数値を「2」で割ってあまりが「1」の場合は濁音の文字コードですので、1
を引いてやると、清音の文字コードになります。
Private Function F_TwoMod_0(ByVal myCode As Long) As Long If myCode Mod 2 = 1 Then myCode = myCode - 1 F_TwoMod_0 = myCode End Function
このファンクション・プロシージャをかましてやることによって、「変換の大原則」にしたがって変換することができます。
濁音・半濁音を清音にする場合
例として、「ハ」と「バ」と「パ」の文字コードの関係を整理してみましょう。
「ハ」の文字コードは12495
であり、「バ」の文字コードは12496
であり、「パ」の文字コードは12497
です。
この場合、文字コードの数値を「3」で割ってあまりが「0」の場合は清音の文字コード、「3」で割ってあまりが「1」の場合は濁音の文字コード、「3」で割ってあまりが「2」の場合は半濁音の文字コードだということです。
したがって、「3」で割ってあまりが「0」の場合は、そのままで清音の文字コード。
つぎに、「3」で割ってあまりが「1」の場合は、「1」を引くと清音の文字コード。
さらに、「3」で割ってあまりが「2」の場合は、「2」を引くと清音の文字コード。
以上のことをマクロにするには、Select
文とMod
演算子をくみあわせます。
Private Function F_ThreeMod(ByVal myCode As Long) As Long Select Case myCode Mod 3 Case 1 myCode = myCode - 1 Case 2 myCode = myCode - 2 End Select F_ThreeMod = myCode End Function
カタカナの「ヴ」を変換する場合
カタカナの「ヴ」の文字コードは12532
なので、その場合にHiragana
の値を「う」にします。
まとめ
以上をまとめると、次のソースコードになります。
Private Sub Worksheet_Change(ByVal Target As Range) Select Case True Case Not Intersect(Target, Range("J7")) Is Nothing Dim Katakana As Range Dim Hiragana As Range Set Katakana = Range("J7") Set Hiragana = Range("B8") If Len(Katakana.Value) = 0 Then Hiragana.Value = "" Else Dim myCode As Long myCode = _ WorksheetFunction.Unicode(Katakana.Value) Select Case myCode Case 12449 To 12459, 12483 To 12484, _ 12490 To 12494, 12510 To 12531 Hiragana.Value = _ F_NormalConverter(myCode) Case 12460 To 12482 Hiragana.Value = _ F_NormalConverter(F_TwoMod_1(myCode)) Case 12485 To 12489 Hiragana.Value = _ F_NormalConverter(F_TwoMod_0(myCode)) Case 12495 To 12509 Hiragana.Value = _ F_NormalConverter(F_ThreeMod(myCode)) Case 12532 Hiragana.Value = "う" End Select End If Set Hiragana = Nothing Set Katakana = Nothing End Select End Sub Private Function F_NormalConverter(ByVal myCode As Long) _ As String F_NormalConverter = _ WorksheetFunction.Unichar(myCode - 96) End Function Private Function F_TwoMod_1(ByVal myCode As Long) As Long If myCode Mod 2 = 0 Then myCode = myCode - 1 F_TwoMod_1 = myCode End Function Private Function F_TwoMod_0(ByVal myCode As Long) As Long If myCode Mod 2 = 1 Then myCode = myCode - 1 F_TwoMod_0 = myCode End Function Private Function F_ThreeMod(ByVal myCode As Long) As Long Select Case myCode Mod 3 Case 1 myCode = myCode - 1 Case 2 myCode = myCode - 2 End Select F_ThreeMod = myCode End Function
おわりに
今回のリクエストがあるまで、カタカナとひらがなの文字コードの関係、清音と濁音と半濁音の関係などについて、しらべたこともありませんでしたが、しらべてみたら明確な数値の関係があり、意外とおもしろかったですね。
もっとも、文字コードの世界は深いので、これ以上掘るよりはほかのことを深掘りしようと思います。
セルの入力規則をVBAで設定する - 不渡届その3
はじめに
- ファイルはYahoo! ボックスに公開しているので、ここからダウンロードして自由につかってください。
- このファイルには「不渡届その1・その2」で作成したマクロを含みます。
- 動作確認は、Offie 365 Solo + Windows 10でおこなっています。
- 動作は無保証です。
セルの入力規則をVBAで設定したい
セルの入力規則はいちど設定すると変更することはあまりないと思いますが、うっかり設定を変更してしまうこともあるとおもいます。
そういうときに困るのはユーザなので、そういうことがないようにVBAでファイルの起動時に毎回入力規則を設定するのがいいでしょう。
設定するには
- 標準モジュールに入力規則を設定するマクロを書きます。
- ThisWorkbookモジュールにそのマクロを起動する命令文を追加します。
セルの入力規則を設定するマクロ
ソースコード
Sub S_SetIMEMode() Application.ScreenUpdating = False Worksheets("NoticeOfDishonor").Unprotect Range("J8, B8, D19, O19, N10, O12, D16").Select With Selection.Validation .Delete .Add Type:=xlValidateInputOnly .IMEMode = xlIMEModeHiragana End With Range("J7, P11").Select With Selection.Validation .Delete .Add Type:=xlValidateInputOnly .IMEMode = xlIMEModeKatakana End With Range("G7, AD10, I13, S16, W16").Select With Selection.Validation .Delete .Add Type:=xlValidateInputOnly .IMEMode = xlIMEModeAlpha End With Range("G8, Y8").Select With Selection.Validation .Add Type:=xlValidateList, Formula1:="=$B$30:$B$33" .IMEMode = xlIMEModeHiragana End With Range("AD9").Select With Selection.Validation .Add Type:=xlValidateList, Formula1:="=$B$42:$B$46" .IMEMode = xlIMEModeHiragana End With Range("AE15").Select With Selection.Validation .Add Type:=xlValidateList, Formula1:="=$B$56:$B$58" .IMEMode = xlIMEModeHiragana End With Range("G10").Select With Selection.Validation .Add Type:=xlValidateList, Formula1:="=$B$36:$B$39" .IMEMode = xlIMEModeHiragana End With Range("AG3").Select Worksheets("NoticeOfDishonor").Protect _ AllowFormattingCells:=True Application.ScreenUpdating = True End Sub
解説
画面のちらつきを抑えたい
Application.ScreenUpdating = False 'セルに入力規則を設定する処理 Application.ScreenUpdating = True
いくつものセルを選択するので、画面がちらつきます。
それを抑えるために、Application.ScreenUpdating
プロパティにFalse
を設定して、画面の再描画を停止して、ちらつきを抑えます。
入力規則を設定しおえたら、Application.ScreenUpdating
プロパティにTrue
を設定して、画面の再描画を再開します。
シートの保護
ふだんはシートを保護していますが、セルの入力規則を設定するときだけ、シートの保護を解除します。
Worksheets("NoticeOfDishonor").Unprotect 'セルに入力規則を設定する処理 Worksheets("NoticeOfDishonor").Protect _ AllowFormattingCells:=True
シートの保護をするときには、ロックしているセルの選択をできないようにして、ロックのかかっていない、これから値を入力したり、変更したりするセルだけを選択できるようにします。
また、文字の大きさを変更できるように、AllowFormattingCells:=True
でセルの書式設定をできるようにしておきます。
セルの入力規則
Excelの標準機能で設定している入力規則をVBAで設定します。
入力規則の設定方法
Range("入力規則を設定するセル").Select With Selection.Validation .Delete .Add Type:="入力規則の種類" .IMEMode = "日本語の入力規則の内容" End With
マクロの自動記録をとると、いったん入力規則をDelete
してから、再度設定していますので、このとおりやりましょう。
また、いったんセルを選択しないとうまくいかないようです。
よくつかう入力規則の種類
入力規則の種類 | 引数 |
---|---|
xlInputOnly | 引数はとくに不要。自由に入力できます |
xlValidateList | 引数 Formula1 を必ず指定します。引数 Formula1 には、コンマで区切った値の一覧またはこの一覧へのシート参照を指定する必要があります |
よくつかう日本語の入力規則の内容
定数 | 内容 |
---|---|
xlIMEModeAlpha | 半角英数字 |
xlIMEModeHiragana | ひらがな |
xlIMEModeKatakana | カタカナ |
入力規則の種類と日本語の入力規則の内容をくみあわせる
どんな文字でも入力してもいいかどうか、リストから選択するか。
半角英数字を入力するのか、ひらがなからかな漢字変換をつかうのか、カタカナを入力するのか。
それぞれのセルによって、設定はちがうので、まずセルを選択して、そのセルにあう内容を設定します。
おわりに
ノンプログラマにとっては、入力規則やシート保護、書式設定などもVBAとおなじか、それ以上に重要です。
VBAをつかって特殊なことを実現することよりも、標準の機能を便利につかえるようにするためにVBAを利用することのほうが優先順位がたかいことはよくあります。
ブックやシートやセルに関する設定を操作することは、VBAの独壇場です。
ほかの言語でもできることをムリにVBAですることよりも、VBAにしかできない、ブックやシートやセルといったExcel固有のオブジェクトを自由に操作できるようにすることが重要だと思います。
特定のセルだけを「Enter」キーで巡回するマクロ - 不渡届その2
- はじめに
- キーボードだけで入力作業を完結したい
- 実現するには
- 特定のセルを巡回するマクロ
- 特定のセルを「逆順で」巡回するマクロ
- 巡回マクロを「Enter」キーに登録・解除するマクロ
- 「ThisWorkbook」モジュールに登録するマクロ
- おわりに
はじめに
- ファイルはYahoo! ボックスに公開しているので、ここからダウンロードして自由につかってください。
- このファイルには「不渡届その1」で作成したマクロを含みます。
- 動作確認は、Offie 365 Solo + Windows 10でおこなっています。
- 動作は無保証です。
キーボードだけで入力作業を完結したい
不渡届を作成する際に、マウスをつかいたくない、というリクエストがありました。
キーボードから手をはなさずに作業したいけど、さすがにホームポジションをまったく崩さずに作業するのはムリなので、Enter
キーや、Alt
キーなどの修飾キー、矢印キーなどはつかってよいことにして、特にEnter
キーでセルの移動をできるようにしたい、といわれました。
実現するには
まず、特定のセルを巡回するマクロを作成します。
たとえば、セルAG3
からセルG8
に、セルG8
からセルJ8
に、セルJ8
からセルAG3
へと巡回します。
ついでに、逆順に巡回するマクロも作成します。
そのつぎに、その巡回マクロをEnter
キーに登録するマクロ・解除するマクロを作成します。
さいごに、ブックモジュールに、ブックを開いたときと閉じるときに自動的に登録・解除するマクロを作成します。
特定のセルを巡回するマクロ
セルを巡回する順番をきめます。
AG3→G8→J8→Y8→J7→B8→AD9→AD10→AE15→D19→O19→G10→ N10→I13→O12→P11→D16→S16→W16→AG3→以下、おなじ
これをマクロに落とし込みます。AG3
がアクティブのときはG8
を選択、G8
がアクティブのときはJ8
を選択、というマクロをつくります。
マクロ名のS_IndicateEnterDirection
は「Enter
を押したときのセルの移動方向を決める」という意味ですが、自分がわかればなんでもいいと思います。
わたしはこのマクロをEnter
キーに登録するつもりなので、このマクロ名にしました。
ソースコード
Sub S_IndicateEnterDirection() Select Case ActiveCell.Address(False, False) Case "AG3" Range("G8").Activate Case "G8" Range("J8").Activate Case "J8" Range("Y8").Activate Case "Y8" Range("J7").Activate Case "J7" Range("B8").Activate Case "B8" Range("AD9").Activate Case "AD9" Range("AD10").Activate Case "AD10" Range("AE15").Activate Case "AE15" Range("D19").Activate Case "D19" Range("O19").Activate Case "O19" Range("G10").Activate Case "G10" Range("N10").Activate Case "N10" Range("I13").Activate Case "I13" Range("O12").Activate Case "O12" Range("P11").Activate Case "P11" Range("D16").Activate Case "D16" Range("S16").Activate Case "S16" Range("W16").Activate Case "W16" Range("AG3").Activate Case Else On Error Resume Next With ActiveCell Application.MoveAfterReturn = True Select Case Application. _ MoveAfterReturnDirection Case xlDown .Offset(1, 0).Select Case xlUp .Offset(-1, 0).Select Case xlToRight .Offset(0, 1).Select Case xlToLeft .Offset(0, -1).Select End Select End With End Select End Sub
解説
ActiveCell.Address(False, False)
でアクティブセルのアドレスを相対参照で取得します。
それをさきほど決めた順番にあてはめてコーディングします。
ただし、順番のなかにないセルの場合、Enter
をおしても全くうごかなくなってしまいます。
ですので、Case Else
のなかで他のセルの挙動を規定しています。
Application.MoveAfterReturn = True
で、Enter
キーを押したあとにアクティブセルを移動するように設定します。
Application.MoveAfterReturnDirection
は、「ファイル」メニュー→「オプション」→「詳細設定」→「Enterキーを押したら、セルを移動する(M)」という項目の「方向」オプションで選んだ「下」、「右」、「上」、「左」のどれが選択されているかを取得します。
xlDown
、xlUp
、xlToRight
、xlToLeft
のそれぞれのとき、「下」、「上」、「右」、「左」に1つセルを移動します。
特定のセルを「逆順で」巡回するマクロ
自分できめたセルを巡回する順番を逆に移動するだけですね。
マクロ名のS_IndicateShiftEnterDirection
は「Shift + Enter
を押したときのセルの移動方向を決める」という意味ですが、自分がわかればなんでもいいと思います。
わたしはこのマクロをShift + Enter
キーに登録するつもりなので、このマクロ名にしました。
ソースコード
Sub S_IndicateShiftEnterDirection() Select Case ActiveCell.Address(False, False) Case "AG3" Range("W16").Activate Case "W16" Range("S16").Activate Case "S16" Range("D16").Activate Case "D16" Range("P11").Activate Case "P11" Range("O12").Activate Case "O12" Range("I13").Activate Case "I13" Range("N10").Activate Case "N10" Range("G10").Activate Case "G10" Range("O19").Activate Case "O19" Range("D19").Activate Case "D19" Range("AE15").Activate Case "AE15" Range("AD10").Activate Case "AD10" Range("AD9").Activate Case "AD9" Range("B8").Activate Case "B8" Range("J7").Activate Case "J7" Range("Y8").Activate Case "Y8" Range("J8").Activate Case "J8" Range("G8").Activate Case "G8" Range("AG3").Activate Case Else On Error Resume Next With ActiveCell Application.MoveAfterReturn = True Select Case Application. _ MoveAfterReturnDirection Case xlDown .Offset(1, 0).Select Case xlUp .Offset(-1, 0).Select Case xlToRight .Offset(0, 1).Select Case xlToLeft .Offset(0, -1).Select End Select End With End Select End Sub
解説
解説はとくにありませんが、気をつけるとしたら、スペルミスくらいですかね。
だれも指摘してくれないと、けっこうな時間、気がつかないことがあります。
巡回マクロを「Enter」キーに登録・解除するマクロ
登録
巡回マクロをEnter
キーに登録するときは、Application.OnKey
メソッドを使います。
Application.OnKey "登録するキー", "登録するマクロ名"
登録するキーは、Enter
キーはエディタに表示されないので、特別なコードを使います。
キー | コード |
---|---|
Enter (メインキーボード) |
~ (チルダ) |
Enter (テンキー) |
{ENTER} |
「逆順」で巡回するマクロはShift + Enter
に登録するのですが、Shift
も特別なコードをつかいます。
キー | コード |
---|---|
Shift |
+ (プラス記号) |
以上をくみあわせると次のようなマクロになります。
Sub S_AddKeysToMacro() Application.OnKey "~", "S_IndicateEnterDirection" Application.OnKey "{Enter}", "S_IndicateEnterDirection" End Sub
逆順のマクロを登録するのは、次のようなマクロになります。
Sub S_AddKeysToMacro2() Application.OnKey "+~", _ "S_IndicateShiftEnterDirection" Application.OnKey "+{Enter}", _ "S_IndicateShiftEnterDirection" End Sub
解除
巡回マクロをEnter
キーから解除するときも、Application.OnKey
メソッドを使います。
Application.OnKey "解除するキー" 'マクロ名はなにも書かない
マクロ名を省略することで、登録していたマクロを解除する意味になります。
次のマクロは、Enter
キーからマクロを解除するものです。
Sub S_RemoveKeysFromMacro() Application.OnKey "~" Application.OnKey "{Enter}" End Sub
次のマクロは、Shift + Enter
キーからマクロを解除するものです。
Sub S_RemoveKeysFromMacro2() Application.OnKey "+~" Application.OnKey "+{Enter}" End Sub
「ThisWorkbook」モジュールに登録するマクロ
「セルを巡回するマクロ」を登録・解除するマクロを実行するタイミングは、このブックを開いたとき・閉じるときです。
次のマクロで「セルを巡回するマクロ」を登録・解除します。
Private Sub Workbook_Open() Call S_AddKeysToMacro Call S_AddKeysToMacro2 End Sub Private Sub Workbook_BeforeClose(Cancel As Boolean) Call S_RemoveKeysFromMacro Call S_RemoveKeysFromMacro2 End Sub
Workbook_Open
プロシージャで、このブックをひらくときに正順・逆順のセル巡回マクロを登録します。
Workbook_BeforeClose
プロシージャで、このブックをとじるときにマクロを解除します。
おわりに
実際にマウスなしで操作するには、「データの入力規則の設定」や「シートの保護」など、ほかにやることがありますが、とりあえず特定のセルを移動することは、今回のマクロでできるようになります。
郵便番号から住所を検索するマクロ(データ更新機能つき) - 不渡届その1
- 郵便番号を入力すると住所の一部を表示するマクロ
- この記事は
- 郵便番号データ
- ワークシートの例として不渡届をえらびました
- マクロの構成
- シートモジュールの郵便番号検索イベントプロシージャ
- ソースコード
- ソースコードの解説
- 「Worksheet_Change」プロシージャの使い方
- 「Intersect」プロパティの使い方
- 「If」文ではなく「Select」文をつかう
- 変数の宣言
- 郵便番号を入力するセルを変数「myCell」に代入
- 郵便番号が2段書きになっているかを確認
- セル内の文字列の長さによって条件分岐させる
- 文字列の長さが8文字の場合
- 文字列の長さが17文字の場合
- 文字列の長さが0文字の場合
- 文字列の長さがその他の場合
- セルの内容をクリアするサブプロシージャ「S_ClearWorksheet」
- 「Worksheet_Change」プロシージャの使い方
- ThisWorkbookモジュールの「Workbook_Open」プロシージャ
- 標準モジュールのデータ更新マクロ
- ソースコード
- ソースコードの解説
- おわりに
郵便番号を入力すると住所の一部を表示するマクロ
セルに郵便番号を入力してEnter
を押すと、該当する住所の一部とそのフリガナを表示するマクロを考えました。
たとえば、郵便番号のセルに「154-0001」とセルに入力してEnter
を押すと、住所のセルに「東京都世田谷区池尻」と表示されるようなものです。
VLOOKUP
のようなワークシート関数をつかうと超高速で表示されるのですが、実務上は「東京都世田谷区池尻」のあとに地番などを入力する必要があるので、関数はムリです。ちゃんと文字列が入力される必要があります。
そこでマクロをつかうことになりました。VLOOKUP
と同じスピードを確保して、セルには検索の結果が数式ではなく、文字列で入力されるという条件は、マクロを使わないと満たせませんね。
- ファイルはYahoo! ボックスに公開しているので、ここからダウンロードして自由につかってください。
- 動作確認は、Offie 365 Solo + Windows 10でおこなっています。
- 動作は無保証です。
この記事は
VBAの命令の1行1行はわかるけれども、いったいどう組み合わせたら実際に使えるマクロが書けるかわからない、と悩んでいる方にむけて書きました。
1つ1つはカンタンな命令しかないかもしれませんが、どう組み合わせるか、なにに配慮するかの例として読んでいただけたらと思います。
この記事は、かなり長いです。いろいろ説明を詰め込みすぎたかもしれません。
郵便番号データ
約15万件あり、日々更新されているのですが、便利なサイトがあったので、その公開データをもとに更新元となる郵便番号データを更新するマクロも同時に作成して、郵便番号データの陳腐化をふせぎます。
無料で郵便番号データを公開してくださっているありがたいサイトはいくつもありますが、このサイトもそのひとつです。ここにCSVファイルがありましたので、それをつかわせていただきます。ほかにもMDBファイルなどもありますが、ExcelであつかうにはCSVファイルがいいですね。
ワークシートの例として不渡届をえらびました
ノンプログラマが実際の業務であつかうExcelの帳票にはいろいろあるとおもいますが、これもそのひとつです。
わたしの会社でもいままで手書きだった不渡届がようやくExcelのファイルになりました。もちろん、手形交換所にはいまでも手書きで提出しますが。
ワークシートはなんでもいいと思いますが、ただセルに入力するだけでは自分がおもしろくないので、Web上に公開されている一般財団法人静岡県銀行協会のこのPDF文書の不渡届をもとに自分でエクセルファイルを作成しました。
実際の業務でつかっている不渡届は社外に出せないので、作成したのはその一部で、例として入力してあるデータはまっかなウソです。架空のデータを入力しています。実在の方とはなんの関係もありません。
マクロの構成
シートモジュールの郵便番号検索マクロ
実際に業務で使うマクロなので、パソコンにあまりくわしくないユーザへの気配りはかかせません。自分だけが使うわけではないので。
郵便番号を検索するマクロは、マクロを起動するトリガーが必要ですね。今回はボタンやショートカットにマクロを登録するのではなく、セルに郵便番号を入力して(セルの内容を変化させて)、Enter
を押すとマクロが起動して、住所とフリガナを表示するようにします。
だから、そのマクロはセルの内容の変化を検知するイベントプロシージャになり、「NoticeOfDishonor」という名前のシートのシートモジュールに書きます。
ThisWorkbookモジュールのマクロ
パソコンにあまりくわしくないユーザは、ブックをひらいたときに、まずどのセルを操作するのか探すのが苦痛です。それをやわらげるために、このブックをひらいたときに、不渡届のシートを選択し、郵便番号のセルを選択しておきます。
標準モジュールのデータ更新マクロ
これは自分で使うか、インターネット参照権限を持った管理者が使う(わたしの部署のほとんどの人はインターネットの参照権限がないばかりか、自分専用のパソコンもありません)ので、イベントプロシージャにする必要はありません。シート上にボタンを配置するか、ショートカットを設定すればじゅうぶんです。今回はほかの管理者にも便利なようにシート上にボタンを配置することにしました。
シートモジュールの郵便番号検索イベントプロシージャ
ここでは、Worksheet_Change
イベントプロシージャをつかいます。Worksheet_SelectionChange
ではセルの値に変化がなくても別のセルをクリックしただけでプロシージャが起動してしまいますので適切ではないですね。
ソースコード
Private Sub Worksheet_Change(ByVal Target As Range) Dim myCell As Range Dim myLf As Long Dim my1stValue As String, my2ndValue As String Select Case True Case Not Intersect(Target, Range("I13")) Is Nothing Set myCell = Range("I13") myLf = InStr(myCell.Value, vbLf) Select Case Len(myCell.Value) Case 8 If F_CheckUpZip(myCell.Value) = False _ Then Exit Sub If F_CheckUpSheet = False _ Then Exit Sub Call S_DisplayAddress(myCell.Value) Case 17 If myLf > 0 Then my1stValue = _ Left(myCell.Value, myLf - 1) my2ndValue = _ Mid(myCell.Value, myLf + 1) If F_CheckUpZip(my1stValue) = _ False Then Exit Sub If F_CheckUpZip(my2ndValue) = _ False Then Exit Sub If F_CheckUpSheet = _ False Then Exit Sub Call S_DisplayAddress2( _ my1stValue, my2ndValue) Else Call S_ClearWorksheet End If Case 0 Range("O12, P11").Value = "" myCell.Activate Case Else Call S_ClearWorksheet End Select Set myCell = Nothing End Select End Sub
ソースコードの解説
「Worksheet_Change」プロシージャの使い方
「Intersect」プロパティの使い方
郵便番号を入力するセル「I13」の値が変化したときだけ、住所とフリガナを入力したいので、Intersect
メソッドを使って、「Worksheet_Change」プロシージャの引数Target
にセル「I13」が含まれるかどうか判断します。
「Intersect」プロパティの基本
Intersect(Target, Range("I13"))
Intersect
プロパティは「2つのRange型オブジェクトの引数Target
とRange("I13")
との間の共通範囲があるかどうか」、共通範囲があればそのRange
型オブジェクトを、なければNothing
を返します。
「Nothing」との比較
Intersect(Target, Range("I13")) Is Nothing
これは「2つのRange型オブジェクトの引数Target
とRange("I13")
との間の共通範囲がないこと(Nothing
)」をしめしています。
接頭辞「Not」をつけると全体の否定になる
Not Intersect(Target, Range("I13")) Is Nothing
一方、先頭にNot
をつけると、全体の意味を否定することになります。
ですから、これは「2つのRange型オブジェクトの引数Target
とRange("I13")
との間の共通範囲があること」をしめしています。
これがマクロを起動するトリガーになります。If
文やSelect
文といった条件文に、この条件をかけば、そのときマクロが起動します。
「If」文ではなく「Select」文をつかう
今回の条件は、「Range("I13")
の値が変化すること」1つですから、次のように書いてもまちがいではありません。
Private Sub Worksheet_Change(ByVal Target As Range) If Not Intersect(Target, Range("I13")) Is Nothing 'ここに実行する処理を書く End If End Sub
でも、使わないのには理由があります。
2つ以上の条件になったときにElseIf
を使いたくないからです。ほかにもっといい方法があるからなんです。
Private Sub Worksheet_Change(ByVal Target As Range) Select Case True Case Not Intersect(Target, Range("I13")) Is Nothing 'ここに実行する処理を書く End Select End Sub
ネストはIf
文よりもSelect
文を使ったほうが1つ深くなりますが、ソースは読みやすくなりますよね。とくに条件が3つ以上のときは積極的にSelect
文を使用したほうがいいと思います。
このプログラムも最終的には3つ以上の条件を設ける予定なので、Select
文で書いています。
'ふつうのSelect文 Select Case Not Intersect(Target, Range("I13")) Is Nothing Case True '今回のSelect文 Select Case True case Not Intersect(Target, Range("I13")) Is Nothing
ふつうのSelect
文では、True
かFalse
かの2種類しか判断できません。でも、今回のSelect
文なら、判断したいセルの数だけ、条件をふやすことができ、複雑な判断が可能になっています。
変数の宣言
変数の宣言は、その変数を使用するまえであれば、プログラムの先頭でも、使用する直前でも、どちらでもかまいません。PythonなどのVBA以外の言語では使用する直前に宣言するのがふつうです。そこらへんについては、VBA界隈で有名な「t-hom」さんが次のような記事を書かれています。
できるだけ変数を使用する直前で宣言したほうがいいということですね。
Dim myCell As Range Dim myLf As Long Dim my1stValue As String, my2ndValue As String
でも、Select
文を使って、判断条件を3つ以上にふやす予定なので、プロシージャの先頭で変数を宣言しています。
myCell
は対象となるセルを格納するRange
型変数です。
郵便番号は2段書きになることも予想されるので、それに対応するための変数も用意します。
myLf
は2段書きになる場合の改行文字の位置を格納します。
my1stValue
とmy2ndValue
は2段書きになった場合の、それぞれ、1段目の郵便番号と2段目の郵便番号の値を格納します。
郵便番号を入力するセルを変数「myCell」に代入
Set myCell = Range("I13")
このプログラムの場合、わざわざ郵便番号を入力するセルを変数に代入しなければならないほど複雑ではありませんが、判断条件であるCase
節が3つ以上にふえることを見越して、リテラルであるRange("I13")
は変数に代入して利用します。
気をつけるのは、使い終わったらなるべくはやくNothing
を代入してかたづけることでしょう。
Set myCell = Nothing
これに関しては、わざわざNothing
を格納する必要はないとおっしゃる方もいます。
郵便番号が2段書きになっているかを確認
myLf = InStr(myCell.Value, vbLf)
セル内で改行する場合、Excelの標準機能ではAlt + Enter
で改行をしますが、そのときの改行文字はラインフィードvbLf
です。
2段書きになっているときは必ず改行文字vbLf
がつかわれていますので、その位置をInStr
関数で取得します。
もし、2段書きになっているなら、vbLf
が何番目の文字かを返します。もし、2段書きになってないなら、0
を返します。
セル内の文字列の長さによって条件分岐させる
セル内には郵便番号を入力します。123-4567
のような形で入力します。
ですので、セル内に1つだけ郵便番号を入力するなら、その文字列の長さは8文字です。
郵便番号を2段書きするなら、その文字列の長さは「郵便番号8文字」+「改行文字1文字」+「郵便番号8文字」の計17文字です。
もしセル内の文字列を削除したなら、文字列の長さは0になります。
文字列の長さがそのほかの数値になる場合は、入力ミスがあったと判断します。
Select Case Len(myCell.Value) Case 8 '住所とフリガナを1段書きで表示する Case 17 If myLf > 0 Then '住所とフリガナを2段書きで表示する Else 'エラー対応をする End If Case 0 '住所欄とフリガナ欄を空欄にする Case Else 'エラー対応をする End Select
文字列の長さが8文字の場合
If F_CheckUpZip(myCell.Value) = False Then Exit Sub If F_CheckUpSheet = False Then Exit Sub Call S_DisplayAddress(myCell.Value)
郵便番号のセルの文字列の長さが8文字の場合は、「123-4567」のようなかたちで郵便番号が入力されているはずです。
ここではそれを確認するために、郵便番号かどうかを判定する関数F_CheckUpZip
にセルの文字列を引数としてわたして、正当性を確認しています。
文字列が郵便番号かどうかを判定するファンクションプロシージャ「F_CheckUpZip」
文字列が郵便番号かどうかを判定するファンクションプロシージャは、8文字の文字列を引数にとり、Boolean
型の値を返します。
ソースコード(再掲)
Private Function F_CheckUpZip(ByVal myZip As String) _ As Boolean F_CheckUpZip = True Dim i As Long For i = 1 To 8 Dim myDigit As String: myDigit = Mid(myZip, i, 1) Select Case i Case 4 If myDigit <> "-" Then F_CheckUpZip = False Call S_ClearWorksheet Exit For End If Case Else Dim myNumber As Long: myNumber = Asc(myDigit) If myNumber < 48 Or myNumber > 57 Then F_CheckUpZip = False Call S_ClearWorksheet Exit For End If End Select Next i End Function
まず、プロシージャ名F_CheckUpZip
にTrue
を格納しておきます。
これは感覚的なものかもしれませんが、このファンクションプロシージャでしらべた結果、「ダメ」=文字列が郵便番号でない、とわかったら、True
ではなくFalse
を返したい。
しかし、Boolean型のデフォルト値はFalse
なので、最初にTrue
を入れておくことにしました。
For文を使って1文字目から8文字目までを調べる
郵便番号は「123-4567」という形の8文字の文字列で4文字目にハイフン-
を含みます。
そこで、4文字目はハイフン-
かどうかをしらべ、それ以外のときは半角の数字かどうかをしらべます。
For i = 1 To 8 Dim myDigit As String: myDigit = Mid(myZip, i, 1) 'なんらかの処理 Next i
郵便番号の文字列から、Mid
関数でi
番目の文字列をを取り出して、変数myDigit
に格納します。
Select Case i Case 4 '4文字目がハイフンでないときの処理 Case Else '4文字目以外が数字でないときの処理 End Select
Select
文をつかって4文字目とそれ以外で処理を分岐します。
If myDigit <> "-" Then F_CheckUpZip = False Call S_ClearWorksheet Exit For End If
4文字目がハイフン-
でないとき、プロシージャ名F_CheckUpZip
にFalse
を格納して、S_ClearWorksheet
というサブプロシージャを呼び出して処理をさせ、For
文を抜けます。
S_ClearWorksheet
というサブプロシージャにはセルの内容をクリアする処理をさせます。何回もくりかえし使う処理なので独立したサブプロシージャにしています。
Dim myNumber As Long: myNumber = Asc(myDigit) If myNumber < 48 Or myNumber > 57 Then F_CheckUpZip = False Call S_ClearWorksheet Exit For End If
i
番目の文字を格納した変数myDigit
をAsc
関数の引数としてつかって、その文字コード (Shift_JIS) を取得し、変数myNumber
に格納します。
0
から9
の数字の文字コード (Shift_JIS) は48
から57
です。
i
番目の文字(4文字目以外)の文字コード (Shift_JIS) が48
から57
以外のとき、プロシージャ名F_CheckUpZip
にFalse
を格納して、S_ClearWorksheet
というサブプロシージャを呼び出して処理をさせ、For
文を抜けます。
このように、1文字目から8文字目までをしらべて、条件に該当しないときはプログラムを終了します。
条件にあったときだけ、次の処理を続行します。
実現したいことをいっきにおこなうのではなく、すこしずつ条件をせばめて希望する処理にちかづけるのはよくある処理方法ですね。
郵便番号データが入ったシートが存在するかどうかをを判定するファンクションプロシージャ「F_CheckUpSheet」
郵便番号データが入ったシートが存在するかどうかをを判定するファンクションプロシージャは引数をとらず、Boolean
型の返り値をかえします。
ソースコード(再掲)
Private Function F_CheckUpSheet() As Boolean F_CheckUpSheet = True On Error Resume Next Dim wsZip As Worksheet Set wsZip = Worksheets("zenkoku") If wsZip Is Nothing Then F_CheckUpSheet = False MsgBox """zenkoku""シートがありません。", _ vbExclamation Exit Function End If On Error GoTo 0 Set wsZip = Nothing End Function
On Error Resume Nextステートメント
On Error Resume Next
On Error Resume Next
ステートメントはエラーが発生した行を無視して、次の行の命令文を実行したいときに使います。
ここでは、次の行にエラーが発生したとき、それを無視して、次の行の命令を実行します。
Dim wsZip As Worksheet: Set wsZip = Worksheets("zenkoku")
Worksheet
型の変数wsZip
にzenkoku
シートを格納してます。
ここで、Dim
文は変数の宣言だけなのでエラーは発生しませんが、Set
文はブック内にzenkoku
シートがないとエラーが発生しますね。
エラーが発生すると、wsZip
にはなにも格納されず、中身は初期値のNothing
のままです。
If wsZip Is Nothing Then F_CheckUpSheet = False MsgBox """zenkoku""シートがありません。", vbExclamation Exit Function End If
wsZip
の中身がNothing
のままの場合、プロシージャ名のF_CheckUpSheet
にFalse
を格納して、「"zenkoku"シートがありません。」というメッセージを表示して、プログラムを終了します。
On Error GoTo 0 Set wsZip = Nothing
この2つの文は、「おかたづけ」ですね。
On Error GoTo 0
は、これを書くと、On Error Resume Next
を無効にして、エラーが発生した場合に特別な処理をせず、ふつうにエラーメッセージを表示します。
On Error GoTo 0
はOn Error Resume Next
とセットだと思っておいたほうがいいですね。
エラー発生時の特別扱いをおわったら、すぐに通常処理にもどすことが大切だと思います。
郵便番号を検索して、該当する住所とフリガナを表示するサブプロシージャ「S_DisplayAddress」
このプロシージャがこのプログラムの本題ですね。
でも、実際につかうプログラムでは、ユーザに対する配慮をおこなう「前置き」の部分もかかせません。
このプロシージャでは、入力した郵便番号をzenkoku
ワークシート
上で探して、その住所とフリガナをセルに表示します。
ワークシート関数ではなく、マクロをつかって処理しているので、セルに入力されるのは数式ではなく、文字列です。
ソースコード(再掲)
Private Sub S_DisplayAddress(ByVal myZip As String) On Error GoTo HandleError Dim wsZip As Worksheet Set wsZip = Worksheets("zenkoku") With wsZip Dim myRow As Long myRow = .Cells(.Rows.Count, 1).End(xlUp).Row Dim myCell As Range Set myCell = _ .Cells(WorksheetFunction.Match(myZip, _ .Range(.Cells(2, 1), .Cells(myRow, 1)), 0) _ + 1, 1) myRow = myCell.Row End With With Worksheets("NoticeOfDishonor") .Range("O12").Value = wsZip.Cells(myRow, 2).Value .Range("P11").Value = wsZip.Cells(myRow, 3).Value End With Set wsZip = Nothing Exit Sub HandleError: Call S_ClearWorksheet End Sub
「On Error GoTo 行ラベル」
Private Sub S_DisplayAddress(ByVal myZip As String) On Error GoTo HandleError 'エラーが発生する可能性がある処理 Exit Sub HandleError: Call S_ClearWorksheet End Sub
HandleError
とは「行ラベル」のことで、エラーが発生したときの移動先です。
On Error Resume Next
はエラーが発生すると、その行を無視して、次の行を実行しますが、On Error GoTo HandleError
はエラーが発生すると、その行を無視して、HandleError
に記述している処理を実行します。
On Error GoTo HandleError
を使ううえで気をつける点は、かならずExit Sub
を記述することですね。
これがないと、毎回HandleError
行ラベルの内容を実行してしまいます。それを避けるために、Exit Sub
でプロシージャを抜けます。
On Error GoTo 行ラベル
は、ただしく使うことがむずかしい文です。多用すると、プログラムが読みにくくなるので、使いすぎないことですね。
ここでは、Worksheet
型変数にワークシートを格納したり、Range
型変数にセルを格納したり、エラーが発生する可能性が2つ以上あるため、On Error GoTo 行ラベル
をつかってます。
ワークシート関数「MATCH」をマクロで使って郵便番号を検索する
Dim myRow As Long myRow = .Cells(.Rows.Count, 1).End(xlUp).Row
zenkoku
シートには約15万件のデータがありますが、途中に空白のセルはありません。
Rows.Count
プロパティでシートの最終行の番号を取得します。
.Cells(.Rows.Count, 1)
は、A列の最終行のセルを表します。わたしの環境では、Range("A1048576")
を指します。これは、Excelのバージョンによって変わるので注意が必要です。
End(xlUp)
プロパティは、今いるセルから上方向に移動します。ちょうどCtrl
キーと上方向キーを同時におしたときに移動する場所へと移動します。
.Cells(.Rows.Count, 1).End(xlUp)
と書くと、zenkoku
シートでRange("A1048576")
からRange("A149213")
へと移動します。
Row
プロパティは、そのセルの行番号を取得します。.Cells(.Rows.Count, 1).End(xlUp).Row
全体では、A列の郵便番号がはいったセルの最終行の行番号149213
を返します。
.Cells(.Rows.Count, 1).End(xlUp)
はとてもよく使われるので、このまま覚えるといいと思います。
Dim myCell As Range Set myCell = .Cells(WorksheetFunction.Match(myZip, _ .Range(.Cells(2, 1), .Cells(myRow, 1)), 0) _ + 1, 1)
ここでワークシート関数MATCH
を使って、郵便番号を検索しています。Find
を使わないのは遅いからです。
この記事にも書いてあるとおり、たしかに遅いです。
ためしにzenkoku
シート上でCtrl + F
で検索をおこなってみると、一瞬のタイムラグがあります。
実務上では、このタイムラグがきらわれるので、より高速なを使うことにしました。
WorksheetFunction.Match(検査値,範囲,方法)
検査値は、変数myZip
に格納された郵便番号です。
範囲は.Range(.Cells(2, 1), .Cells(myRow, 1))
であり、Range("A2")
からRange("A149213")
のことです。
方法は次のいずれかを指定するのですが、ここでは0
を指定して、完全に一致する値をさがします。
数値 | 内容 |
---|---|
-1 | 「検査値」以上の最小値 |
0 | 「検査値」に完全一致する値 |
1 | 「検査値」以下の最大値 |
ワークシート関数MATCH
の返り値に関してひとつ大切なことは、あたえられたセル範囲のなかで上から何番目かを返す、ということです。
このプログラムの場合、ほしいのは範囲のなかで何番目かではなく、セルの行番号です。
zenkoku
シートは1行目が見出しになっていて、範囲がRange("A2")
からRange("A149213")
ですから、ワークシート関数MATCH
の返り値に1
をたすと、セルのただしい行番号を得ることができます。
myRow = myCell.Row
最後に変数myRow
を再利用して、得られた答えのセルの行番号を格納します。
住所とフリガナをセルに入力
With Worksheets("NoticeOfDishonor") .Range("O12").Value = wsZip.Cells(myRow, 2).Value .Range("P11").Value = wsZip.Cells(myRow, 3).Value End With
変数myRow
にもとめたい郵便番号のセルの行番号が格納されたので、あとは、住所のセル「O12」とフリガナのセル「P11」に代入するだけです。
代入するのは、=
演算子をつかって代入するのがいいでしょう。
VBAでコピペするより代入した方がスピードがあると思います。
文字列の長さが17文字の場合
Case 17 If myLf > 0 Then '1段目と2段目の郵便番号の住所とフリガナを格納 Else 'エラー処理 End If
変数myLf
の値が0
より大きいということは改行文字が郵便番号のセルにふくまれるということです。改行文字がある場合は、1段目の郵便番号と2段目の郵便番号を順番にさがして、住所のセルとフリガナのセルにそれぞれ2段書きで格納します。
1段目と2段目の郵便番号の住所とフリガナを格納
my1stValue = Left(myCell.Value, myLf - 1) my2ndValue = Mid(myCell.Value, myLf + 1) If F_CheckUpZip(my1stValue) = False Then Exit Sub If F_CheckUpZip(my2ndValue) = False Then Exit Sub If F_CheckUpSheet = False Then Exit Sub Call S_DisplayAddress2(my1stValue, my2ndValue)
my1stValue
は1段目の郵便番号、改行文字よりも前の7文字、my2ndValue
は2段目の郵便番号、改行文字よりも後の7文字です。
どちらの文字列もF_CheckUpZip
で調べて、郵便番号の形(123-4567)になっていなかったら、プログラムを終了します。
そして、F_CheckUpSheet
で調べて、zenkoku
シートがなかったときもプログラムを終了します。
2つの郵便番号を検索して、該当する住所とフリガナを表示するサブプロシージャ「S_DisplayAddress2」
引数が2つにふえただけで、基本的な処理は、郵便番号が1つの場合のS_DisplayAddress
サブプロシージャとかわりません。
ソースコード(再掲)
Private Sub S_DisplayAddress2( _ ByVal my1stValue As String, ByVal my2ndValue As String) On Error GoTo HandleError Dim wsZip As Worksheet Set wsZip = Worksheets("zenkoku") With wsZip Dim myRow As Long myRow = .Cells(.Rows.Count, 1).End(xlUp).Row Dim myRange1 As Range, myRange2 As Range Set myRange1 = _ .Cells(WorksheetFunction.Match(my1stValue, _ .Range(.Cells(2, 1), .Cells(myRow, 1)), 0) _ + 1, 1) Set myRange2 = _ .Cells(WorksheetFunction.Match(my2ndValue, _ .Range(.Cells(2, 1), .Cells(myRow, 1)), 0) _ + 1, 1) Dim myRow1 As Long, myRow2 As Long myRow1 = myRange1.Row myRow2 = myRange2.Row End With Set myRange1 = Nothing: Set myRange2 = Nothing With Worksheets("NoticeOfDishonor") .Range("O12").Value = _ wsZip.Cells(myRow1, 2).Value & vbLf & _ wsZip.Cells(myRow2, 2).Value .Range("P11").Value = _ wsZip.Cells(myRow1, 3).Value & vbLf & _ wsZip.Cells(myRow2, 3).Value End With Set wsZip = Nothing Exit Sub HandleError: Call S_ClearWorksheet End Sub
サブプロシージャ「S_DisplayAddress」と本質的におなじことをしているので、説明は割愛します。
文字列の長さが0文字の場合
文字列の長さが0文字ということは、何も入っていないということです。DELETE
キーでセルの内容を削除した場合などがそれにあたります。
Case 0 Range("O12, P11").Value = "" myCell.Activate
郵便番号のセルだけでなく、住所とフリガナのセルも内容を削除します。
文字列の長さがその他の場合
文字列の長さが8文字(郵便番号1つ)、17文字(郵便番号2つ)、0文字(セルに何も文字列がはいっていない)の場合以外は、メッセージを表示して、プログラムを終了します。ここにメッセージを直接書いてもかまいませんが、プログラムの見通しをよくするために独立したプロシージャを書いています。
Case Else Call S_ClearWorksheet
セルの内容をクリアするサブプロシージャ「S_ClearWorksheet」
エラーメッセージを表示して、郵便番号のセル・住所のセル・フリガナのセルの文字列をクリアするサブプロシージャです。
Private Sub S_ClearWorksheet() MsgBox "郵便番号が正しくありません。" & vbCrLf & _ "正しい形式「123-4567」(ハイフンあり)で" & vbCrLf & _ "入力してください。", vbExclamation Range("I13, O12, P11").Value = "" Range("I13").Activate End Sub
ThisWorkbookモジュールの「Workbook_Open」プロシージャ
このブックをひらいたときにNoticeOfDishonor
シートを選択して、セル「AG3」を選択するマクロです。
こうしておけば、入力担当者がいちいち郵便番号のセルを探す手間がはぶけます。
Private Sub Workbook_Open() Worksheets("NoticeOfDishonor").Activate Range("AG3").Activate End Sub
標準モジュールのデータ更新マクロ
「住所.jp」さんからダウンロードしたCSVファイルはそのままではわたしの希望のかたちにはなっていません。そこでダウンロードしたCSVファイルに対してプログラムを実行して、セルの内容を希望のかたちに変更します。
ソースコード
Option Explicit 'http://jusyo.jp/downloads/new/csv/csv_zenkoku.zip Sub S_CreateZIPToAddressTable_Main() Switch = True Dim myWB As Workbook Select Case F_CheckUpFile Case 0 Exit Sub Case 1 Set myWB = Workbooks("zenkoku.csv") Case 2 Set myWB = _ Workbooks.Open(ThisWorkbook.Path & _ "\zenkoku.csv") End Select If F_CheckUpSheet(myWB) = False Then Exit Sub Call S_CreateZIPToAddressTable_Core(myWB) Set myWB = Nothing Switch = False MsgBox "データ更新終了!", vbInformation End Sub Private Property Let Switch(ByVal Flag As Boolean) With Application .ScreenUpdating = Not Flag .EnableEvents = Not Flag .DisplayAlerts = Not Flag .Calculation = _ IIf(Flag, xlCalculationManual, xlCalculationAutomatic) .PrintCommunication = Not Flag End With End Property Private Function F_CheckUpFile() As Long If Dir(ThisWorkbook.Path & "\zenkoku.csv") = "" Then MsgBox "郵便番号・住所データのCSVファイルが" & _ "同じフォルダに存在しません。", vbExclamation F_CheckUpFile = 0 Exit Function End If Dim myFile As Workbook For Each myFile In Workbooks If myFile.Name = "zenkoku.csv" Then F_CheckUpFile = 1 Exit For Else F_CheckUpFile = 2 End If Next myFile End Function Private Function F_CheckUpSheet(ByVal myWB As Workbook) _ As Boolean With myWB Dim myWS As Worksheet For Each myWS In .Worksheets If myWS.Name = "zenkoku" Then F_CheckUpSheet = True Exit For End If Next myWS End With If F_CheckUpSheet = False Then MsgBox "シート名が""zenkoku""になっていません" & _ vbCrLf & _ "シート名を""zenkoku""にしてください。", _ vbExclamation End If End Function Private Sub S_CreateZIPToAddressTable_Core _ (ByVal myWB As Workbook) With myWB.Worksheets("zenkoku") .Columns(22).Delete .Columns(18).Delete .Range(.Columns(14), .Columns(15)).Delete .Range(.Columns(1), .Columns(4)).Delete Dim myRow As Long myRow = .Cells(.Rows.Count, 1).End(xlUp).Row .Cells(2, 15).Formula = _ "=IF(B2=0,D2&F2&H2&J2, _ IF(B2=1,D2&F2&N2&"" ""&L2,""""))" .Cells(2, 16).Formula = _ "=IF(B2=0,E2&G2&I2&K2, _ IF(B2=1,E2&G2&"" ""&M2,""""))" .Range(.Cells(2, 15), .Cells(2, 16)).Copy _ Destination:=.Range(.Cells(3, 15), _ .Cells(myRow, 16)) With Application .Calculation = xlCalculationAutomatic .Calculation = xlCalculationManual End With With .Range(.Cells(2, 15), .Cells(myRow, 16)) .Copy .PasteSpecial Paste:=xlPasteValues End With .Range(.Columns(2), .Columns(14)).Delete .Range(.Columns(2), .Columns(3)).ColumnWidth = 70 .Cells(1, 2).Value = "住所" .Cells(1, 3).Value = "住所カナ" End With Call S_ReplaceZeroToNothing(myWB) Call S_FreezePanes(myWB) Call S_CopyTable(myWB) End Sub Private Sub S_ReplaceZeroToNothing(ByVal myWB As Workbook) With myWB.Worksheets("zenkoku") Dim myRow As Long myRow = .Cells(.Rows.Count, 1).End(xlUp).Row With .Range(.Cells(2, 3), .Cells(myRow, 3)) .Replace What:="01", _ Replacement:="1", LookAt:=xlPart .Replace What:="02", _ Replacement:="2", LookAt:=xlPart .Replace What:="03", _ Replacement:="3", LookAt:=xlPart .Replace What:="04", _ Replacement:="4", LookAt:=xlPart .Replace What:="05", _ Replacement:="5", LookAt:=xlPart .Replace What:="06", _ Replacement:="6", LookAt:=xlPart .Replace What:="07", _ Replacement:="7", LookAt:=xlPart .Replace What:="08", _ Replacement:="8", LookAt:=xlPart .Replace What:="09", _ Replacement:="9", LookAt:=xlPart End With End With End Sub Private Sub S_FreezePanes(ByVal myWB As Workbook) With myWB .Activate With .Worksheets("zenkoku") .Activate .Cells(2, 2).Activate ActiveWindow.FreezePanes = True End With End With End Sub Private Sub S_CopyTable(ByVal myWB As Workbook) With ThisWorkbook Dim myWS As Worksheet For Each myWS In .Worksheets If myWS.Name = "zenkoku" Then myWS.Delete Exit For End If Next myWS End With With myWB .Worksheets("zenkoku").Copy _ Before:=ThisWorkbook.Worksheets("UpdateTable") .Close End With ThisWorkbook.Save End Sub
ソースコードの解説
データ更新マクロの流れを確認
- プロパティ・プロシージャ
Switch
でマクロの高速化・自動化の設定をして - ファンクション・プロシージャ
F_CheckUpFile
でCSVファイルが存在しているか、開いているかいないかをチェックして - ファンクション・プロシージャ
F_CheckUpSheet
でzenkoku
シートの有無を確認して - サブプロシージャ
S_CreateZIPToAddressTable_Core
でセルの内容を変更します。
プロパティ・プロシージャ「Switch」
Property Let
プロシージャは、プロパティの値を設定するプロシージャです。ここでは、プロシージャの高速化・自動化に関するプロパティをまとめて設定するためにつかってます。
高速化・自動化の「切り替え」をするという意味でSwitch
という名前にしています。
ほかの言語では、VBAのSelect
文と同じ意味でSwitch
文がありますが、それとは関係ありません。
Private Property Let Switch(ByVal Flag As Boolean) With Application .ScreenUpdating = Not Flag .EnableEvents = Not Flag .DisplayAlerts = Not Flag .Calculation = _ IIf(Flag, xlCalculationManual, _ xlCalculationAutomatic) .PrintCommunication = Not Flag End With End Property
画面再描画・イベント発生・警告自動表示・ブック自動計算・プリンタ通信を最適化します。
t-homさんのサイトにくわしい説明があります。
CSVファイルの状態を確認する「F_CheckUpFile」ファンクション・プロシージャ
まず、Dir
関数でCSVファイルが存在するかどうかを確認して、存在しなければ、プロシージャ名の変数F_CheckUpFile
に0
を格納します。
If Dir(ThisWorkbook.Path & "\zenkoku.csv") = "" Then MsgBox "郵便番号・住所データのCSVファイルが" & _ "同じフォルダに存在しません。", vbExclamation F_CheckUpFile = 0 Exit Function End If
ファイルが存在するとわかったら、開いているすべてのファイルを確認して、CSVファイルの名前があったら、プロシージャ名の変数F_CheckUpFile
に1
を格納し、なかったら、2
を格納します。
Dim myFile As Workbook For Each myFile In Workbooks If myFile.Name = "zenkoku.csv" Then F_CheckUpFile = 1 Exit For Else F_CheckUpFile = 2 End If Next myFile
ここは改善の余地があるかもしれません。For Each
文で全部のブックをしらべるのではなく、Set
文でmyFile
にCSVを格納してみて、エラーが発生するかどうかをしらべたり、プロシージャのデータ型をLong
ではなく、String
型にして、返り値をNoExistence
、Opened
、NoOpened
などの自分にとってわかりやすい文字列を返り値に設定するのもいいかもしれません。
CSVファイルのシート名を確認する「F_CheckUpSheet」
シート名をすべてしらべて、zenkoku
になっていれば、処理を続行。
With myWB Dim myWS As Worksheet For Each myWS In .Worksheets If myWS.Name = "zenkoku" Then F_CheckUpSheet = True Exit For End If Next myWS End With
zenkoku
になっていなければ、メッセージをだして、シート名を変更するよう促して、プログラムを終了します。
If F_CheckUpSheet = False Then MsgBox "シート名が""zenkoku""になっていません" & _ vbCrLf & _ "シート名を""zenkoku""にしてください。", _ vbExclamation End If
実際にセル内のデータを操作する「S_CreateZIPToAddressTable_Core」サブプロシージャ
CSVファイルのデータは、いろいろな人のニーズを考えて、分割して掲載されているので、「郵便番号」「住所(漢字)」「住所(カナ)」の3つに集約します。
必要としていない列を削除
分割してあるデータの中で、結合したいデータではない列を削除します。
このとき、いちばん右の列から削除すると、自分で混乱することがありません。左の列から削除すると、その都度、列番号が変わるので、やめたほうがいいですね。
With myWB.Worksheets("zenkoku") .Columns(22).Delete .Columns(18).Delete .Range(.Columns(14), .Columns(15)).Delete .Range(.Columns(1), .Columns(4)).Delete End With
住所(漢字)と住所(カナ)のデータを結合
データの中に事業所フラグ(0
と1
)があって、事業所の住所データと事業所以外の住所データではデータの構造がちがうので、結合のしかたもちがいます。
ですので、IF
で分岐させることにして、漢字のデータとカナのデータのそれぞれのための結合用関数を用意します。
そして、一番上のデータのセルにワークシート関数を埋め込みます。
With myWB.Worksheets("zenkoku") .Cells(2, 15).Formula = _ "=IF(B2=0,D2&F2&H2&J2,IF(B2=1,D2&F2&N2&"" ""&L2,""""))" .Cells(2, 16).Formula = _ "=IF(B2=0,E2&G2&I2&K2,IF(B2=1,E2&G2&"" ""&M2,""""))" End With
ワークシート関数を埋め込むのは、VBAでFor
文を回して処理すると非常に遅くて、いったんワークシート関数を埋め込んで、その数式をコピーしてExcelに計算させたほうが圧倒的に速いからです。
挿入した数式をコピー
Copy
メソッドで数式をコピーするのですが、注意点がひとつ。
Switch
プロパティ・プロシージャのなかで自動計算をオフにしているので、数式をコピーしたあと、いったん自動計算をオンにします。
すると、自動計算がはじまり、それぞれの行の住所のデータを結合してくれます。
自動計算をオンにしたらすぐに計算(結合)してくれるので、次の命令文で自動計算をすぐにオフにしてかまいません。
With myWB.Worksheets("zenkoku") .Range(.Cells(2, 15), .Cells(2, 16)).Copy _ Destination:=.Range( _ .Cells(3, 15), .Cells(myRow, 16)) With Application .Calculation = xlCalculationAutomatic .Calculation = xlCalculationManual End With End With
数式を文字列に変換
これもExcelの機能で処理するのがいいでしょう。
というか、ほかに処理方法はあるのでしょうか。
With myWB.Worksheets("zenkoku") With .Range(.Cells(2, 15), .Cells(myRow, 16)) .Copy .PasteSpecial Paste:=xlPasteValues End With End With
余分なデータを削除
挿入した数式を文字列に変換したことで、数式の元データとなっていた列を削除することが可能になりました。
With myWB.Worksheets("zenkoku") .Range(.Columns(2), .Columns(14)).Delete End With
さらなるデータの整形
住所(漢字)データのセルと住所(カナ)データのセルの横幅をひろげて見やすくします。
S_ReplaceZeroToNothing
サブプロシージャで、「01」〜「09」という丁目の表記を「1」〜「9」に変更します。実際の書類上では「01丁目」という表記は変なので「1丁目」にします。
S_FreezePanes
サブプロシージャで、ウィンドウ枠を固定して、常に見出しが見えるようにしておきます。
With myWB.Worksheets("zenkoku") .Range(.Columns(2), .Columns(3)).ColumnWidth = 70 .Cells(1, 2).Value = "住所" .Cells(1, 3).Value = "住所カナ" End With Call S_ReplaceZeroToNothing(myWB) Call S_FreezePanes(myWB)
CSVファイルのシートをコピー
S_CopyTable
サブルーチンを呼んで、CSVファイルの整形済みのzenkoku
シートをブックにコピーします。
Call S_CopyTable(myWB)
「01」を「1」に置換する「S_ReplaceZeroToNothing」サブプロシージャ
Excelの標準機能の「置換」機能をVBAから利用して「01丁目」を「1丁目」にします。
これも、標準機能をつかわずに、For
文で回したりすると、おそろしく時間がかかります。
With .Range(.Cells(2, 3), .Cells(myRow, 3)) .Replace What:="01", _ Replacement:="1", LookAt:=xlPart .Replace What:="02", _ Replacement:="2", LookAt:=xlPart .Replace What:="03", _ Replacement:="3", LookAt:=xlPart .Replace What:="04", _ Replacement:="4", LookAt:=xlPart .Replace What:="05", _ Replacement:="5", LookAt:=xlPart .Replace What:="06", _ Replacement:="6", LookAt:=xlPart .Replace What:="07", _ Replacement:="7", LookAt:=xlPart .Replace What:="08", _ Replacement:="8", LookAt:=xlPart .Replace What:="09", _ Replacement:="9", LookAt:=xlPart End With
ウィンドウ枠を固定する「S_FreezePanes」サブプロシージャ
FreezePanes
プロパティは、Window
オブジェクトに対しておこなうものですが、ActiveWindow
に対しておこなうのが安全です。
そのため、まず、ブックをアクティブにして、シートをアクティブにして、セルを選択してから、FreezePanes
プロパティにTrue
を設定します。
With myWB .Activate With .Worksheets("zenkoku") .Activate .Cells(2, 2).Activate ActiveWindow.FreezePanes = True End With End With
CSVファイルのシートをコピーする「S_CopyTable」サブプロシージャ
まず、ブックの中にzenkoku
シートがあったら、それは古いデータなので、削除します。
With ThisWorkbook Dim myWS As Worksheet For Each myWS In .Worksheets If myWS.Name = "zenkoku" Then myWS.Delete Exit For End If Next myWS End With
準備がととのったら、zenkoku
シートをCSVファイルからブックにコピーして、CSVファイルをとじます。
最後にブックを保存して終了です。
With myWB .Worksheets("zenkoku").Copy _ Before:=ThisWorkbook.Worksheets("UpdateTable") .Close End With ThisWorkbook.Save
おわりに
最後まで読んでいただき、ありがとうございました。
長いかなあとも思いますが、せっかく書いたので、このままアップします。
ノンプログラマにとってのヒューリスティクスの重要性
ヒューリスティクスってなに
ヒューリスティクスっていうのは、ひとことでいえば「経験則」、あるいは「臨機応変」のことです。「その場しのぎ」という意味でつかわれることもあります。
わたしのだいすきなマンガに「ドカベン」がありますが、ヒューリスティクスによれば、山田太郎がキャッチャーになるのは当然です。
山田太郎のように太っていればキャッチャーをやらせて、中西球道のようにスリムならピッチャーをやらせるのがヒューリスティクスです。
ふとっているほうがしっかりボールを受けとめてくれそうだと思いますよね。それに山田太郎にはピッチャーはむりだろうと思いますよね。
また、「激変する環境変化に柔軟に対応し、生き残る能力」のことを指すこともあります。
山でけものに襲われたら逃げるのがふつうですよね。それが経験則です。わたしたちは生き残るための知恵としてヒューリスティクスを身につけています。
ヒューリスティクスには、判断がただしいかどうか保証はありませんが、すぐに判断できるというメリットがあります。
飛行機は落ちたら死ぬから自動車で移動しよう、というのもヒューリスティクスです。本当はきちんとデータを検証し、過去の事例から死亡率を算出しないと正解はわかりません。
でも、すぐに飛行機の予約をとるかどうか決めるしかないときは、ヒューリスティクスにたよるしかありません。
また、月に10日勤務する予定のアルバイトの方から来月の勤務予定表をもらったときに9日分しか勤務予定がはいっていなかったら、「ああ、1日分予定を書きわすれたんだな」と判断します。これもヒューリスティクスです。
専門的な用語をつかうと
専門的にいえば、かならず正しい答えを導けるかどうかわからないけれど、ある程度のレベルで正解に近い解をもとめることができる方法で、答え(正解とは限らない)にたどりつく時間が短いという特徴があります。
ヒューリスティクスはコンピュータの世界と心理学の世界の両方でつかわれます。
どちらも同じ意味でつかわれていて、どちらもノンプログラマにとっては重要です。
コンピュータの世界では
プログラミングの方法(戦略)を指します。
プログラミングの戦略には、3つあるとおもいます。
1つめは、アルゴリズム(手順の言語化)にもとづいて、正解を導きだす「超最適化戦略」。
これが理想です。というかふつうです。プログラミングをするひとは、まずこれをめざします。
2つめは、近似アルゴリズムにもとづいて、答えをあるていどの誤差の範囲内におさめる「近似解戦略」。
プログラムの種類によっては、正解ではなくても近い答えでじゅうぶんなことがあります。ふつうは超最適化戦略か近似解戦略をとります。
3つめは、ヒューリスティクスにたよった「その場しのぎ戦略」です。
とにかくはやくプログラミングをおえなければならない状況で有効です。ノンプログラマは(専門家でも?)納期が10分とか15分とかいうときはあります。「とりあえずfor
ループでまわしとけ」とか、そういうことです。
とくに重要なのは
「その場しのぎ戦略」の重要性と有効性です。ノンプログラマには「本業」があります。それを助けるためにプログラミングをおこないます。逆はありません。
本業をおこたってプログラミングに没頭するようでは、いくら難解な問題をエレガントな解法で解決したとしても、評価はされません。
上司は30分後に答えがほしいのに、完璧な答えにこだわって3日後に答えをだしてもまったく意味がありません。
本業をがんばって、上司の期待にこたえるための1つの方法として、ノンプログラマはプログラミングをするのです。
一般に、「その場しのぎ戦略」は「近似アルゴリズム戦略」よりも正解に近いことが多いのです。それを知らないと、「近似アルゴリズム戦略」で(その場ではむだとわからないが)むだな努力をしたり、よけいな時間をくったり、しなくてもいい遠回りをしてしまうことになります。
ヒューリスティクスの重要性は
ここですべてを伝えきるのは不可能ですが、むだなことをしないためヒューリスティクスを知っておいたほうがいいのはたしかです。
「経験則」にしたがって「臨機応変」にその場その場をしのぎましょう。