沖の雑記帳

基本的には趣味に絡んで雑多な内容を色々と

Windowsバッチファイルでマイナンバーのチェックデジットを計算

Visual Batを入れてみたことだし、使い勝手の検証のためになんかいいものはないかなと思ったらこんな記事を見つけました。 ちょっと流行(?)に乗り遅れた感じがしますね。
でもそもそもバッチファイル自体が流行以前の問題なので問題ないですね。

マイナンバーのチェックデジットを計算する - Qiita

計算方法やらなんやらは上記サイトを参照してください。

というわけで、早速書いてみました。なんか割りと試行錯誤しすぎてかなり汚いコードになってます。
もっと綺麗に書けるかもしれないんで、わかる方がいたらコメントいただけないでしょうか?

マイナンバーのチェックデジット計算

@echo off
REM validate_mynumber.bat
REM マイナンバーのチェックサムを検証する
REM 参考:http://qiita.com/qube81/items/fa6ef94d3c8615b0ce64
setlocal enabledelayedexpansion
call :calc_strlen %1
if %strlen% neq 12 echo requires 12-digit input & goto end

set mynumber=%1
set check_digit=%mynumber:~-1%
set /a sum=0
set temp=%mynumber:~0,-1%

for /l %%I in (1,1,11) do (
  if %%I leq 6 (
    set /a q=%%I+1
  ) else (
    set /a q=%%I-5
  )
  set p=!temp:~-1!
  set /a sum+=!p!*!q!
  set temp=!temp:~0,-1!
)
set /a remainder=!sum!%%11

if !remainder! leq 1 (
  set check=0
) else (
  set /a check=11-!remainder!
)
if !check_digit! equ !check! (
  echo true
) else (
  echo false
)
goto end

:calc_strlen
set str=%1
set strlen=0
:loop_calc
if defined str (
  set str=%str:~1%
  set /a strlen+=1
  goto :loop_calc
)
exit /b

:end
endlocal

検証用バッチファイル

@echo off
setlocal enabledelayedexpansion

set number=12345678901
for /l %%I in (0,1,9) do (
  echo input:%number%%%I
  call validate_mynumber %number%%%I
)

endlocal

検証結果

$ test.bat
input:123456789010
false
input:123456789011
false
input:123456789012
false
input:123456789013
false
input:123456789014
false
input:123456789015
false
input:123456789016
false
input:123456789017
false
input:123456789018
true
input:123456789019
false

ちゃんと、8の時にtrueになっているので合っているようですね。
一応、自分のマイナンバーも入力してみましたがしっかりtrueが返ってきたのでよかった(返ってこないと大問題)

追記

各言語の実装をまとめられる方からコメントをいただきました。
参考までに現在どのような言語でマイナンバーのチェックデジット計算が実装されているか、自分はここにはない別の言語で実装してやるなんかの参考にもどうぞ

C++でマイナンバーのチェックデジットを計算する - Qiita

解説

処理の解説がないのは少々不親切なので追記

文字数の計算

Batファイルにはそもそも文字列長を計算してくれるような機能はありません
なので少々強引に文字列長の計算をする処理を:calc_strlenラベル以下に記述しています。

:calc_strlen
set str=%1
set strlen=0
:loop_calc
if defined str (
  set str=%str:~1%
  set /a strlen+=1
  goto :loop_calc
)
exit /b

今回は処理を簡略化するために第1引数以外を無視しています。
:calc_strlenが文字列長を計算し、strlenという変数に文字列長を代入します。
%1は第1引数
if defined strはバッチファイルの仕様上、空でなければ定義されているという扱いになるため変数strが空になるまでループさせ、変数strlenをインクリメントする処理になっています。
%str:~1%:これは、strの1文字目から最後までを取得という意味。0文字目開始なので、ループ中で空になるまで1文字ずつシフトしています。

if %strlen% neq 12はそのままstrlenが12じゃなかったらという意味ですね。
今回扱いたい文字列長は12桁の数値なので12文字以外は処理を行わないようにしています。

バッチファイルで使用できる比較演算子については、コマンドプロンプトif /?などと入力していただければ確認できるので省略します。

初期化処理
set mynumber=%1
set check_digit=%mynumber:~-1%
set /a sum=0
set temp=%mynumber:~0,-1%

特に解説すべきところはなさそうですが、2行目についてset check_digit=%mynumber:~-1%の部分は、最後から1文字目から最後まで文字列を取得
また、set temp=%mynumber:~0,-1%の部分は、0文字目から最後から1文字目までの文字列を取得しています。

バッチファイルの文字列は0文字目を起点とするため、変数名:の後に0以上の数値を指定することで指定した位置からの文字を取得することができます。
同様に、変数名:(開始位置),(終了位置)とすることで開始位置から終了位置までの文字列を取得することができます。
また0以上の数値を指定すると開始位置からの文字数、負の値を指定すると終了位置からの文字数を指定することができます。

これらのことを利用して検証用のチェックデジットと計算に使用するための数字を分割しています。

計算・検証

for /l %%I in (1,1,11) do (
  if %%I leq 6 (
    set /a q=%%I+1
  ) else (
    set /a q=%%I-5
  )
  set p=!temp:~-1!
  set /a sum+=!p!*!q!
  set temp=!temp:~0,-1!
)

まず前半部分です。FOR文の使用方法についてもfor /?とすることで確認可能なので省略
今回、!(変数名)!という表記が出てきていますがこちらは変数の遅延展開*1を使用しています。
この辺りから遅延展開というものを使わないとどうやらうまく計算されなくてドハマリしてました。
ただ計算内容自体は参考にしたRuby版と同様で、i≦6の場合はi+1を、i>7の場合はi-5をqとして、検証用に計算しているだけですね。

set /a remainder=!sum!%%11

if !remainder! leq 1 (
  set check=0
) else (
  set /a check=11-!remainder!
)
if !check_digit! equ !check! (
  echo true
) else (
  echo false
)

最後は特筆すべき点はありませんね。
少しわかりにくいところとしてはバッチファイルを書く場合の注意点が1点含まれているところでしょうか?
剰余の計算が他の言語同様に%として使用できるのですが、バッチファイルでは%を表す場合は%%と重ねて書かなければなりません。
なので%%と二重に書いています。

さて解説は以上のとおりとなります。

いかがでしたでしょうか?バッチファイルは本来計算が得意な言語(?)ではありませんが工夫次第で色々なことができそうですね。

追記(2016-03-07)

Awk版を作成しました。

awkでマイナンバーのチェックデジットを検証・集計 - 沖の雑記帳

WinBatch版及びAwk版をそれぞれGitHubにあげてみました。

okiworks (Hironao Oki) · GitHub

追記(2016-04-11)

このページも含めリンクしてくださっている、yumedotoさんのページリンクを追加
様々な言語での実装状況まとめも作成されています。

qiita.com

*1:使用直前で変数が更新される。遅延展開しない場合は最初にセットされた値

VisualBatなるものを入れてみた際に発生した問題

最近、仕事上でバッチファイルを書く必要がある人がいて色々な質問が飛んでくる。
私自身はMSX-DOSMS-DOSの頃からPCを使っているのでバッチファイル自体は未だに直感的に書いているのだけども

何か便利な…そう、オートコンプリートが出来たりちょっとしたヘルプが見れたりというものはないのだろうか? そんな中で見つけたVisualBatなるものを見つけた*1

なるソフトを見つけた。 バッチファイルを記述するための統合環境だ。

もう十数年前にあったら便利だっただろうなぁと思いつつも私はあまりお世話になることはなさそう?

で、この件とは別に起動すると必ず「キーボード履歴ユーティリティは動作を停止しました。」というダイアログボックスが表示されるのだ なんだこれは?今までこんなの見たことないぞ?とぐぐってみたら同じ原因っぽいのを見つけた。

「キーボード履歴ユーティリティ は動作を停止しました」の対処 - Forever Undecided

確かに私のPCもChocolateyDevbox-Commonを入れているしレジストリAutoRunの中に同名のキーが見つかった。
対処方法を行なってみたら見事に出なくなった。
とりあえず原因はともかくなんで干渉しているのだろうか?

忘れかけている方言

今週のお題「方言」

僕は北海道で22年間過ごしたんだけども内地*1に出てきてからすっかり方言を忘れてきている。
そんな中でも忘れていないというよりは未だに使う方言などもあるし、方言だってずっと知らずに後から知ったものも数多くある。

未だに使う方言

  • したっけ
    そうしたら
    またね
    みたいな感じの意味になるのかな?今でも咄嗟に口から出ることがある

  • なした?なしたの?
    どうしたの?
    ということですね

  • 手袋を履く
    言い換えがわかりません。手袋を…つける?

  • エアコンを焚く
    もう一つありました…
    エアコンを焚くなんて普通言いませんよね?つけるかな? 地元にいた頃はエアコンなんてなかったし、冬にストーブを焚くのと同じ感覚でいるんだろうなという自己完結
    他の地域でもいうのかな?

方言だと知らなかった

  • サビオ
    絆創膏のことです。というかこれ商品名らしいんですよね。怪我をした時に駅の売店に駆け込んでサビオください!
    と言ったときの店員の「???」という状態と言い換える語彙が浮かばずに「???」ってなった僕とで困りました

  • ガスがかかる
    いやー、昨日ガスかかってひどかったねー
    なんて話を会社の同僚にしたらガス?何言ってんの?って言われてしまった。
    霧がかかる…ということなのかな?霧がひどかったってことでいいのかな?

言い換えがわからない

  • いずい
    なんか違和感っていうか気持ち悪いっていうかしっくりこないっていうか…
    これ標準語でなんて言えばいいの?

  • うるかす
    米をうるかすとか…
    これも標準語でなんていうのかわからない

パッと思いつくのはこの辺りかな?

ただ上記以外はあちこちに移り住んでるうちに徐々に上書き(?)されてていろんな方言が混ざっちゃってます。

愛知の東三河に6年間いたり、職場で飛び交っていた名古屋弁三河弁の混合
さらにはよく麻雀を打っていた仲間の甲州弁、土佐方言

抜け切らない方言以外はもう住んでたり付き合いが長かったりの人の影響で自分でもどこの方言かわかっていません。

ただ、なぜ関東に住んでも標準語にはならないのか…ということについては、おそらく方言が弱い(という表現で良いのか?)からでしょう

というわけで、今週のお題初投稿でした。

したっけ~

*1:本州のことだね

内包表記の条件分岐覚書

仕事でPythonを使うようになったのでこれまで惰性で書いてた部分も含めて勉強中 pythonの内包表記でのif文やifelse文の使い方がいつも思い出せなくなって毎回調べてるのでメモ

まずは元のデータ あるバイナリデータからオフセットのリストを取り出したもの

print(ofslist)
(512, 20992, 25088, 26112, 36352, 39424, 40448, 44544, 45568, 56832, 60416, 60928, 62464, 62976, 63488, 64000, 64512, 67072, 67584, 68096, 69120, 81408, 81920, 83456, 84992, 87552, 88064, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

ifを使用 0より大きい部分だけ欲しい

print([x for x in ofslist if x > 0])
[512, 20992, 25088, 26112, 36352, 39424, 40448, 44544, 45568, 56832, 60416, 60928, 62464, 62976, 63488, 64000, 64512, 67072, 67584, 68096, 69120, 81408, 81920, 83456, 84992, 87552, 88064]

if~elseを使用 基本的に前詰めで入ってるはずだけど、異常データで途中に0があると解析転けるから数値じゃないデータを入れておきたい 今回はとりあえず'No Data'に置き換えた

print([x if x > 0 else 'No data' for x in ofslist])
[512, 20992, 25088, 26112, 36352, 39424, 40448, 44544, 45568, 56832, 60416, 60928, 62464, 62976, 63488, 64000, 64512, 67072, 67584, 68096, 69120, 81408, 81920, 83456, 84992, 87552, 88064, 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data', 'No data']

ドメインの移行先

ExpressWebのサービス終了に伴って、自サイトの移行先を探しています。
現在、沖の作業場ExpressWebに置いています。
ASP.NET MVCを使用したサイトになっているので移動先はできればWindows Serverなのですが金額と時間との兼ね合いを考えると
構築しなおしてWindows Serverにこだわる必要性はないのかもしれないとも思う

ただし、そうなった場合でもExpressWebが月額250円からだったことを考えるとどこを選んでも支出は増える
有力な候補としてはWinServerなのだけども、一部システムをPythonで運用しているためスターターパックでは金額が一番安くて助かるもののPythonが使えない
Pythonを使おうと思うとプレミアム以上が必須だけど月額2548円は単純に10倍以上の金額で少々厳しいものがある。

とした時に、サイトを作りなおす時間をかけてもASP.NETを諦めてUNIX系の共用サーバーを借りるのが妥当な気もしている。
月額250円から同じ料金帯での移行は難しいがせめて月額500円~833円…年間1万円以内で移行できるのが理想なのだがこれでもハードルは高いと思う
ブログは暫くこちらで運用していくつもりなので問題ないにしてもサービス終了までの残りは40日を切っている。
30日前には移行先を確定して作り始めないとDNSの移行も踏まえて、遅くとも2週間前くらいまでに契約を済ませてしまいたい。
どこかに良い共用サーバーは落ちていないものだろうか