何年か前に仕事でモジュラス10ウェイト3でチェックデジットを計算するというコードを書いた。ひょっこりコードの断片が見つかったので記事に残しておこうと思う。
モジュラス10 (JAN) ‐ 通信用語の基礎知識 (wdic.org)
https://www.wdic.org/w/WDIC/%e3%83%a2%e3%82%b8%e3%83%a5%e3%83%a9%e3%82%b910%20(JAN)
JANの読み取りミスを防ぐために使われるチェック用数字(チェックディジット)の算出方法のこと。
また、ISBNが13桁化しISBN-13となった際、JANコードの仕様をそのまま採用したことから、チェックディジットの計算方法もモジュラス10となった。
JANのアルゴリズムは、末尾から奇数桁に3、偶数桁に1を掛けてから単純に足し算し、結果を10で割った余り(剰余)を求め、10からその剰余を引いた値をチェックディジットとして用いている。
だらだら書きたくなかったので無理くり1行にまとめています。いわゆる自己満足なコードです。
/**
* チェックデジット算出. モジュラス10ウェイト3
* @return
*/
public static String calcCheckDigit(String x) {
int sum = 0;
char[] c = x.toCharArray();
for (int i = 0; i < c.length; i++) {
sum += (c[i] & 0xf) * (((i ^ c.length) & 1) << 1 | 1);
}
return DIGIT_TBL[sum % 10];
}
/**
* デジット仕上げ用テーブル
*/
static final String[] DIGIT_TBL = { "0", "9", "8", "7", "6", "5", "4", "3", "2", "1" };
(c[i] & 0xf)
はアスキーコードの数値化です。”0″は0x30
、”1″は0x31
、”9″は0x39
なので下位4ビットをマスクしてます。
((i ^ c.length) & 1)
は、iが末尾から奇数桁なら 1、偶数桁なら 0を返す式です。なんで”^” XORを採用したかですが、末尾から数えて奇数桁か偶数桁かどうかは文字長が奇数か偶数かで決まるのでこうした気がします。単に末尾からの桁数なので ((c.length - i) & 1)
とかでもOKですよね。
(((i ^ c.length) & 1) << 1 | 1)
は、3倍か1倍かの振り分けをシフト演算で実現しています。奇数桁なら 1なので 1<<1
で 2進数で10
、|1
により 二進数で11
になります。つまり 3です。
偶数桁なら 0なのでいくらシフトしてもゼロ。|1
により1 です。
デジット仕上げ用テーブルは「10からその剰余を引いた値をチェックディジット」の部分を担当しています。数値を文字にして戻り値にする関数だったので、配列を用いて数値→文字変換も同時に解決しています。
権利を主張するようなものではないので、自由に使ってもらって構わないです。責任はとれませんのでちゃんとデバッグはしてくださいね。。