自分で書いたrubyの記事(マイナンバーのチェックデジットを計算する)を公開したらいろんな言語でやってみたって人がたくさん現れて参考になりました。ありがとうございます。
最近Goを書いてみているので、Goを使って書いてみます。細かい説明は元記事の方で。
mynumber.go
package main
import (
"fmt"
"regexp"
"strconv"
"unicode/utf8"
)
func main() {
// 有効なマイナンバーです
r := validateMyNumber("123456789018")
fmt.Println(r)
}
func validateMyNumber(numstr string) bool {
// 12文字かどうか
if utf8.RuneCountInString(numstr) != 12 {
return false
}
// 全て数字かどうか
if !regexp.MustCompile(`^[0-9]+$`).Match([]byte(numstr)) {
return false
}
// 最後の一桁をチェックデジットとする
checkDigit, _ := strconv.Atoi(numstr[11:])
// 残り11けたを逆順にします
runes := []rune(numstr[:11])
for i, j := 0, 11-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
// 1文字ずつ処理します
pq := 0
for i, r := range runes {
// 何番目か
n := i + 1
// Pn
p, _ := strconv.Atoi(string(r))
// Qn
var q int
if n >= 7 {
q = n - 5
} else {
q = n + 1
}
// Pn * Qn を足していきます
pq += p * q
}
// 条件でチェックデジットを検証します
remainder := pq % 11
if remainder <= 1 {
return checkDigit == 0
}
return checkDigit == (11 - remainder)
}
実行します。
$ go run mynumber.go
> true
合ってますね。
雑ですが、テストを書いてみます。
mynumber_test.go
package main
import (
"testing"
)
func TestValidateMyNumber(t *testing.T) {
// 12桁以外が通ったらエラー
if validateMyNumber("1234567890120000000") {
t.Error("12桁でないものが通った")
}
// 半角数字以外が通ったらエラー
if validateMyNumber("I234S6789OI2") {
t.Error("数字以外が通った")
}
// 有効なマイナンバーがはじかれたら失敗
if validateMyNumber("123456789018") == false {
t.Error("有効なものがはじかれた")
}
// 不正なものが通ってしまったら失敗
if validateMyNumber("123456789010") {
t.Error("間違ってるものが通った")
}
}
テストを実行します。
$ go test -v
=== RUN TestValidateMyNumber
--- PASS: TestValidateMyNumber (0.00s)
PASS
ok mynumber 0.006s
なんとなく、正しいみたいですね。
本当はもうちょっとテストケース書いたりすると良さそうです。
文字列->数字変換とか、正規表現とかスライスとか色々入ってて良い勉強でした。