ISBNをいろいろするライブラリ

ISBNの13桁化(ISBN(国際標準図書番号)規格改定等について お知らせ)に伴って、既存のデータベースの10桁を変換するために、作ったものです。

  • 10桁、13桁ISBNのチェックデジットの計算
  • 10桁ISBNと13桁JANの相互変換
  • チェックデジットなしのコードをチェックデジットを付けた10桁、13桁ISBNに変換

入力値は最低9桁あれば、10桁、13桁のISBNに変換できるようにしています。2段目JANコード(C分類、価格)の意味も表示できます。

追記

isbnlib.py

※最新版はGist:227568をどうぞ。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""ISBN(国際標準図書番号)規格改定
http://www.isbn-center.jp/whatsnew/kikaku.html
ISBNコードをいろいろするライブラリ。isbnlib.py
"""
import os,sys

#13桁ISBNの接頭文字
ISBN13_PREFIX = ('978','979')
#書籍JANの接頭文字(ISBNと同様)
JAN_PREFIX_1ST = ISBN13_PREFIX
#書籍JAN2段目の接頭文字
JAN_PREFIX_2ND = ('192','191')
#2005年12月現在、10桁に対して付加する接頭文字
ISBN13=ISBN13_PREFIX[0]

def cd10(code):
  """10桁ISBNのチェックデジットを計算して返す。
  http://www.kikuchi.biz-web.jp/barcodefile/ISBN.html
  """
  s = str(code)[:9]
  w = 10
  a = 0
  for i in range(len(s)):
    a = a + (int(s[i]) * w)
    w -= 1
  d = 11 - (a % 11)
  if d == 10: d = "X"
  if d == 11: d = "0"
  return d

def cd13(code):
  """13桁ISBNのチェックデジットを計算して返す。
    http://www.isbn-center.jp/whatsnew/kikaku.html
  """
  s = str(code)[:12]
  a = b = 0
  for i in range(0,len(s),2): a = a + int(s[i])
  for i in range(1,len(s),2): b = b + int(s[i])
  d = (a + (b*3)) % 10
  if d>0: d = 10 - d
  return d

def cdjan(s): return cd13(s)

class ISBN(object):
  """10桁と13桁のISBNを相互変換するクラス
  """
  def __init__(self, anyisbn):
    """クラスの初期化
    9桁(チェックデジットなし)、10桁、13桁(JAN)のいずれの形式でも
    受け入れる。逆にそれ以外は受け入れない。
    """
    self.raw = None
    anyisbn = anyisbn.strip().upper().replace('ISBN','').replace('-','')
    if len(anyisbn) in (9,10):
      self.raw = anyisbn[:9]
    elif len(anyisbn)==13:
      self.raw = anyisbn[3:12]
    else:
      raise ValueError,"ISBN [%s] length error. should be 9 or 10 or 13" % anyisbn

  def isbn10(self): return "%s%s" % (self.raw,self.old_checkdegit())
  def oldstyle(self): return self.isbn10()
  def old_checkdegit(self): return cd10(self.raw)

  def isbn13(self): return "%s%s%s" % (ISBN13,self.raw,self.new_checkdegit())
  def newstyle(self): return self.isbn13()
  def new_checkdegit(self): return cd13(ISBN13+self.raw)

class CCode(object):
  """Cコード(バーコードの2段目に含まれている)の内容を保持しているクラス。
  """
  _target = (u"一般",u"教養",u"実用",u"専門","None",u"婦人",u"学参I (小・中学生対象)",u"学参II(高校生対象)",u"児童 (中学生以下対象)",u"雑誌扱い")
  _style = (u"単行本",u"文庫",u"新書",u"全集・双書",u"ムック・その他",u"事・辞典",u"図鑑",u"絵本",u"磁性媒体など",u"コミック")
  _ndc = (u"総記",u"百科事典",u"年鑑","None",u"情報科学","None","None","None","None","None",
  u"哲学",u"心理(学)",u"倫理(学)","None",u"宗教",u"仏教",u"キリスト教","None","None","None",
  u"歴史・総記",u"日本歴史",u"外国歴史",u"伝記","None",u"地理",u"旅行","None","None","None",
  u"社会科学総記",u"政治(含む国防軍事)",u"法律",u"経済・財政・統計",u"経営","None",u"社会",u"教育","None",u"民族風習",
  u"自然科学総記",u"数学",u"物理学",u"化学",u"天文・地学",u"生物学","None",u"医学・薬学","None","None",
  u"工学工業総記",u"土木",u"建築",u"機械",u"電気",u"電子・通信",u"海事",u"採鉱・冶金",u"その他工業","None",
  u"産業総記",u"農林業",u"水産業",u"商業","None",u"交通通信業","None","None","None","None",
  u"芸術総記",u"絵画・彫刻",u"写真・工芸",u"音楽・舞踏",u"演劇・映画",u"体育・スポーツ",u"諸芸・娯楽",u"家事","None",u"コミック・劇画"
  u"語学総記",u"日本語",u"英米語","None",u"ドイツ語",u"フランス語","None",u"外国語","None","None",
  u"文学総記",u"日本文学総記",u"日本文学詩歌",u"日本文学小説","None",u"日本文学・評論・随筆その他","None",u"外国文学小説",u"外国文学その他","None")

  def __init__(self,ccode=None):
    self.raw = ccode
    if self.raw:
     self.target = int(ccode[0:1])
     self.targetv = self._target[self.target]
     self.style = int(ccode[1:2])
     self.stylev = self._style[self.style]
     self.ndc = int(ccode[2:4])
     self.ndcv = self._ndc[self.ndc]
  def __str__(self): return self.raw

class BookJAN(ISBN):
  """2段目バーコードのデータをCコードと価格に分類するクラス。
  Cコードの細かい内容はCCodeクラスが管理する。
  """
  def __init__(self, input):
    if len(input)==13:
      if input[:3] in JAN_PREFIX_1ST:
         super(BookJAN,self).__init__(input)
         self.ccode = None
         self.price = None
      elif input[:3] in JAN_PREFIX_2ND:
         self.raw = input
         self.ccode = CCode(self.raw[3:7])
         self.price = int(self.raw[7:12])
      else:
       raise Exception,"[%s] does not valid Book JAN code" % input
    else:
     raise Exception,"[%s] does not valid Book JAN code" % input

def test(encoding='euc-jp'):
  """テストルーチン"""
  tests = ('494999908','4798108545',
    '4-7741-2228-9','ISBN4-7741-2228-9','isbn4774122289',
    'ISBN450123456','12345678901','9784398470270','9784817180148')

  print "="*20
  print " ISBN"
  print "="*20

  for c in tests[0:4]:
    isbn = ISBN(c)
    print "Raw   ",isbn.raw,"[<-",c,"]"
    print "Old    ",isbn.oldstyle()
    print "New ",isbn.newstyle()
    print ""

  print "="*20
  print " Book JAN"
  print "="*20
  tests = ('9784398470270','9784817180148')
  for c in tests[0:4]:
    jan = BookJAN(c)
    print "RAW   ",jan.raw
    print "New   ",jan.newstyle()
    print ""

  print "="*20
  print " Book JAN CCode"
  print "="*20
  tests = ('1910193003605','1923055038004')
  for c in tests[0:4]:
    jan = BookJAN(c)
    print "RAW   ",jan.raw
    if jan.ccode:
     print "C-CODE   ",jan.ccode
     print "C-CODE.Target ",jan.ccode.target,' :',jan.ccode.targetv.encode(encoding)
     print "C-CODE.Style  ",jan.ccode.style,' :',jan.ccode.stylev.encode(encoding)
     print "C-CODE.NDC    ",jan.ccode.ndc,':',jan.ccode.ndcv.encode(encoding)
     print "PRICE   ",jan.price
    print ""

  idx = 0
  cc = 0
  for c in CCode()._ndc:
    print idx,
    if c:
      print c.encode(encoding)
      cc += 1
    else:
      print c
    idx += 1
  print cc,'/',len(CCode()._ndc)

if __name__=='__main__':
  encoding = "euc-jp"
  if sys.platform == 'win32': encoding="cp932"
  test(encoding)

谷根千83号出来上がりました

これから配達、発送作業に入るのでしばしお待ちを。

特集は、「「キモノ」をめぐる生活 はたらくキモノ」f:id:ymorimoto:20060407133243j:image:small

クイズが間に合わず、まだ谷根千ねっと上では掲載できないのでここで。

2006.4.12 追記

クイズ公開しました。3問正解で「谷根千5号(1985.09.15発行) 森鴎外特集」を全文お読みいただけます。どうぞご利用ください。