沖の雑記帳

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

スターリンソートっていうのが流行っていたらしい

スターリンソートっていうのが流行っていたらしいんだけども流行に乗り遅れた。

とりあえず、Qiitaで最近ちょいちょい見るようになった大元はこれなのかな? qiita.com

なお、本家(?)は下記 github.com

ちょっとコード等が長いので表示分割

続きを読む

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

先日、WindowsバッチファイルとAwkチェックデジットを計算するプログラムを書きました。
その続きです。多分最後です。

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

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

元の記事と同様に参照元は以下のQiitaの記事です。

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

このシリーズいつまで続けようか?そもそも需要がない言語がほとんどだよね?
この辺は私がおっさんなので新しい言語を触ってないというよりも仕事柄古い言語を触ることが多いだけでもあるんだけどね。

さて、続きは長くなるので一旦折りたたみ

続きを読む

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

先日、Windowsバッチファイルでチェックデジットを計算するプログラムを書きました。

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

元の記事と同様に参照元は以下のQiitaの記事です。

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

でも、集計するならawkとか使ったほうが楽だよね。それにまだawk版はなさそうじゃん?ってことで書きました。
ただ、awkって実計算部分は他の言語と大差ないんだよね
なので今回は解説らしい解説も特には不要かな?

検証環境

Windows7
GNU Awk 3.1.8

awkで気をつけること

配列や、文字数の開始は1からで0ではないこと。この辺は他の言語と違っています。
変数は全てグローバル。関数や、各パターンマッチで同じ変数名使うと書き換わるよ。*1
パターンマッチした時の処理にnextとか書いとかないと次に一致したパターンも延々処理していくよ。*2
と、こんなところでしょうか?

それでは続いてコードを貼り付けましょうね。

awkマイナンバーのチェックデジット検証・集計

# サマリ出力するデータの初期化
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言語なんかと変わらないですね。

以下検証関連
最後に入力データ数、チェック結果の数と、正しいデータのみを出力しています。

*1:良い書き方ではないけど関数に関しては避ける方法はある

*2:特に何もなければ上から下まで全てのパターンをなめる

続きを読む

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:使用直前で変数が更新される。遅延展開しない場合は最初にセットされた値

内包表記の条件分岐覚書

仕事で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']