2023年1月28日土曜日

【Python環境構築】jupyterlab variableinspector インストール編

 Pythonを使う上で一番大変なのは環境構築だと思う。


■jupyterlab variableinspector

Jupyter Lab上で変数に値を代入したタイミングでその中身と型が確認できる拡張機能

jupyterlab variableinspectorによる変数の中身の確認


■インストール方法

※Windows10、Anaconda前提で記載しています。

1.node.jsのインストール

Jupyter Lab上で

conda install -c conda-forge nodejs

もしくは

conda install nodejs

を実行。


2.Jupyter Labを一度Shutdown

私の環境だけかもしれませんが、一度Buildしないとそのあとの「jupyterlab variableinspector」が入りませんでした。


3.Jupyter Labをリビルド

コマンドプロンプト上で

jupyter lab build

を実行。


4.jupyterlab variableinspectorのインストール

コマンドプロンプト上で

jupyter labextension install @lckr/jupyterlab_variableinspector@3.0.7

を実行。

多くのサイトでは

jupyter labextension install @lckr/jupyterlab_variableinspector

とあるのですが、今のVer.(3.0.9)はなんかインストールに失敗したので、以下の記事を参考に、古いVer.(3.0.7)をインストールすることにしました。

jupyterlab variable inspectorのインストール
thumbnail
https://zenn.dev/toitoy8/articles/220508_jup-extend01


5.Jupyter Labで確認

Jupyter Labのセル上で右クリックし、「Open Variable Inspector」が表示されていればインストール完了です。

Open Variable Inspector


■jupyterlab variableinspector使用方法

Jupyter Labのセル上で右クリックし、「Open Variable Inspector」をクリックすると、Variable Inspectorというタブが出てきます。

Variable Inspector

Variable Inspectorタブを右にドラッグするとタブが分割されてセルとVariable Inspectorの両方が見えるようになります。

タブを分割

その状態でセルに変数を代入すると、自動でVariable Inspectorに変数の名前・型・値とうが表示されます。

Variable Inspectorで変数を確認

↓よろしければクリックをお願いします!


TOPページに戻る


■関連情報

【Python】スクレイピングでGoogleファイナンスの日本の個別株情報を取得する

【Python】APIで日本の個別株情報を取得する

【Python】株価データに移動平均を付ける

【Python】株価データの移動平均線からゴールデンクロスの場所を探す

【ASN.1】ASN.1の型とPERのデコード

 ※注意! 本内容は独学で得た知識のため、内容に誤りがある場合があります。
      予め誤りがある前提でご覧ください。 

■ASN.1で良く用いられる型(代表的なもの)

デコードを前提に記載すると、代表的な型はだいたい8種類あります。

説明
BOOLEAN1ビットを使用してTRUEかFALSEの論理値を表す
INTEGER

(a..b)の範囲の整数値を表す
サイズが9Bits以上の場合、自動的に8Bitsの整数倍のサイズとなる。
BIT STRINGSIZE(a) or SIZE(a..b)でビット列を表す
OCTET STRINGSIZE(a) or SIZE(a..b)でオクテット列を表す
ENUMERATED各要素の中から1つを選択する
SEQUENCE全要素を順番に処理する
SEQUENCE OFSIZE(a) or SIZE(a..b)の回数分SEQUENCE型で処理する
CHOICE各要素の中から1つを選択し、選択された処理をする

■BOOLEAN型

1Bitを使用し、TRUE(1) or FLASE(0)を判定します。

使われ方としてはSEQUENCE型やCHOICE型の中で記載されることが多く、以下のように表現されます。

Data ::= SEQUENCE {
	trueOrFalse	BOOLEAN
}

仮に'80'Hのデータが転送されてきたとした場合は

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  80|1___|trueOrFalse BOOLEAN Data      |    1|     true|
    |_000|Padding                       |     |         |
    |0000|                              |     |         |

となり、1BitでTRUE or FALSEを判定します。結果

Data
	trueOrFalse = true

という結果が返ります。


■INTEGER型

整数値を返します。

値範囲が定義される場合(INTEGER(1..8))と定義されない場合(INTEGER(5))があり、値範囲が定義されている場合はINTEGER(a..b)というように表現されます。

この時、値が0の場合はaとなるため、値+aが戻り値となります。

また値が範囲で定義されているので値を表現するためにb-a分のBit数が必要となります。
ex.)INTEGER(1..8)の場合、初期値が0ではなく1なので8('1000'B)を表現する4Bitsではなく、8-1=7('111'B)を表現できる3Bitsが必要となります。

仮に'80'Hのデータが転送されてきたとした場合、ASN.1の情報として

Data ::= SEQUENCE {
	value	Value
}

Value ::= INTEGER(1..10)

となっていた場合、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |value    |Value
  80|100_|Value INTEGER Data            |    4|    4+1=5|
    |___0|Padding                       |     |         |
    |0000|                              |     |         |

となり、

Data
	value = 5

という結果が返ります。

また、

Data ::= SEQUENCE {
	value	INTEGER(1..10)
}

という記載や

Data ::= SEQUENCE {
	value	INTEGER(1..maxValue)
}

maxValue INTEGER ::= 5

という記載がされる場合がありますが、SEQUENCE型の外にINTEGER型を置くかどうか、値の範囲を変数で持つか、の違いであり、結果は全て

Data
	value = 5

となります。


ちなみに

Data ::= SEQUENCE {
	value	INTEGER(5)
}

のように固定値の場合は1Bitも使用せずに

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |value    |value
    |    |Value INTEGER Data            |     |        5|
  80|1000|Padding                       |     |         |
    |0000|                              |     |         |

となり

Data
	value = 5

という結果が返ります。


■BIT STRING型

ビット列を返します。

SIZEで表現するBit数を定義しますが、SIZE(a..b)のように可変長の場合はb-a分のDataLengthが必要となります。

仮に'80'Hのデータが転送されてきたとした場合、ASN.1の情報として

Data ::= SEQUENCE {
	bits	BIT STRING(SIZE(3))
}

となっていた場合、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |bits     |bits
  80|100_|bits BIT STRING Data          |  100|   '100'B|
    |___0|Padding                       |     |         |
    |0000|                              |     |         |

となり

Data
	bits = '100'B

という結果が返ります。


また

Data ::= SEQUENCE {
	bits	BIT STRING(SIZE(1..5))
}

のように可変長で設定された場合は

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |bits     |bits
  80|100_|bits BIT STRING DataLength    |    4|    4+1=5|
    |___0|bits BIT STRING Data          |00000| '00000'B|
    |0000|                              |     |         |

となり

Data
	bits = '00000'B

という結果が返ります。


■OCTET STRING型

オクテット列を返します。

BIT STRING同様、SIZEで表現するOct数を定義しますが、SIZE(a..b)のように可変長の場合はb-a分のDataLengthが必要となります。

仮に'10 00 00 00'Hのデータが転送されてきたとした場合、ASN.1の情報として

Data ::= SEQUENCE {
	octets	OCTET STRING(SIZE(3))
}

となっていた場合、

HEX |Bits|Contents                      |Value |Parameter|Link
----+-----------------------------------|------+---------+------
    |    |Data SEQUENCE Data            |      |octets   |octets
  10|0001|octets OCTET STRING Data      |010000|'010000'H|
    |0000|                              |      |         |
  00|0000|                              |      |         |
    |0000|                              |      |         |
  00|0000|                              |      |         |
    |0000|                              |      |         |
  00|0000|Padding                       |      |         |
    |0000|                              |      |         |

となり

Data
	octets = '010000'H

という結果が返ります。

また

Data ::= SEQUENCE {
	octets	OCTET STRING(SIZE(1..10))
}

のように可変長で設定された場合は

HEX |Bits|Contents                      |Value |Parameter|Link
----+-----------------------------------|------+---------+------
    |    |Data SEQUENCE Data            |      |octets   |octets
  10|0001|octets OCTET STRING DataLength|  0001|    1+1=2|
    |0000|octets OCTET STRING Data      |  0000|  '0000'H|
  00|0000|                              |      |         |
    |0000|                              |      |         |
  00|0000|                              |      |         |
    |0000|Padding                       |      |         |
  00|0000|                              |      |         |
    |0000|                              |      |         |

となり

Data
	octets = '0000'H

という結果が返ります。


■ENUMERATED型

用意された要素の1つを返します。

0が初期値となりますので、要素数-1のBit数が必要となります。

例えば、月を判断するために以下のようなASN.1の情報があり

Data ::= SEQUENCE {
	months	Months
}

Months ::= ENUMERATED {
	january,	--'Value = 0'
	february,	--'Value = 1'
	march,		--'Value = 2'
	april,		--'Value = 3'
	may,		--'Value = 4'
	june,		--'Value = 5'
	july,		--'Value = 6'
	august,		--'Value = 7'
	september,	--'Value = 8'
	october,	--'Value = 9'
	november,	--'Value = 10'
	december	--'Value = 11'
}

そこに'80'Hのデータが転送されて来たとき、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |months   |Months
  80|1000|Months ENUMERATED Data        |    4|may      |
    |0000|Padding                       |     |         |

となり

Data
    months
        may

という結果が返ります。


■SEQUENCE型

順番に処理をしていきます。

Extensionbit、OptionalBitmap、DefaultBitmapが無い場合は1Bitも使用しません。

例えば以下のようなASN.1の情報があったとして、

Data ::= SEQUENCE {
	sex	Sex,
	age	Age
}

Sex ::= ENUMERATED {
	male,
	female
}

Age ::= INTEGER(0..128)

そこに'80 00'Hのデータが転送されて来たとき、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |sex      |Sex
  80|1___|Sex ENUMERATED Data           |    1|female   |
    |    |Data SEQUENCE Data            |     |age      |Age
    |_000|Age INTEGER Data              |   20|       20|
    |1000|                              |     |         |
  00|0___|                              |     |         |
    |_000|Padding                       |     |         |

となるので

Data
    sex
        female
    age = 20

という結果が返ります。

このように、SEQUENCE型自体は1Bitも使用せずに他の型でBitsを使い結果を返すことになります。


■SEQUENCE OF型

同じ処理(SEQUENCE)を繰り返します。

SIZEで処理回数を定義しますが、SIZE(4)のように固定長である場合はDataLengthは不要ですがSIZE(a..b)のように可変長である場合はb-aのDataLengthが必要になります。

例えば複数人の男女を表現する場合、'80'Hのデータが転送されてきたとき、麻雀のように4人固定である場合は

Majang ::= SEQUENCE(SIZE(4)) OF Sex

Sex ::= ENUMERATED {
	male,
	female
}

とすることで

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE OF DataLength   |    4|         |
    |    |Data SEQUENCE OF Data         |     |Sex      |Sex
  80|1___|Sex ENUMERATED Data           |    1|female   |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |_0__|Sex ENUMERATED Data           |    0|male     |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |__0_|Sex ENUMERATED Data           |    0|male     |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |___0|Sex ENUMERATED Data           |    0|male     |
    |0000|Padding                       |     |         |

となり

Majang
    Sex
        female
    Sex
        male
    Sex
        male
    Sex
        male

という結果になります。

しかし家族のように可変の複数人である場合は

Family ::= SEQUENCE(SIZE(1..5)) OF Sex

Sex ::= ENUMERATED {
	male,
	female
}

となるので

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  80|100_|Data SEQUENCE OF DataLength   |    4|4+1=5    |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |___0|Sex ENUMERATED Data           |    0|male     |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |0___|Sex ENUMERATED Data           |    0|male     |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |_0__|Sex ENUMERATED Data           |    0|male     |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |__0_|Sex ENUMERATED Data           |    0|male     |
    |    |Data SEQUENCE Data            |     |Sex      |Sex
    |___0|Sex ENUMERATED Data           |    0|male     |

となり、

Family
    Sex
        male
    Sex
        male
    Sex
        male
    Sex
        male
    Sex
        male

という結果が返ります。


■CHOICE型

複数の要素から一つを選択し処理します。

ENUMERATED型と同様に0が初期値となりますので、要素数-1のBit数が必要となります。

例えば以下のようなASN.1の情報があったとして、

Legs ::= CHOICE {
	insect	INTEEGR(6),	--'Value = 0'
	octopus	INTEGER(8),	--'Value = 1'
	squid	INTEGER(10)	--'Value = 2'
}

そこに'80'Hのデータが転送されて来たとき、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  80|10__|Legs CHOICE Data              |    2|        2|squid
    |    |squid INTEGER Data            |   10|       10|
    |__00|Padding                       |     |         |
    |0000|                              |     |         |

となり

Legs
	squid = 10

という結果が返ります。


他にもIA5STRINGやSET等、ASN.1の型はいろいろありますので確認してみてください。


↓よろしければクリックをお願いします!

TOPページに戻る


■関連ページ

【ASN.1】ASN.1(Abstract Syntax Notation One)とは

【ASN.1】PER(Packed Encoding Rules)コーディングとは


2023年1月25日水曜日

【マクロVBA】VBA_TIPS-03 マクロの可読性

社内ツールの問題点 

よく社内DXや社内ツール、社内マクロ等で問題となる以下の事象があります。

・前任のSEが作ったツールのドキュメントが無い
・前任が作ったツールを改修したいが解読が難しい


社内ツール作成後、きちんと仕様書を残してくれれば後任は助かるのですが、たいていの場合作って「はい!終わり!」がほとんどだと思います。

また社内SEが社員ならまだ人事異動しても社内に残っているので聞ける可能性もありますが、社内SEが派遣社員だった場合、契約満了後は聞くことすらできなくなります。
実際、過去に私のいた部署で大規模なシステムを構築した際に、ドキュメントが無いままSEがいなくなり、そのシステムを改修をするのに3名の派遣SEを雇いましたが結局解読できずじまいだった。
そんなことも往々にしてあるようです。

作った本人は「なんでSEなのに読み解けないの?」だと思いますし、解読する側は「なんでこんなわかりづらい書き方するの?」だと思います。

それぞれのSEの技術の差があれど、仕様書等のドキュメントを残さないのであれば可読性の高い書き方(誰でも読めばわかる書き方)をすべきだと思います。


なぜ可読性が低いのか?

ネットに載っているコードのコピペ

プログラムを書く際に、ホームページ等のコードを参考にすることも多いかと思います。

ただプログラムを書く人はたいてい書き方にクセが出てくるので、同じようなコードのようでも変数の使い方や関数の使い方が微妙に違ったりします。
ネットのコピペを行うと、いろいろな人のクセが入り混じったコードになるので解読が難しくなります。

例えば以下のようなコードがあったとして、メッセージボックスに表示されるのはどれだかパッと出ますか?

Sub TEST()

Range("F5").Value = "鈴木"
Range("V5").Value = "佐藤"
Range("M5").Value = "田中"

MsgBox Cells(5, 13).Value

End Sub








正解は

正解は田中


きちんと見ればわかるのですが、RangeとCellsが混在しているだけでも少し混乱しませんか?
このようにネットのコピペばかり使うと統一感が無くなってしまうので、可読性は低くなります。


ドキュメント作らないくせに効率重視

プログラムに慣れてくるとどうしても効率を優先してしまう場合があります。

例えばこんなデータがあったとして

会員情報

以下のように1行ずつデータを処理しある要素をメッセージボックスに表示させるとします。

Sub TEST2()

Dim Arry() As String
Dim y As Long
Dim x As Integer

ReDim Arry(8)
For y = 2 To 2
    For x = 1 To 9
        Arry(x) = Cells(y, x).Value
    Next x
    MsgBox Arry(5)
Next i

End Sub
このとき表示されるのは”2023”ですが、この2023は対象期間(前)?それとも対象期間(後)?どちらでしょう。

こんな感じでドキュメントを残してくれればすぐわかるのですが、

配列名インデックス内容
Arry()0会員番号1
1会員番号2
2会員番号3
3会員番号4
4会員氏名
5対象期間(前)_年
6対象期間(前)_月
7対象期間(後)_年
8対象期間(後)_月

ドキュメントが無い場合はやはり解読に時間がかかります。

このようにドキュメント作らないくせに効率を求めたコードを書くと、後任がすごく困ります。


可読性を高くするには

変数の仕様書だけでもあると、SEは解読にさほどの時間がかかりませんが、それが無い(もしくは作らない)のであれば、これからのSEはたとえクソコードと言われようとも初心者でもわかるコードの書き方をすべきだと思います。

変数の統一

できるだけ変数はルール化しておくと理解が早くなります。

たとえば

Integer型ならintHogehoge
String型ならstrHogehoge

のように変数名の前に型の略称を入れておくと変数を見ただけで「これは文字列型」と理解が早くなります。

また、周回用のカウンタではiやcがよく用いられますが、Excelマクロで縦横周回させるようであれば縦y横xと誰もが学生時代に習ったxyを使用するとより理解度が増します。


たとえ行数が増えても、簡単な書き方を心がける

昔のPCはCPUやメモリが貧弱だったので、より効率よくプログラムを書く必要がありましたが、今の時代オフィス業務をやるにしては高性能なCPU、オフィス業務では絶対使いきれないメモリ容量と正直PCの性能が上がりすぎてあまり効率的に書く必要性が無くなってきたと思います。

例えば↑で書いた

Sub TEST2()

Dim Arry() As String
Dim y As Long
Dim x As Integer

ReDim Arry(8)
For y = 2 To 2
    For x = 1 To 9
        Arry(x) = Cells(y, x).Value
    Next x
    MsgBox Arry(5)
Next i

End Sub

ですが、

配列名インデックス内容
Arry()0会員番号1
1会員番号2
2会員番号3
3会員番号4
4会員氏名
5対象期間(前)_年
6対象期間(前)_月
7対象期間(後)_年
8対象期間(後)_月

があれば解読は簡単ですが無い場合はやはり解読に時間がかかります。

しかし以下のように記載すると

Sub TEST3()

Dim y As Long
Dim str会員番号1 As String
Dim str会員番号2 As String
Dim str会員番号3 As String
Dim str会員番号4 As String
Dim str会員氏名 As String
Dim str対象期間_前_年 As String
Dim str対象期間_前_月 As String
Dim str対象期間_後_年 As String
Dim str対象期間_後_月 As String

For y = 2 To 2
    str会員番号1 = Cells(y, 1).Value
    str会員番号2 = Cells(y, 2).Value
    str会員番号3 = Cells(y, 3).Value
    str会員番号4 = Cells(y, 4).Value
    str会員氏名 = Cells(y, 5).Value
    str対象期間_前_年 = Cells(y, 6).Value
    str対象期間_前_月 = Cells(y, 7).Value
    str対象期間_後_年 = Cells(y, 8).Value
    str対象期間_後_月 = Cells(y, 9).Value
    MsgBox str対象期間_前_年
Next i

End Sub

変数の内容も、メッセージボックスに表示される内容も一目瞭然です。


仕様書がきちんとあれば正直効率よくコードを書いてドキュメントを残す。これが一番きれいだと思いますが、社内SEの大きな利点は口頭で

社員「こういうの作ってほしい」
SE「できましたー」

だと思うので、仕様書を必ず作る必要はないと思いますが、ならばぜひコードの書き方を工夫するのも良いと思います。


↓よろしければクリックをお願いします!

TOPページに戻る


■関連ページ

【マクロVBA】VBA_TIPS-02 Select文とIf文


2023年1月21日土曜日

【社内DX案件紹介】データの期間によってレコードを分割しファイル出力する

 ■社内DXの依頼内容

・2つのインプットファイルがあり、その中の複数シートのデータを、1つのアウトプットファイル、1シートに集約しXLSファイルとして出力したい

・処理漏れや誤処理厳禁のため、処理後に処理漏れ確認および誤処理検知できるような仕様としたい

A社B社2つのファイルを集約し、出力する


■社内DXの条件

1.2つのインプットファイル(A社ファイル,B社ファイル)は、2シート目から順に転記

2.インプットファイル、“対象期間(前)”と“対象期間(後)”の月数が、12か月以内のものは、そのまま転記し、13か月以上のものは"1月~12月”の表示形式で最大12か月の期間で行を分ける。(1行の内、期間は年を跨がない)

(例)「対象期間(前):2021年5月」「対象期間(後):2023年3月」の場合

対象期間(前)対象期間(後)
1行目20215202112
2行目20221202212
3行目2023120233

3.ファイルパス、ファイル数、ファイル名は固定(拡張子は.xlsx or .xls)。

4.シート数、シート名は変動有り(データ記載は2シート目~は固定)


■諸元

・インプットファイルを開く

・2シート目からの情報を取得する

・対象期間(前)と対象期間(後)の期間が12か月以内かどうか判断し、転記する
 なお13か月以上の場合は年単位で分割し転記する

・転記は検証用シート「処理結果」とアウトプット用シート「output」にそれぞれ転記することとし、「処理結果」で処理漏れがないか確認できるようにする

・アウトプット用シート「output」を新規ブックにコピーし.xls形式にて保存する


■作成方法

1.2シート目からデータのある2つのファイルを準備する。

  なおデータの配置は以下とします。
  ・A~D列:番号
  ・E列   :氏名
  ・F列   :対象期間(前)の年
  ・G列   :対象期間(前)の月
  ・H列   :対象期間(後)の年
  ・I列    :対象期間(後)の月

A社ファイル

B社ファイル

2.実際にマクロを書いていく

Sub Main()

Dim strFP   As String       '/--フォルダパス--/'
Dim strFN   As String       '/--ファイル名--/'
Dim mWB     As Workbook     '/--マクロのワークブック--/'
Dim iWB     As Workbook     '/--インプットファイルのワークブック--/'
Dim iWS     As Worksheet    '/--インプットファイルのワークシート--/'


Dim cWS As Integer          '/--シート数格納--/'
Dim s As Integer            '/--シート数カウンタ--/'
Dim n As Integer            '/--年周回カウンタ--/'
Dim y As Long               '/--インプットファイル 行カウンタ--/'
Dim yI As Long              '/--インプットファイル 行数格納--/'
Dim yO As Long              '/--アウトプットファイル 行数格納--/'
Dim yR As Long              '/--処理結果 行数格納--/'

Dim strNo1 As String        '/--番号1Block目--/'
Dim strNo2 As String        '/--番号2Block目--/'
Dim strNo3 As String        '/--番号3Block目--/'
Dim strNo4 As String        '/--番号4Block目--/'
Dim strName As String       '/--氏名--/'
Dim lngFromY As String      '/--対象期間(前)(年)--/'
Dim intFromM As String      '/--対象期間(前)(月)--/'
Dim dateFrom As Date        '/--対象期間(前)(年月)--/'
Dim lngToY As String        '/--対象期間(後)(年)--/'
Dim intToM As String        '/--対象期間(後)(月)--/'
Dim dateTo As Date          '/--対象期間(後)(年月)--/'
Dim lngDiffY As Long        '/--対象期間(年)--/'
Dim lngDiffM As Long        '/--対象期間(月)--/'

Set mWB = ActiveWorkbook
strFP = mWB.Sheets("メイン").Cells(2, 2).Value

'/--データが残ってたりしないよう、先にデータを消去--/'
mWB.Sheets("処理結果").Select
mWB.Sheets("処理結果").Range(Rows(3), Rows(Rows.Count)).Clear
mWB.Sheets("output").Select
mWB.Sheets("output").Range(Rows(2), Rows(Rows.Count)).Clear

yR = 3
yO = 2

strFN = Dir(strFP & "\*.xls*")

Do Until strFN = ""
    Select Case strFN
        '/--見つけたファイルがマクロファイルの場合は何もしない--/'
        Case mWB.Name
        
        '/--見つけたファイルがマクロファイル以外の場合--/'
        Case Else
            Set iWB = Workbooks.Open(strFP & "\ " & strFN)
            cWS = iWB.Worksheets.Count
            For s = 2 To cWS
                Set iWS = iWB.Sheets(s)
                strNo1 = ""
                strNo2 = ""
                strNo3 = ""
                strNo4 = ""
                strName = ""
                lngFromY = 0
                intFromM = 0
                lngToY = 0
                intToM = 0
                
                yI = iWS.Cells(Rows.Count, 1).End(xlUp).Row
                For y = 2 To yI
                    '/--インプット情報を取得--/'
                    strNo1 = iWS.Cells(y, 1)
                    strNo2 = iWS.Cells(y, 2)
                    strNo3 = iWS.Cells(y, 3)
                    strNo4 = iWS.Cells(y, 4)
                    strName = iWS.Cells(y, 5)
                    lngFromY = CLng(iWS.Cells(y, 6))
                    intFromM = CInt(iWS.Cells(y, 7))
                    lngToY = CLng(iWS.Cells(y, 8))
                    intToM = CInt(iWS.Cells(y, 9))
                    
                    '/--データの期間を計算--/'
                    dateFrom = DateSerial(lngFromY, intFromM, 1)
                    dateTo = DateSerial(lngToY, intToM, 1)
                    lngDiffM = DateDiff("m", dateFrom, dateTo)
                    
                    '/--検証用シートに共通項目だけ転記--/'
                    With mWB.Sheets("処理結果")
                        .Cells(yR, 1).Value = iWB.Name
                        .Cells(yR, 2).Value = iWS.Name
                        .Cells(yR, 3).Value = y
                        .Cells(yR, 4).Value = strNo1
                        .Cells(yR, 5).Value = strNo2
                        .Cells(yR, 6).Value = strNo3
                        .Cells(yR, 7).Value = strNo4
                        .Cells(yR, 8).Value = strName
                        .Cells(yR, 9).Value = lngFromY
                        .Cells(yR, 10).Value = intFromM
                        .Cells(yR, 11).Value = lngToY
                        .Cells(yR, 12).Value = intToM
                    End With
                    
                    '/--データ期間ごとの処理--/'
                    Select Case lngDiffM
                        Case Is < 12   '/--同月含む12か月以内の場合--/'
                            '/--検証用シートに1レコード分だけ転記--/'
                            With mWB.Sheets("処理結果")
                                .Cells(yR, 13).Value = lngFromY
                                .Cells(yR, 14).Value = intFromM
                                .Cells(yR, 15).Value = lngToY
                                .Cells(yR, 16).Value = intToM
                            End With
                            '/--アウトプット用シートに1レコード分だけ転記--/'
                            With mWB.Sheets("Output")
                                .Cells(yO, 1).Value = strNo1
                                .Cells(yO, 2).Value = strNo2
                                .Cells(yO, 3).Value = strNo3
                                .Cells(yO, 4).Value = strNo4
                                .Cells(yO, 5).Value = strName
                                .Cells(yO, 6).Value = lngFromY
                                .Cells(yO, 7).Value = intFromM
                                .Cells(yO, 8).Value = lngToY
                                .Cells(yO, 9).Value = intToM
                            End With
                            yR = yR + 1
                            yO = yO + 1
                            
                        Case Else      '/--同月含む13か月以上の場合--/'
                            
                            '/--何年跨いでいるかの期間を取得--/'
                            lngDiffY = DateDiff("yyyy", dateFrom, dateTo)
                            
                            '/--年跨ぎ分の周回処理--/'
                            For n = 0 To lngDiffY
                                '/--検証用シートに対象期間(年)+n年の情報を転記--/'
                                With mWB.Sheets("処理結果")
                                    .Cells(yR, n * 4 + 13).Value = lngFromY + n
                                    If n = 0 Then
                                        .Cells(yR, n * 4 + 14).Value = intFromM
                                    Else
                                        .Cells(yR, n * 4 + 14).Value = 1
                                    End If
                                    .Cells(yR, n * 4 + 15).Value = lngFromY + n
                                    If n = lngDiffY Then
                                        .Cells(yR, n * 4 + 16).Value = intToM
                                    Else
                                        .Cells(yR, n * 4 + 16).Value = 12
                                    End If
                                End With
                                
                                '/--アウトプット用シートに対象期間(年)+n年の情報を転記--/'
                                With mWB.Sheets("Output")
                                    .Cells(yO, 1).Value = strNo1
                                    .Cells(yO, 2).Value = strNo2
                                    .Cells(yO, 3).Value = strNo3
                                    .Cells(yO, 4).Value = strNo4
                                    .Cells(yO, 5).Value = strName
                                    .Cells(yO, 6).Value = lngFromY + n
                                    If n = 0 Then
                                        .Cells(yO, 7).Value = intFromM
                                    Else
                                        .Cells(yO, 7).Value = 1
                                    End If
                                    .Cells(yO, 8).Value = lngFromY + n
                                    If n = lngDiffY Then
                                        .Cells(yO, 9).Value = intToM
                                    Else
                                        .Cells(yO, 9).Value = 12
                                    End If
                                    yO = yO + 1
                                End With
                            Next n
                            yR = yR + 1
                    End Select
                    
                Next y
                Set iWS = Nothing
            Next s
    End Select
    iWB.Close
    Set iWB = Nothing
    strFN = Dir()
Loop

'/--アウトプット用シートを新規ブックにコピーして保存--/'
Application.DisplayAlerts = False
Sheets("Output").Select
Sheets("Output").Copy
ActiveWorkbook.SaveAs Filename:=strFP & "\output.xls", FileFormat:=xlExcel8
ActiveWindow.Close
Application.DisplayAlerts = True

Set mWB = Nothing

End Sub


3.マクロ処理結果

処理結果のシートには検証のデータが、

検証用のシート


Outputのシートには実際にファイル出力されるデータが転記され

出力されるシート


インプットファイルと同じフォルダにoutput.xlsというファイルが出力されます。

出力されたファイル


■マクロ解説

1.データの期間を計算

'/--データの期間を計算--/'
dateFrom = DateSerial(lngFromY, intFromM, 1)
dateTo = DateSerial(lngToY, intToM, 1)
lngDiffM = DateDiff("m", dateFrom, dateTo)

・dateFrom = DateSerial(lngFromY, intFromM, 1)

年(lngFromY)、月(intFromM)、日(1)で日付のシリアル値(1900/1/1からの経過日数)を日付型のdateFromへ格納しています。
lngFromY:対象期間(前)(年)
intFromM:対象期間(前)(月)
同じようにdateTo = DateSerial(lngToY, intToM, 1)で対象期間(後)の年月日も日付型で格納しています。

・lngDiffM = DateDiff("m", dateFrom, dateTo)

dateFromからdateToまでの期間を単位で計算し、数値型lngDiffMへ格納しています。


2.13か月以上の処理

lngDiffMが12以上(同月はlngDiffM=0となるので、13か月目は12となります。)は13か月以上として処理します。

'/--何年跨いでいるかの期間を取得--/'
lngDiffY = DateDiff("yyyy", dateFrom, dateTo)

'/--年跨ぎ分の周回処理--/'
For n = 0 To lngDiffY
    '/--アウトプット用シートにfrom対象期間+n年の情報を転記--/'
    With mWB.Sheets("Output")
        .Cells(yO, 1).Value = strNo1
        .Cells(yO, 2).Value = strNo2
        .Cells(yO, 3).Value = strNo3
        .Cells(yO, 4).Value = strNo4
        .Cells(yO, 5).Value = strName
        .Cells(yO, 6).Value = lngFromY + n
        If n = 0 Then
            .Cells(yO, 7).Value = intFromM
        Else
            .Cells(yO, 7).Value = 1
        End If
        .Cells(yO, 8).Value = lngFromY + n
        If n = lngDiffY Then
            .Cells(yO, 9).Value = intToM
        Else
            .Cells(yO, 9).Value = 12
        End If
        yO = yO + 1
    End With
Next n

・lngDiffY = DateDiff("yyyy", dateFrom, dateTo)

dateFromからdateToまでの期間を単位で計算し、数値型lngDiffMへ格納しています。
これにより何年跨いだかが判明します。

・For n = 0 To lngDiffY

跨いだ年数分周回させます。

・.Cells(yO, 6).Value = lngFromY + n

1年目はn=0なので「lngFromY:対象期間(前)(年)」の値が入り、
2年目はn=1となり「lngFromY」の1年後の年が入ります。
このようにnが+1されるごとに1年後の値が入るようになります。

・If n = 0 Then
.Cells(yO, 7).Value = intFromM
 Else
.Cells(yO, 7).Value = 1
 End If

1年目(n=0)は「intFromM:対象期間(前)(月)」の値が入りますが、2年目以降は対象期間(前)(月)は1月であるべきなので、n>0は1が入るようにしています。

・If n = lngDiffY Then
.Cells(yO, 9).Value = intToM
 Else
.Cells(yO, 9).Value = 12
 End If

最終年目(n=lngDiffY)は「intFromM:対象期間(後)(月)」の値が入りますが、最終年-1年目までは対象期間(後)(月)は12月であるべきなので、n<lngDiffYは12が入るようにしています。

・yO = yO + 1

意外と忘れがちですが、13か月以上の場合は年毎に分割(インプット1に対してアウトプット複数)なので、この周回の中で「yO:Outputシートの行」を+1していきます。


このようにDateSerial、DateDiffを組み合わせ期間を取得、その期間の中で年と月をどのよう表現すればよいか考えると条件にある

2.インプットファイル、“対象期間(前)”と“対象期間(後)”の月数が、12か月以内のものは、そのまま転記し、13か月以上のものは"1月~12月”の表示形式で最大12か月の期間で行を分ける。(1行の内、期間は年を跨がない)

を達成できるようになります。


↓よろしければクリックをお願いします!

TOPページへ戻る


■社内DX案件紹介

【社内DX案件紹介】アンケートの集計-1-

【社内DX案件紹介】アンケートの集計-2-

【社内DX案件紹介】架電内容ごとの情報振り分け-1-

【社内DX案件紹介】架電内容ごとの情報振り分け-2-

【社内DX案件紹介】2つのファイルの内容を比較する

【社内DX案件紹介】封入物の重さによって印刷会社を変える

【社内DX案件紹介】値の入っているシートのみ印刷する


■社内DXの進め方

【社内DXの進め方】ブログの目的(会社が求めるDX)

【社内DXの進め方】社内DXの推進について

【社内DXの進め方】社内DXは具体的に何をすればよいのか?

【社内DXの進め方】なぜ今Excelか?


2023年1月11日水曜日

【ASN.1】PER(Packed Encoding Rules)コーディングとは

※注意! 本内容は独学で得た知識のため、内容に誤りがある場合があります。
     予め誤りがある前提でご覧ください。 

■PERコーディングとは

PERコーディングとは、極力データサイズを小さくするためにできた符号化規則で「プリアンブル(省略可)・データ長(省略可)・値(省略可)」の3種類で構成されています。

またオクテット列ではなく、ビット列で構成されているため情報が圧縮されてより少ないデータ量で転送が可能になります。

プリアンブルとしては主に「Extensionbit」「OptionalBitmap」「DefaultBitmap」があり、データ長として「DataLength」、値として「Value」となっています。

構成省略可否説明
Extensionbit省略可拡張部(...)があるかどうかを1Bitで判定
OptionalBitmap省略可オプション項目(OPTIONAL)数分のBitsで判定
DefaultBitmap省略可デフォルト項目(DEFAULT)数分のBitsで判定
DataLength省略可データを表すのに必要なBit数を定義。SIZEや要素数で判定
Value省略可表示される情報

・Extensionbit

将来拡張される可能性がある場合があるため、拡張される場合を想定して「...」を付与することで定義される。

この「...」がある場合は最初の1BitをExtensionbitとして追加情報があるかどうか判断される。

例えば'60'Hという情報が転送されてきたとして、ASN.1の情報として

Data ::= SEQUENCE {
	value	Value
}

Value ::= INTEGER(0..15)

である場合は、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |value    |Value
  60|0110|Value INTEGER Data            |    6|        6|
    |0000|Padding                       |     |         |

となるので

Data
	value = 6

という結果が返ります。

しかし、以下のように

Data ::= SEQUENCE {
	value	Value,
	...
}

Value ::= INTEGER(0..15)

Extensionbit(...)がある場合は、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  60|0___|Data SEQUENCE Extensionbit    |    0|False    |
    |    |Data SEQUENCE Data            |     |value    |Value
    |_110|Value INTEGER Data            |   12|       12|
    |0___|                              |     |         |
    |_000|Padding                       |     |         |

となるので

Data
	value = 12

という結果が返ります。


・OptionalBitmap

情報において必須項目(Mondatry)とオプション(Optional)の2種類があります。

ASN.1においてもそれは同様でオプション項目には以下のようにOPTIONALが付与され、OptionalBitmapにてオプション項目が有効か無効か判定されます。

例えば'60 24'Hという情報が転送されてきたとして、ASN.1の情報として

Data ::= SEQUENCE {
	value1	Value,           -- Mondatry
	value2	Value OPTIONAL,  -- Optional
	value3	Value OPTIONAL,  -- Optional
	value4	Value OPTIONAL,  -- Optional
}

Value ::= INTEGER(0..15)

である場合は

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  60|011_|Data SEQUENCE OptionalBitmap  |'011'|off,on,on|
    |    |Data SEQUENCE Data            |     |value1   |Value
    |___0|Value INTEGER Data            |    0|        0|
    |000_|                              |     |         |
    |    |Data SEQUENCE Data            |     |value3   |Value
    |___0|Value INTEGER Data            |    1|        1|
  24|001_|                              |     |         |
    |    |Data SEQUENCE Data            |     |value4   |Value
    |___0|Value INTEGER Data            |    2|        2|
    |010_|                              |     |         |
    |___0|Padding                       |     |         |

となるので、

Data
	value1 = 0
	value3 = 1
	value4 = 2

という結果が返ります。


・DefaultBitmap

パラメータとして標準値が決まっている場合、敢えてその値をBitsから取り出さなくてもDEFAULTを付与することで判定用の1bitで標準値を取り出すことが可能になります。

例えば'80'Hという情報が転送されてきたとして、ASN.1の情報として

Data ::= SEQUENCE {
	value	Value
}

Value ::= INTEGER(0..15)

である場合は

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |value    |Value
  80|1000|Value INTEGER Data            |    8|        8|
    |0000|Padding                       |     |         |

となるので、

Data
	value = 8

という結果が返ります。

しかしここで以下のように

Data ::= SEQUENCE {
	value	Value DEFAULT 99
}

Value ::= INTEGER(0..15)

DEFAULTを付与すると

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  80|1___|Data SEQUENCE DefaultBitmap   |  '1'|on       |
    |    |Data SEQUENCE Default Data    |     |       99|
    |_000|Padding                       |     |         |
    |0000|                              |     |         |

となるので、

Data
	value = 99

という結果が返ります。


・DataLength

データ長を定義する場合SIZE(a)のように固定長である場合とSIZE(a..b)のように可変長である場合があります。

SIZE(a)の場合はaを表すことができるBit数を確保すれば良いですがSIZE(a..b)のように可変長である場合はそのサイズを測るBit数の判定が必要となります。

例えば'10'Hという情報が転送されてきたとして、ASN.1の情報として

Value ::= INTEGER(4..19)

である場合は、19-4である15を表すことができる4Bits(’1111’B)が必要になります。

しかしここで重要なのは初期値(Value=0のとき)が4なので、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  10|0001|Value INTEGER Data            |    1|    1+4=5|
    |0000|Padding                       |     |         |

となります。そうなると値としては

value = 5

という結果が返ります。


↓よろしければクリックをお願いします!

TOPページに戻る


■関連ページ

【ASN.1】ASN.1(Abstract Syntax Notation One)とは

【ASN.1】ASN.1の型とPERのデコード