# Delphi で ISBN のチェックディジットを計算してみる --- tags: Delphi programming Pascal objectpascal created_at: 2020-12-15 updated_at: 2020-12-17 --- # はじめに 書籍には **ISBN (国際標準図書番号)** というコードが付いています。 例えば古い書籍を調べていたとします。この書籍の第 2 版の ISBN-10 コードが Google 先生で調べてみても判りませんでした。 | 版 | ISBN-10 | 出版年 | |:---:|:---|:---:| |第 1 版| 3519023385 | 1977 | |第 2 版| ?????????? | 1981 | |第 3 版| 3519223384 | 1984 | |第 4 版| 3519323389 | 1986 | ...ですが、この ISBN コード、よく見ると前から 5 文字目が連番になっているようです。恐らく第 2 版は **351912338?** だと思われるのですが、最後の一文字 (チェックディジット) が判らないため、10 桁の ISBN コードを得ることができません。 実際には前 9 桁が判れば Google 先生で引っ掛かる事が多いのですが、折角なので Delphi で ISBN のチェックディジットを求めてみたいと思います。 **See also:** - [ISBN (Wikipedia)](https://ja.wikipedia.org/wiki/ISBN) # コード (ISBN-10) チェックディジットの求め方は Wikipedia にある通りですが、英語版 Wikipedia の説明の方が親切だと思います。 **See also:** - [ISBN (Wikipedia:en)](https://en.wikipedia.org/wiki/International_Standard_Book_Number) ## Delphi の場合 使用する Delphi は **10.4 Sydney** です。インライン変数宣言を使っているため、**10.3 Rio** よりも前の Delphi をお使いの場合にはコードの修正が必要となります。 数字以外の文字入力を無視するようにしたため、ソースコードが少し長くなっていますが、`3-519-02338-5` のようにハイフン込みで入力しても、チェックディジットを正しく計算してくれます。 ```pascal:isbn10.dpr program ISBN10; {$APPTYPE CONSOLE} uses System.SysUtils; const STARTNUM = Ord('0'); begin Write('Input ISBN-10: '); var s := StringOfChar('0', 9); var idx := 1; while not Eoln do begin var c: Char; Read(c); if CharInSet(c, ['0'..'9']) then begin s[idx] := c; idx := idx + 1; if idx = 10 then Break; end; end; Writeln; var v1 := 0; for var i in [1..9] do v1 := v1 + (Ord(s[i]) - STARTNUM) * (11 - i); var v2 := (11 - (v1 mod 11)) mod 11; var cd: Char; if v2 = 10 then cd := 'X' else cd := Chr(STARTNUM + v2); Writeln('The check digit of ', s, ' is ', cd, '.'); end. ``` **See also:** - [Delphi (Embarcadero)](https://www.embarcadero.com/jp/products/delphi) - [Delphi Community Edition (Embarcadero)](https://www.embarcadero.com/jp/products/delphi/starter) ## 標準 Pascal の場合 折角なので**標準 Pascal** でも書いてみました。Delphi のコードは標準 Pascal への移植を意識したものになっているため、大きな違いはありません。検証に使ったのは **Pascal-P5** です。 ```pascal:isbn10.pas program ISBN10(Input, Output); label 1; var i, idx, sn, v1, v2: Integer; s: packed array [1..9] of Char; c, cd: Char; begin sn := Ord('0'); Write('Input ISBN-10: '); s := '000000000'; idx := 1; while not Eoln do begin Read(c); if c in ['0'..'9'] then begin s[idx] := c; idx := idx + 1; if idx = 10 then goto 1; end; end; Writeln; 1: v1 := 0; for i:=1 to 9 do v1 := v1 + (Ord(s[i]) - sn) * (11 - i); v2 := (11 - (v1 mod 11)) mod 11; if v2 = 10 then cd := 'X' else cd := Chr(sn + v2); Writeln('The check digit of ', s, ' is ', cd, '.'); end. ``` **See also:** - [割と簡単に '標準 Pascal' を試してみたい (Qiita)](./41e95154e8da2f901698.md) - [Pascal-P シリーズについて (Qiita)](./adab81f28295a124e9eb.md) # コード (ISBN-13) ISBN-13 のチェックディジットも求めてみましょう。こちらのほうがアルゴリズム的にはスッキリ書けます。 ## Delphi の場合 ISBN-10 のものと比べて、コードが少し短くなっています。 ```pascal:isbn13.dpr program ISBN13; {$APPTYPE CONSOLE} uses System.SysUtils; const STARTNUM = Ord('0'); WEIGHT: array [Boolean] of Integer = (3, 1); begin Write('Input ISBN-13: '); var s := StringOfChar('0', 12); var idx := 1; while not Eoln do begin var c: Char; Read(c); if CharInSet(c, ['0'..'9']) then begin s[idx] := c; idx := idx + 1; if idx = 13 then Break; end; end; Writeln; var v1 := 0; for var i in [1..12] do v1 := v1 + (Ord(s[i]) - STARTNUM) * WEIGHT[Odd(i)]; var v2 := 10 - (v1 mod 10); var cd := Chr(STARTNUM + v2); Writeln('The check digit of ', s, ' is ', cd, '.'); end. ``` ## 標準 Pascal の場合 こちらもコードが少し短くなっています。 ```pascal:isbn13.pas program ISBN13(Input, Output); label 1; var i, idx, sn, v1, v2: Integer; s: packed array [1..12] of Char; c, cd: Char; w: array [Boolean] of Integer; begin sn := Ord('0'); w[False] := 3; w[True] := 1; Write('Input ISBN-13: '); s := '000000000000'; idx := 1; while not Eoln do begin Read(c); if c in ['0'..'9'] then begin s[idx] := c; idx := idx + 1; if idx = 13 then goto 1; end; end; Writeln; 1: v1 := 0; for i:=1 to 12 do v1 := v1 + (Ord(s[i]) - sn) * w[Odd(i)]; v2 := 10 - (v1 mod 10); cd := Chr(sn + v2); Writeln('The check digit of ', s, ' is ', cd, '.'); end. ``` # ISBN-10 と ISBN-13 の相互変換 ISBN-10 と ISBN-13 は変換可能です。例として次の ISBN コードを変換してみます。 | ISBN | コード | |:---:|:-:| | **ISBN-10** | 3519023385 | | **ISBN-13** | 9783519023388 | ## ISBN-10 から ISBN-13 へ ISBN-13 のプログラムを使います。固定の **接頭記号** と ISBN-10 の先頭 9 文字を入力します (10 文字のままでも構いません)。 | 接頭記号 | コード | CD | |:---:|:-:|:---:| | 978 [^1] | 351902338 | 5 | ![image.png](./images/fb358d99-49ef-adc6-85a5-69c22613b071.png) ISBN-13 コード `9783519023388` が得られました。 ## ISBN-13 から ISBN-10 へ ISBN-10 のプログラムを使います。接頭記号の先頭 3 文字と最後のチェックディジットを抜いた 9 文字を入力します (先頭 3 文字を抜いた 10 文字でも構いません)。 | 接頭記号 | コード | CD | |:---:|:-:|:---:| | 978 [^1] | 351902338 | 8 | ![image.png](./images/f181d5ab-a4e6-8562-8f72-f563e2d0d42b.png) ISBN-10 コード `3519023385` が得られました。 # おわりに 冒頭の **351912338?** のチェックディジットは `X` です。ISBN-10 は `351912338X` だったのですね。 ![image.png](./images/4ade7623-dbfa-7a72-11e5-df0b2cd6811d.png) Google 先生にこのコードを尋ねても大した情報は得られないという...ちなみに書籍名は**『Compilerbau: Eine Einführung (第 2 版)』**です。 今年は Delphi 25 周年、Pascal 50 周年という事でイロイロな Delphi / Pascal 関連書籍を読んだり調べたりしました。 **See also:** - [Pascal / Delphi 関連の書籍を読んでみる (まとめ) (Qiita)](./34bbcdea27ce224079e0.md) [^1]: 一部の国では接頭記号が `979` であるため、ISBN-10 から ISBN-13 への変換は完全ではありません。