環境依存文字を入力する

ChrWで環境依存文字を入力し、AscWでUnicodeコードポイントを取得する方法を解説。VBAで「?」になる原因と注意点も紹介します。

'Unicodeから文字入力
ActiveCell = ChrW(9451)
'Unicodeの文字コードを取得
Debug.Print AscW(ActiveCell.Text)

VBAで扱えない環境依存文字について

Windowsや使用しているアプリケーションの環境によっては、正しく表示・入力できない「環境依存文字」が存在します。
これらの文字は、フォントや文字コードの違いによって利用できない場合があり、VBAで扱う際にも注意が必要です。

VBAでサポートされていない環境依存文字
取得できない文字

環境依存文字は、PCやアプリケーションの環境によって表示できたりできなかったりする文字です。
たとえば、画像のように「11」を変換した際に候補として出てくる特殊な文字は、別のPCではそもそも存在しない場合があります。これが環境依存文字の典型例です。

さらに厄介なのは、VBAが認識できない環境依存文字が存在するという点です。
画像の文字をアクティブセルに入力し、その値をイミディエイトウィンドウへ出力しようとすると、実際には「?」が返されてしまいます。

これは、VBAの文字コード体系にその文字が存在しないため、正しく表現できないことが原因です。
PC上では表示できていても、VBA側では文字として扱えず、結果的に「?」へ置き換えられてしまいます。
まさに扱いづらい困った存在といえるでしょう。

そのため、ワークシートにこの文字を入力しようとして「?」を指定しても、当然ながら「?」が入力されるだけで、元の環境依存文字を再現することはできません

コード解説

そこで活用するのが、ChrW関数です。

ActiveCell = ChrW(9451)

ChrW関数は、Unicodeのコードポイントを指定して該当する文字を返す関数です。
このコードを実行すると、アクティブセルに画像で示した環境依存文字が入力されます。

VBA自体はその文字を正しく扱えないため、イミディエイトウィンドウへ出力すると「?」に置き換えられてしまいます。
しかし、ワークシートに出力する場合は問題なく文字が表示されます。

また、VBAの内部メモリ上ではUnicodeのコードポイントとして保持されているため、変数に代入して扱うことも可能です。

ただし、Unicodeが定義するコードポイントは非常に多く、すべてを掲載することは現実的ではありません。
そのため、必要な文字コードは都度調べて指定する必要があります

Debug.Print AscW(ActiveCell.Text)

AscW関数を使うと、指定した文字のUnicodeコードポイントを取得できます。
この関数は、引数に渡した文字列のうち、先頭の1文字に対応するUnicodeコードポイントを返します。

AscW関数で環境依存文字のコードを調べ、そのコードをChrW関数へ渡すことで、ワークシートへ正しく入力することが可能になります。

環境依存文字は本来あまり使用が推奨されませんが、「㈱」のように実務で便利なケースもあり、使いたくなる場面は少なくありません。

なお、Mac版ExcelのVBAはUnicodeの扱いがWindows版と異なるため、AscW関数で環境依存文字のコードを取得できない場合があります
特殊文字や外字では常に「?」として扱われることが多く、正しいコードを取得できない点に注意が必要です。

補足:ChrW / AscW が扱えない文字(サロゲートペア)について

環境依存文字とは別に、VBAでは扱えない文字として「サロゲートペア文字」があります。
これは、画像のような絵文字などのようにU+10000 以上のコードポイントを持つ文字のことです。
なお、変換候補と実際の入力結果に差が出ているのは、フォントやIMEの仕様によるもので、環境依存文字とは別の問題です。

VBAのChrW関数は0〜65535(U+FFFF)までのコードポイントしか扱えないため、サロゲートペア文字を正しく生成することはできません。
また、AscW関数で取得しようとしても、サロゲートペアの片側だけが返されるため、正しいコードポイントを取得できません。

そのため、絵文字などのサロゲートペア文字は、VBAでは基本的に扱えない点に注意が必要です。

テーブル機能で抽出したデータだけ選択状態にする

'対象のテーブルと列を指定(1つ目のテーブルの1列目)
With ActiveSheet.ListObjects(1).ListColumns(1)
    .Range.AutoFilter 1, "=002*"  '列数は1行上と同じにする:フィルタ条件(002から始まる文字列)
    .Range.SpecialCells(xlCellTypeVisible).Select  '可視セルのみの選択
    ActiveSheet.ShowAllData  'フィルタの全解除
    
    'フィルタ条件一致有無の判定(集計行がある場合は3にする)
    If Selection.Cells.Count >= 2 Then
        '選択をデータ範囲のみにする
        Intersect(.DataBodyRange, Selection).Select
        '抽出データがある場合の処理
    Else
        '抽出データがない場合の処理
        Debug.Print "抽出データなし"
    End If
End With

このコードを使用すると、テーブル機能でフィルタをかけていない状態でも、フィルタを適用した際に抽出されたデータのみを選択状態にすることができます。

さらに、抽出結果が存在するかどうかの判定も行っています。
このコード単体では「選択する」だけなので、その後に選択範囲を処理するためには、この判定が必要となります。

コード実行時の実際の動き

主な使用場面としては、テーブル内のデータのうち提出や集計に不要なデータを削除したり、必要なデータを転記したりする場合です。
フィルタをかけた状態を維持せずに、対象データだけを選択できるようになります。

この処理では仕様として、可視セル選択時に必ず見出しセルが選ばれるようになっています。(集計行がある場合は集計行セルも選択されます。)
これは、抽出条件に一致するデータが存在しない場合に可視セル選択でエラーが発生するのを避けるためです。
エラー処理を組み込むよりも、必ず選択セルが存在するようにしてエラーを回避する方が分かりやすいと考えています。
ただし、そのままではデータ範囲外のセルまで選択されてしまうため、一致するデータがある場合にはデータ範囲外のセルを選択から除外するようにしています。

コード解説

'対象のテーブルと列を指定(1つ目のテーブルの1列目)
With ActiveSheet.ListObjects(1).ListColumns(1)

・・・

End With

ここでは、アクティブシートにある1つ目のテーブルの1列目のセル範囲を With に指定しています。
これにより、見出しや集計列を含めた列全体のセル範囲を With ブロック内で使用できるようになります。

この処理の性質上、同じシートに複数のテーブルがあることは想定していませんが、対象となる列が必ずしも1列目とは限りません。
その場合は「ListColumns(1)」の数字部分を任意の列番号に変更すれば問題ありません。
また、見出し名を文字列で指定することも可能ですが、その場合は見出しが変更されないように使用者へ注意を促す必要があります。

    .Range.AutoFilter 1, "=002*" '列数は1行上と同じにする:フィルタ条件(002から始まる文字列)
    .Range.SpecialCells(xlCellTypeVisible).Select  '可視セルのみの選択

1行目では、抽出条件を指定しています。
最初の引数「1」は、フィルタを適用する列番号の指定です。処理としては、直前の行にある「ListColumns(1)」の数字と同じものを指定してください。
次の引数「”=002*”」が具体的な条件で、ここでは「002」で始まる文字列を抽出対象としています。
この部分を任意に変更することで、他の条件を設定することも可能です。
なお、条件の指定方法には、他にもさまざまなパターンがあります。

2行目では、可視セルを選択状態にしています。
抽出結果の可視セルが対象となるため、抽出条件に一致するセルとテーブル範囲の列全体が選択状態になります。

ActiveSheet.ShowAllData  'フィルタの全解除

ここでは、かけたフィルタを全て解除しています。
このコードは、フィルタがかかっていない状態で実行するとエラーになる点に注意が必要です。
ただし、処理上は「フィルタがかかっていない」という前提がないため、エラー処理を入れる必要はありません。
詳しくは以下の記事で解説しています。

ちなみに、処理をステップ実行で確認すると分かりますが、この時点では画面上の選択状態と実際の選択セルに違いが生じます。
これは画面更新が行われていないために発生する現象です。

その後のセル選択によって画面更新が行われるため処理上は問題ありませんが、もし処理を変更して画面更新が行われなくなる場合には、画面更新のコードを1行追加することで対策できます。

'選択状態の画面更新(処理上は不要でOK)
'Application.ScreenUpdating = True

具体的には、このコードを直後に挿入するだけで画面更新が行われます。

    'フィルタ条件一致有無の判定(集計行がある場合は3にする)
    If Selection.Cells.Count >= 2 Then
        '選択をデータ範囲のみにする
        Intersect(.DataBodyRange, Selection).Select
        '抽出データがある場合の処理

ここでは、フィルタ条件に一致するデータが存在するかどうかを判定しています。
仕様上、見出しセルは必ず選択されるため、選択セルの個数が2以上であれば条件一致のデータが存在すると判断します。
逆に選択セルが1個のみの場合は、条件に一致するデータがなかったと判断します。
なお、集計行があるテーブルでは選択セルが1つ増えるため、条件式の「2」の部分を「3」に修正してください。

抽出条件に一致するデータが選択されている場合は、実際の選択範囲とデータ範囲を Intersect メソッドで突き合わせ、データ範囲のみを選択状態にしています。
この後は、選択状態のセルに対して必要な処理を作成してください。

    Else
        '抽出データがない場合の処理
        Debug.Print "抽出データなし"
    End If

ここでは、抽出条件に一致するデータが存在しなかった場合の処理を作成します。
特に処理が不要であれば、この部分の追加処理は省略して構いません。
また、例コードにあるイミディエイト出力も削除して問題ありません。

Excelの再計算モードを自動/手動に切り替える

Application.Calculationで再計算モードを制御し、手動・自動を切り替えるVBAコード

'Excelを手動計算にする
Application.Calculation = xlCalculationManual
'Excelを自動計算にする
Application.Calculation = xlCalculationAutomatic

Excelの再計算の動作設定を変更するには、Application.Calculation プロパティを使用します。

このプロパティに以下の値を設定することで、再計算の動作を切り替えることができます。

自動計算 = xlCalculationAutomatic(-4105)
Excelの初期設定。セルの値が変更されると自動的に再計算が行われます。

手動計算 = xlCalculationManual(-4135)
自動的には再計算されず、ユーザーが再計算を実行したときにのみ更新されます。
VBAでは Calculate メソッドを呼び出した際に再計算が行われます。

再計算の切り替え

再計算の切り替えは、Excelのオプション設定からも行えます。
そのため、ブックによっては手動計算に設定されている場合があります。
これは、再計算に時間がかかるケースで、セルの更新ごとに再計算を行わせることによる待ち時間を解消するために利用されています。

VBAでも同様に扱うことができます。処理中に必要なタイミングで再計算を実行できるため、必ずしも処理開始時に手動へ切り替える必要はありません。

ただし、処理中にセルの更新が多数発生し、そのたびに数秒の待ち時間が積み重なり、最終的に数分かかってしまうような場合には、このプロパティを「xlCalculationManual」に設定してから処理を実行し、最後に「xlCalculationAutomatic」に戻すことで待機時間をなくし、大幅な処理速度向上につながります。
なお、自動計算に設定した時点で再計算が実行されるため、別途再計算を行うコードを実行する必要はありません。

再計算の例外仕様

手動計算モードでは通常、セルの更新による再計算は行われませんが、一部例外として「データテーブル(What-If分析のデータテーブル)」は計算モードに関わらず常に再計算される仕様になっています。

ただ使用頻度がかなり低い機能であるため、かなり特殊なケースと考えてもらって問題ないので普段は再計算を手動にしていれば再計算が勝手に行われることはないと認識していてOKです。
再計算を抑止することができないので、もし使用されたファイルを処理化する場合は再計算されることを念頭に置いて処理作成を行ってください。

特定セルのみ再計算を実行する

セル単位の再計算は Range.Calculate。Range(“A1”).Calculate のコード例で基本を解説します

'A1セルのみ再計算する
Range("A1").Calculate
'指定シートのA1セルのみ再計算する
Worksheets("指定シート").Range("A1").Calculate

セルのみを再計算するには Range オブジェクトに対して 「Calculate」 メソッドを使用します。
このメソッドは 指定するオブジェクトによって再計算の範囲を細かく制御できます。

コードのようにセル範囲を指定することで、指定したセルだけを再計算できます。

手動計算にしている状態で、シート全体の再計算では時間がかかり無駄が生じる場合に、セル単位で限定的に再計算箇所を指定できます。

Excelでは基本的に自動で再計算されますが、処理負荷が大きく遅延が発生する場合や、一部のセルだけを更新して他の計算結果に影響を与えたくない場合には、セル単位で再計算を実行する方法が役立ちます。

特に、このサイトの基本理念である「既存データを改変しない」という観点からも、重たい関数に直面する場面は多々あります。
こうした場合には再計算の抑制をうまく活用し、効率的に処理を進めていきましょう。

Worksheets("指定シート").Range("A1").Calculate

例コード2つ目では、特定のワークシートを指定しています。
1つ目の例コードでは Worksheet オブジェクトを省略しているため、その場合はアクティブシートが対象となります。
アクティブではないシートや、処理上アクティブシートが変動する可能性がある場合には、Worksheet オブジェクトを明示的に指定しておくと処理が安定するためおすすめです。

ワークシートを再計算する

ワークシート単体を再計算するには Worksheet.Calculate。コード例で基本的な使い方を解説します。

'アクティブシートを再計算
ActiveSheet.Calculate
'特定のシートを再計算
Worksheets("Sheet1").Calculate
'指定のブックの特定のシートを再計算
Workbooks("Book1.xlsx").Worksheets("Sheet1").Calculate

ワークシートの再計算を行うには 「Calculate」 メソッドを使用します。
このコードを実行すると、アクティブシートに含まれる関数がすべて再計算されます
処理途中で計算方法を手動に切り替えている場合など、コード内で関数計算を更新したいときによく利用されます。

ワークシートオブジェクトを指定することで、特定のシートだけを再計算することも可能です。
2つ目の例では、ワークシート名を指定することで対象シートを限定しています。

さらに、3つ目のコードのように ブックを指定することで別のブックのシートを再計算させることもできます。
この方法を応用すれば、特定のブック内の全シートをまとめて再計算することも可能です。
詳細は以下の記事で解説しています。

すべてのブックやセル単位で再計算を行うことも可能です。
詳しくは以下の記事で解説しています。

特定のブックの全シートを再計算する

特定ブックの全シートのみを再計算する方法です。
全ブック再計算より処理時間を短縮できます。

'ループ用の変数宣言
Dim zz対象Ws As Worksheet

'特定のブックの全シートを再計算
For Each zz対象Ws In Workbooks("Book1.xlsx").Worksheets
zz対象Ws.Calculate
Next zz対象Ws

再計算を行うには対象のオブジェクトに対して「Calculate」メソッドを使用します。
ただし、Workbookオブジェクトにはこのメソッドが存在しません

そのため、複数のブックを開いている状態で特定のブックだけを再計算するには、対象ブック内の全シートをループして「Worksheet.Calculate」を実行する必要があります。

このコードを利用すれば、複数のブックを開いている場合でも特定のブックだけを再計算することが可能となり、処理に時間のかかる不要なブックの再計算を除外できます

コード解説

コードの流れは非常にシンプルです。
対象のブック内の Worksheet オブジェクトをループ処理し、それぞれに対して再計算を実行するだけです。

'ループ用の変数宣言
Dim zz対象Ws As Worksheet

まずはループ処理で使用するための変数を宣言します。
この変数には Worksheet オブジェクトを代入するため、型は Object 型でも構いませんが、Worksheet 型にしておくとコード入力時にインテリセンスが利用できるので効率的です。

'特定のブックの全シートを再計算
For Each zz対象Ws In Workbooks("Book1.xlsx").Worksheets
    ~~~
Next zz対象Ws

次にループ処理の部分です。
For Each ループを使うことで、特定の Workbook に含まれるすべての Worksheet を順番に処理できます。

ここで注意すべき点として、Excel のシートには複数のオブジェクトが存在します。
主に Worksheets と Sheets があり、特に後者の Sheets オブジェクトはマクロ記録でよく利用されます。
ただし、通常のコード記述では Worksheets を明示的に扱う方が分かりやすく、インテリセンスも効くため効率的にコード作成が行えます。

また、Workbooks(“Book1.xlsx”) の引数の文字列を対象とするブックの名前に変更すれば、任意のブックを指定することができます。

For Each zz対象Ws In Workbooks("Book1.xlsx").Sheets
    ~~~
Next zz対象Ws

このコードのように Worksheet オブジェクトではなく、Sheets オブジェクトとしても多くの場合問題なく動作します

ただし Sheets オブジェクトには、グラフシートやマクロシートなど通常の Excel で使用するワークシート以外のものも含まれます
そのため再計算を目的とする場合は、Worksheet オブジェクトを指定する方が適切です。

zz対象Ws.Calculate

ループ内の処理は Worksheet オブジェクトに対して Calculate メソッドを実行しているだけです。

このようにすることで、複数のブックを開いている状態でも特定のブックのみの全シートを再計算させることができます。
使いどころは限定的ですが、知っていれば処理時間を短縮できる場合もあるため有用です。

「全ブックまとめて再計算したい」という方は、以下の記事をご確認ください。


関連の記事

Excel再計算の実行(全ブック対象)

Application.Calculate は開いている全ブックの必要セルのみを再計算します。Application.CalculateFull は全セルを強制再計算します。

'必要なセルのみ再計算(高速)
Application.Calculate
'全セルを強制再計算(重い処理)
Application.CalculateFull

再計算を行うにはApplication.Calculateメソッドを使用します。
このメソッドは、再計算が必要なセルだけを更新するため、高速に処理できます。

Excelに対して再計算の実行を行いますが、対象は開かれている全てのブックのうち再計算が必要なセルです。
全セルを強制的に再計算したい場合はApplication.CalculateFullメソッドを使用します。

このメソッドは全てのセルを対象とするため処理が重く、負荷が大きくなります。
更新漏れがなくなるという安心感はありますが、通常の処理では基本的にApplication.Calculateメソッドの利用が推奨されます。

利用場面としては、処理中に再計算を手動にして停止している状態で、一度再計算を実行しておきたい時などが挙げられます。
処理の最後に1回だけ実行するなど頻繁に利用しない場合は、実際に使ってみて動作の重さが気になるようであれば切り替える形でも良いでしょう。
なお、自動計算を処理中に手動計算へ切り替えている場合は、自動計算に設定を戻せば再計算されるため、このメソッドを使う必要はありません。

なお指定のブックのみを再計算する場合は、Workbookオブジェクトに直接「Calculate」メソッドは存在しません
そのため、対象ブックの全シートをループして「Worksheet.Calculate」を実行する方法が一般的です。
同様に、セル範囲単位では「Range.Calculate」を使えます。
必要に応じてループ処理を組み合わせることで柔軟に再計算を制御できます。
詳しくは以下の記事で解説しています。

PowerQueryでコメントを利用する

PowerQueryの処理ステップにコメントを残す方法について

PowerQueryにコメントを記載することが出来ます
これはExcel数式では出来ない便利な機能です

コメントが挿入されたことを示すアイコン表示状態の画像

この画像のステップ行にコメントが加えられるとアイコンが表示されます

コメントが表示された状態の画像

コメントが作成されたステップにマウスオーバーすると、こちらの画像の様にコメントがポップアップ表示されますので画面上の邪魔になることがありません

VBAと同じくステップ数が増えると、後々確認するとなんのためのステップか分からなくなります、もう絶対なります
そんなときのためにコメントを残して置きましょう

PowerQueryでコメントを残すには2通りの方法があります

プロパティの開き方の画像

1つ目は、ステップ名で右クリックを押すと表示されるメニュー項目のプロパティです

プロパティの設定画面が表示された状態の画像

これを開くとコメント入力ができる画面が表示されます
説明というのがコメントになります
改行も気にせず書くことが可能です

詳細エディターを開くボタンの場所の画像

もう一つは、詳細エディターで編集する方法です
ホームタブにある詳細エディターをクリックすると画面が表示されます

詳細エディター画面でコメントが表示されていることを確認している画像

こちらの画像ではすでにコメントが入力されている箇所があります
プロパティで設定したコメントもここにしっかり反映されます
コメント記号についてはVBAとは違いますので注意してください

1行のコメントの場合は「//」と入力します
その後に続けてコメント文を記載します

複数行コメントの場合の入力方法の画像

複数行にしたい場合は「/*」と「*/」で囲みます
改行をどれだけおこなっても問題ない形になります

実際のところ、この詳細エディターでの編集は1つのステップに対して行う場合は必要ありません
複数のステップをまとめてコメント追記する場合にいちいち画面を設定画面を開きなおさなくて良いのでそういった場面で利用します
この詳細エディターでコメントを記載すると、実際はこのプログラムでPowerQueryが動作していることがよく感じられます

コメントはクドいくらいが丁度いいので、有効に活用してください

セル内文字列を置き換えする

セルに入力された文字列の置き換えについてのコードです(完全・部分一致)

'ABC列で部分一致の検索置換を実行する
Range("A:C").Replace What:="検索文字", Replacement:="置換文字", LookAt:=xlPart
'ABC列で完全一致の検索置換を実行する
Range("A:C").Replace What:="検索文字", Replacement:="置換文字", LookAt:=xlWhole

セル内文字列の置き換えには「Replace」メソッドを使用します
Excelの標準機能と同じように、セルに入力された内容と条件一致するものを検索して、見つかった場合に置き換えを実行する際に使用します

引数の「What」が検索対象の文字列です
置き換えする文字列は「Replacement」に指定します
この2つの指定により、任意の文字列を検索して置き換えを実行することができます

引数「LookAt」の設定で部分一致か完全一致かの選択ができます
部分一致の場合は「xlPart」を設定し、完全一致の場合は「xlWhole」と設定します
検索処理でも同じ事を言えますが引数の設定は前回実行した設定が引き継がれます
なので引数の設定は必要のない部分でも一応設定しておくほうが安心です
部分一致と完全一致が意図せず入れ替わっていたりします

完全一致とすれば、セルの中にある文字列全体が検索と全く同じ時のみ置換の対象になります
部分一致にすれば、セルの中にある文字列の一部だけを置換することが出来ますが、複数当てはまる場合は全て置換対象になる点だけ注意してください
あまりにも短い文字を検索対象にしていると、意図しない変換が行われてしまいます

テキストファイルを一括読み込みする

FileSystemObjectを利用して、テキストファイルを一括で読み込みを行うコードについて

'FSOの宣言
Dim bhFSO As Object, bhFSOT As Object
Set bhFSO = CreateObject("Scripting.FileSystemObject")
'指定テキストファイルを開く
Set bhFSOT = bhFSO.OpenTextFile("指定テキストファイルの絶対パス")

'開いたテキストを1つのデータに一括読み込み
Dim bhTxt As String
bhTxt = bhFSOT.ReadAll

'FSOの解放
Set bhFSOT = Nothing
Set bhFSO = Nothing

テキストファイルの文字列を1つのデータに一括で読み込みをするコードです
通常VBAでテキストファイルを読み込む場合は以下のInputステートメントを利用します

ただ、このコードの場合にはデータが配列として分割されます
たとえばテキストデータ全体で検索や置換をしたい場合にそのようなデータとなった場合配列をループでまわして検証する必要が出てきてしまいます
またあえて1セルにまとめて入力したい場面も想定されます
これは新しいテキスト関数が非常に便利になったので実務上有用です

そんな場合に記事コードを利用します
このコードを利用すると文字列変数に一括でデータを読み込むことで
全体的な処理や関数処理が行いやすくなります