スターリンソートっていうのが流行っていたらしいんだけども流行に乗り遅れた。
とりあえず、Qiitaで最近ちょいちょい見るようになった大元はこれなのかな? qiita.com
なお、本家(?)は下記 github.com
ちょっとコード等が長いので表示分割
続きを読むスターリンソートっていうのが流行っていたらしいんだけども流行に乗り遅れた。
とりあえず、Qiitaで最近ちょいちょい見るようになった大元はこれなのかな? qiita.com
なお、本家(?)は下記 github.com
ちょっとコード等が長いので表示分割
続きを読む先日、WindowsバッチファイルとAwkでチェックデジットを計算するプログラムを書きました。
その続きです。多分最後です。
Windowsバッチファイルでマイナンバーのチェックデジットを計算 - 沖の雑記帳
awkでマイナンバーのチェックデジットを検証・集計 - 沖の雑記帳
元の記事と同様に参照元は以下のQiitaの記事です。
このシリーズいつまで続けようか?そもそも需要がない言語がほとんどだよね?
この辺は私がおっさんなので新しい言語を触ってないというよりも仕事柄古い言語を触ることが多いだけでもあるんだけどね。
さて、続きは長くなるので一旦折りたたみ
続きを読む先日、Windowsバッチファイルでチェックデジットを計算するプログラムを書きました。
Windowsバッチファイルでマイナンバーのチェックデジットを計算 - 沖の雑記帳
元の記事と同様に参照元は以下のQiitaの記事です。
でも、集計するならawkとか使ったほうが楽だよね。それにまだawk版はなさそうじゃん?ってことで書きました。
ただ、awkって実計算部分は他の言語と大差ないんだよね
なので今回は解説らしい解説も特には不要かな?
配列や、文字数の開始は1からで0ではないこと。この辺は他の言語と違っています。
変数は全てグローバル。関数や、各パターンマッチで同じ変数名使うと書き換わるよ。*1
パターンマッチした時の処理にnext
とか書いとかないと次に一致したパターンも延々処理していくよ。*2
と、こんなところでしょうか?
それでは続いてコードを貼り付けましょうね。
# サマリ出力するデータの初期化 BEGIN { invalid_count = 0 valid_count = 0 unexpected_count = 0 } # 全入力データを保存 { mynumber[FNR] = $0 } # 数値のみの行にマッチ /^[0-9]+$/ { sum = 0 if (length($0) == 12) { print "mynumber : " $0 digit = calc_checkdigit(substr($0,1,11)) print "digit : " digit if (digit == substr($0,12,1)) { print "validate : true" valid_count++ result[FNR] = 1 } else { print "validate : false" invalid_count++ result[FNR] = 0 } } else { print $0 " :requires 12-digit." unexpected_count++ } print "" next } # 非数を1つでも含む行にマッチ /[^0-9]+/ { print $0 " : Not be used non-numeric.\n" unexpected_count++ next } # どのパターンにもマッチしてこなかった場合は不正データ # 改行のみの場合などはここに来る { print "Unexpected data.\n" unexpected_count++ } # 検証結果のサマリ出力 END { print "Summary:" print " Number of data : " FNR print " Valid data : " valid_count for (i = 1; i <= FNR; i++) { if (result[i]) { print "\t" mynumber[i] } } print " Invalid data : " invalid_count print " Illegal data : " unexpected_count # 不正入力を出したい時だけ # for (i = 1; i <= FNR; i++) { # if (!result[i]) { # print "\t" mynumber[i] # } # } } # チェックデジットの計算 # 入力は11桁の数値列 function calc_checkdigit(number){ for (i = 1; i < 12; i++) { p = substr(number,12-i,1) if (i <= 6) { q = i + 1 } else { q = i - 5 } sum += p * q } remainder = sum % 11 if (remainder <= 1) { return 0 } else { return 11 - remainder } }
基本的にはコメントの通りです。
今回はそれぞれの入力に大してチェックデジットやtrue/falseなどを出力していますが、どちらかというと正しいものだけを抽出して出力したり表形式で正否を出力したりといった使い方の方が実用性があるでしょう。
一部を除いてほぼC言語なんかと変わらないですね。
以下検証関連
最後に入力データ数、チェック結果の数と、正しいデータのみを出力しています。
Visual Batを入れてみたことだし、使い勝手の検証のためになんかいいものはないかなと思ったらこんな記事を見つけました。
ちょっと流行(?)に乗り遅れた感じがしますね。
でもそもそもバッチファイル自体が流行以前の問題なので問題ないですね。
計算方法やらなんやらは上記サイトを参照してください。
というわけで、早速書いてみました。なんか割りと試行錯誤しすぎてかなり汚いコードになってます。
もっと綺麗に書けるかもしれないんで、わかる方がいたらコメントいただけないでしょうか?
@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点含まれているところでしょうか?
剰余の計算が他の言語同様に%
として使用できるのですが、バッチファイルでは%
を表す場合は%%
と重ねて書かなければなりません。
なので%%
と二重に書いています。
さて解説は以上のとおりとなります。
いかがでしたでしょうか?バッチファイルは本来計算が得意な言語(?)ではありませんが工夫次第で色々なことができそうですね。
Awk版を作成しました。
awkでマイナンバーのチェックデジットを検証・集計 - 沖の雑記帳
WinBatch版及びAwk版をそれぞれGitHubにあげてみました。
okiworks (Hironao Oki) · GitHub
このページも含めリンクしてくださっている、yumedotoさんのページリンクを追加
様々な言語での実装状況まとめも作成されています。
*1:使用直前で変数が更新される。遅延展開しない場合は最初にセットされた値
仕事でPython
を使うようになったのでこれまで惰性で書いてた部分も含めて勉強中
python
の内包表記でのif
文やif
~else
文の使い方がいつも思い出せなくなって毎回調べてるのでメモ
まずは元のデータ あるバイナリデータからオフセットのリストを取り出したもの
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']