ISBN10桁13桁変換Oracle PL/SQLパッケージ

去年作ったモノですが、気がつくと結構あちこちで使っていた。
手元ではLinux版のOracle9iで動作しています。

001/* Oracle用 ISBNパッケージ
002  10桁と13桁のISBNを相互に変換する。チェックデジットの計算もできる。
004 
005 $Id: pkg_isbn.sql,v 1.5 2005/11/07 02:12:27 ymo Exp $
006*/
007 
008CREATE OR REPLACE PACKAGE ISBN_PKG AS
009/*
010|| 入力されたISBNをチェックデジットを付けたりいろいろする。
011|| 10桁を13桁に直したり、13桁を10桁に直すこともできる。
012*/
013 FUNCTION ISBN10(i_isbn IN varchar2) RETURN varchar2;
014 FUNCTION ISBN13(i_isbn IN varchar2) RETURN varchar2;
015 
016/*
017|| 入力されたISBNをチェックデジットを抜いた大事なところだけにする。
018*/
019 FUNCTION REGULATE_ISBN(i_isbn IN varchar2) RETURN varchar;
020 
021/*
022|| 10桁版チェックデジットを計算して返す
023||  入力は9文字、10文字、13文字のどれか
024*/
025 FUNCTION CHECK_DEGIT10( i_isbn IN varchar2) RETURN char;
026 
027/*
028|| 13桁版ISBNのチェックデジットを計算して返す。
029||  入力は9文字、10文字、13文字のどれか
030*/
031 FUNCTION CHECK_DEGIT13( i_isbn IN varchar2) RETURN char;
032 
033END ISBN_PKG;
034/
035 
036----------------------------------------------------------
037--    PACKAGE BODY
038----------------------------------------------------------
039CREATE OR REPLACE PACKAGE BODY ISBN_PKG AS
040 
041----------------------------------------------------------
042-- F ISBN10
043----------------------------------------------------------
044FUNCTION ISBN10(i_isbn IN varchar2) RETURN varchar2
045IS
046   sRaw varchar2(9);
047BEGIN
048 sRaw := REGULATE_ISBN(i_isbn);
049 RETURN sRaw || CHECK_DEGIT10(sRaw);
050END;
051 
052----------------------------------------------------------
053-- F ISBN13
054----------------------------------------------------------
055FUNCTION ISBN13(i_isbn IN varchar2) RETURN varchar2
056IS
057   sRaw varchar2(9);
058BEGIN
059 sRaw := REGULATE_ISBN(i_isbn);
060 RETURN '978' || sRaw || CHECK_DEGIT13(sRaw);
061END;
062 
063----------------------------------------------------------
064-- F REGULATE_ISBN
065----------------------------------------------------------
066FUNCTION REGULATE_ISBN(i_isbn IN varchar2) RETURN varchar
067IS
068   buf varchar2(20);
069   sRaw varchar2(9);
070BEGIN
071 buf := SUBSTRB(REPLACE(REPLACE(UPPER(TRIM(i_isbn)),'ISBN'),'-'),1,20);
072 sRaw := TRIM(SUBSTRB(buf,1,9));
073 
074 IF LENGTH(buf) = 9 THEN
075  sRaw := SUBSTRB(buf, 1, 9);
076 END IF;
077 IF LENGTH(buf) = 10 THEN
078  sRaw := SUBSTRB(buf, 1, 9);
079 END IF;
080 IF LENGTH(buf) = 13 THEN
081  sRaw := SUBSTRB(buf, 4, 9);
082 END IF;
083 
084 RETURN sRaw;
085END;
086 
087----------------------------------------------------------
088-- F CHECK_DEGIT10
089----------------------------------------------------------
090FUNCTION CHECK_DEGIT10(i_isbn IN varchar2) RETURN char
091IS
092   vWeight PLS_INTEGER;
093   vSum    PLS_INTEGER;
094   vResult PLS_INTEGER;
095   sRaw varchar2(9);
096BEGIN
097 sRaw := REGULATE_ISBN(i_isbn);
098 vWeight := 10;
099 vSum := 0;
100 FOR i IN 1..LENGTH(sRaw)
101 LOOP
102   vSum := vSum + vWeight * to_number(SUBSTR(sRaw, i, 1));
103   vWeight := vWeight - 1;
104 END LOOP;
105 vResult := 11 - (vSum MOD 11);
106 if vResult = 10 THEN
107   RETURN 'X';
108 END IF;
109 if vResult = 11 THEN
110   RETURN '0';
111 END IF;
112 RETURN SUBSTR(to_char(vResult),1,1);
113 
114 EXCEPTION
115  WHEN OTHERS THEN
116    RETURN 'E';
117END;
118 
119----------------------------------------------------------
120-- F CHECK_DEGIT13
121----------------------------------------------------------
122FUNCTION CHECK_DEGIT13(i_isbn IN varchar2) RETURN char
123IS
124   vSum1 PLS_INTEGER;
125   vSum2 PLS_INTEGER;
126   vResult PLS_INTEGER;
127   r char;
128   sRaw varchar2(12);
129BEGIN
130 sRaw := '978' || REGULATE_ISBN(i_isbn);
131 
132 vSum1 := 0;
133 vSum2 := 0;
134 
135 FOR i IN 1..LENGTH(sRaw)
136 LOOP
137   IF MOD(i,2) = 1 THEN
138    vSum1 := vSum1 + to_number(SUBSTR(sRaw, i, 1));
139   ELSE
140    vSum2 := vSum2 + to_number(SUBSTR(sRaw, i, 1));
141   END IF;
142 END LOOP;
143 
144 vResult := vSum1 + (vSum2 * 3);
145 vResult := 10 - to_number(SUBSTR(to_char(vResult),length(to_char(vResult)),1));
146 IF vResult > 9 THEN
147   RETURN '0';
148 ELSE
149   RETURN SUBSTR(to_char(vResult),1,1);
150 END IF;
151 
152 EXCEPTION
153  WHEN OTHERS THEN
154    RETURN 'E';
155END;
156 
157END ISBN_PKG;
158/
159 
160 
161/**
162SELECT 'Answer => 4798108545: '||ISBN_PKG.ISBN10('479810854') FROM DUAL;
163SELECT 'Answer => 9784798108544: '||ISBN_PKG.ISBN13('4798108545') FROM DUAL;
164 
165SELECT 'Answer => 4949999087: '|| ISBN_PKG.ISBN10('494999908') FROM DUAL;
166SELECT 'Answer => 9784949999083: ' || ISBN_PKG.ISBN13('494999908') FROM DUAL;
167 
168 
169SELECT ISBN_PKG.CHECK_DEGIT10('4798108545') FROM DUAL;
170SELECT ISBN_PKG.CHECK_DEGIT10('494999908') FROM DUAL;
171SELECT ISBN_PKG.CHECK_DEGIT10('ISBN4-9499-9908') FROM DUAL;
172SELECT ISBN_PKG.CHECK_DEGIT10('9784883810246') FROM DUAL;
173SELECT ISBN_PKG.CHECK_DEGIT10('9784883810161') FROM DUAL;
174SELECT ISBN_PKG.CHECK_DEGIT10('9784431711438') FROM DUAL;
175 
176SELECT ISBN_PKG.CHECK_DEGIT13('4798108545') FROM DUAL;
177SELECT ISBN_PKG.CHECK_DEGIT13('494999908') FROM DUAL;
178SELECT ISBN_PKG.CHECK_DEGIT13('ISBN4-9499-9908') FROM DUAL;
179*/

Gist:239168

Pythonでモジュラス10ウエイト3とisbn10to13

Guidelines For Shipping Container Labeling http://www.bisg.org/docs/shipping_label_guidelines_09-2005.pdf
こんなのをやるにあたって、ちゃんとしたモジュラス10ウエイト3のチェックデジットを計算する必要があったので作った。

ついでに、isbnlib.pyよりもシンプルに10桁ISBNを13桁にする関数も。

Python版

01#!/usr/bin/env python
02# -*- coding: utf-8 -*-
03"""モジュラス10ウエイト3とISBNのチェックデジット計算。Python版。
04"""
05def m10w31(code):
06    """モジュラス10ウエイト3チェックデジットの計算
07    """
08    s = list(str(code))
09    s.reverse()
10    sum = 0
11 
12    w = 3
13    for i in s:
14        sum = sum + (int(i) * w)
15        if w == 3: w = 1
16        else: w = 3
17 
18    d = sum % 10
19    if d<>0: d = 10 - d
20    return str(d)
21 
22def isbn10to13(isbn10):
23    """10桁ISBNを13桁ISBNに変換
24    """
25    s = '978'+isbn10[:9]
26    return s+m10w31(s)
27 
28def ssccBarCode(isbn13):
29    """ SSCC bar code
31    """
32    PRFX1='01'
33    PRFX2='1'
34    code = isbn13[:12]
35    return "(%s)%s %s-%s" % (PRFX1, PRFX2, code, m10w31(PRFX2+code))
36 
37if __name__=='__main__':
38    data = ('4770025364','4770023230','4770015419','4881359290','4883731626','4873112109')
39    for isbn10 in data:
40         isbn13 = isbn10to13(isbn10)
41         print " %s -> %s -> %s" % (isbn10,isbn13,ssccBarCode(isbn13))

Delphi版は関数だけ

01function m10w31(code: string): integer;
02/**
03 Delphi版 モジュラス10ウエィト3のチェックデジット計算。
04*/
05var
06  i,w,sum: integer;
07begin
08  w := 3;
09  sum := 0;
10  for i := length(code) downto 1 do
11  begin
12    sum := sum + strtoint(code[i]) * w;
13    if w = 3 then w := 1
14    else w := 3;
15  end;
16  Result := sum mod 10;
17  if Result<>0 then Result := 10 - Result;
18end;

最新版はgithubをどうぞ。
Python版Delphi版

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をどうぞ。

001#!/usr/bin/env python
002# -*- coding: utf-8 -*-
003"""ISBN(国際標準図書番号)規格改定
005ISBNコードをいろいろするライブラリ。isbnlib.py
006"""
007import os,sys
008 
009#13桁ISBNの接頭文字
010ISBN13_PREFIX = ('978','979')
011#書籍JANの接頭文字(ISBNと同様)
012JAN_PREFIX_1ST = ISBN13_PREFIX
013#書籍JAN2段目の接頭文字
014JAN_PREFIX_2ND = ('192','191')
015#2005年12月現在、10桁に対して付加する接頭文字
016ISBN13=ISBN13_PREFIX[0]
017 
018def cd10(code):
019  """10桁ISBNのチェックデジットを計算して返す。
021  """
022  s = str(code)[:9]
023  w = 10
024  a = 0
025  for i in range(len(s)):
026    a = a + (int(s[i]) * w)
027    w -= 1
028  d = 11 - (a % 11)
029  if d == 10: d = "X"
030  if d == 11: d = "0"
031  return d
032 
033def cd13(code):
034  """13桁ISBNのチェックデジットを計算して返す。
036  """
037  s = str(code)[:12]
038  a = b = 0
039  for i in range(0,len(s),2): a = a + int(s[i])
040  for i in range(1,len(s),2): b = b + int(s[i])
041  d = (a + (b*3)) % 10
042  if d>0: d = 10 - d
043  return d
044 
045def cdjan(s): return cd13(s)
046 
047class ISBN(object):
048  """10桁と13桁のISBNを相互変換するクラス
049  """
050  def __init__(self, anyisbn):
051    """クラスの初期化
052    9桁(チェックデジットなし)、10桁、13桁(JAN)のいずれの形式でも
053    受け入れる。逆にそれ以外は受け入れない。
054    """
055    self.raw = None
056    anyisbn = anyisbn.strip().upper().replace('ISBN','').replace('-','')
057    if len(anyisbn) in (9,10):
058      self.raw = anyisbn[:9]
059    elif len(anyisbn)==13:
060      self.raw = anyisbn[3:12]
061    else:
062      raise ValueError,"ISBN [%s] length error. should be 9 or 10 or 13" % anyisbn
063 
064  def isbn10(self): return "%s%s" % (self.raw,self.old_checkdegit())
065  def oldstyle(self): return self.isbn10()
066  def old_checkdegit(self): return cd10(self.raw)
067 
068  def isbn13(self): return "%s%s%s" % (ISBN13,self.raw,self.new_checkdegit())
069  def newstyle(self): return self.isbn13()
070  def new_checkdegit(self): return cd13(ISBN13+self.raw)
071 
072class CCode(object):
073  """Cコード(バーコードの2段目に含まれている)の内容を保持しているクラス。
074  """
075  _target = (u"一般",u"教養",u"実用",u"専門","None",u"婦人",u"学参I (小・中学生対象)",u"学参II(高校生対象)",u"児童 (中学生以下対象)",u"雑誌扱い")
076  _style = (u"単行本",u"文庫",u"新書",u"全集・双書",u"ムック・その他",u"事・辞典",u"図鑑",u"絵本",u"磁性媒体など",u"コミック")
077  _ndc = (u"総記",u"百科事典",u"年鑑","None",u"情報科学","None","None","None","None","None",
078  u"哲学",u"心理(学)",u"倫理(学)","None",u"宗教",u"仏教",u"キリスト教","None","None","None",
079  u"歴史・総記",u"日本歴史",u"外国歴史",u"伝記","None",u"地理",u"旅行","None","None","None",
080  u"社会科学総記",u"政治(含む国防軍事)",u"法律",u"経済・財政・統計",u"経営","None",u"社会",u"教育","None",u"民族風習",
081  u"自然科学総記",u"数学",u"物理学",u"化学",u"天文・地学",u"生物学","None",u"医学・薬学","None","None",
082  u"工学工業総記",u"土木",u"建築",u"機械",u"電気",u"電子・通信",u"海事",u"採鉱・冶金",u"その他工業","None",
083  u"産業総記",u"農林業",u"水産業",u"商業","None",u"交通通信業","None","None","None","None",
084  u"芸術総記",u"絵画・彫刻",u"写真・工芸",u"音楽・舞踏",u"演劇・映画",u"体育・スポーツ",u"諸芸・娯楽",u"家事","None",u"コミック・劇画"
085  u"語学総記",u"日本語",u"英米語","None",u"ドイツ語",u"フランス語","None",u"外国語","None","None",
086  u"文学総記",u"日本文学総記",u"日本文学詩歌",u"日本文学小説","None",u"日本文学・評論・随筆その他","None",u"外国文学小説",u"外国文学その他","None")
087 
088  def __init__(self,ccode=None):
089    self.raw = ccode
090    if self.raw:
091     self.target = int(ccode[0:1])
092     self.targetv = self._target[self.target]
093     self.style = int(ccode[1:2])
094     self.stylev = self._style[self.style]
095     self.ndc = int(ccode[2:4])
096     self.ndcv = self._ndc[self.ndc]
097  def __str__(self): return self.raw
098 
099class BookJAN(ISBN):
100  """2段目バーコードのデータをCコードと価格に分類するクラス。
101  Cコードの細かい内容はCCodeクラスが管理する。
102  """
103  def __init__(self, input):
104    if len(input)==13:
105      if input[:3] in JAN_PREFIX_1ST:
106         super(BookJAN,self).__init__(input)
107         self.ccode = None
108         self.price = None
109      elif input[:3] in JAN_PREFIX_2ND:
110         self.raw = input
111         self.ccode = CCode(self.raw[3:7])
112         self.price = int(self.raw[7:12])
113      else:
114       raise Exception,"[%s] does not valid Book JAN code" % input
115    else:
116     raise Exception,"[%s] does not valid Book JAN code" % input
117 
118def test(encoding='euc-jp'):
119  """テストルーチン"""
120  tests = ('494999908','4798108545',
121    '4-7741-2228-9','ISBN4-7741-2228-9','isbn4774122289',
122    'ISBN450123456','12345678901','9784398470270','9784817180148')
123 
124  print "="*20
125  print " ISBN"
126  print "="*20
127 
128  for c in tests[0:4]:
129    isbn = ISBN(c)
130    print "Raw   ",isbn.raw,"[<-",c,"]"
131    print "Old    ",isbn.oldstyle()
132    print "New ",isbn.newstyle()
133    print ""
134 
135  print "="*20
136  print " Book JAN"
137  print "="*20
138  tests = ('9784398470270','9784817180148')
139  for c in tests[0:4]:
140    jan = BookJAN(c)
141    print "RAW   ",jan.raw
142    print "New   ",jan.newstyle()
143    print ""
144 
145  print "="*20
146  print " Book JAN CCode"
147  print "="*20
148  tests = ('1910193003605','1923055038004')
149  for c in tests[0:4]:
150    jan = BookJAN(c)
151    print "RAW   ",jan.raw
152    if jan.ccode:
153     print "C-CODE   ",jan.ccode
154     print "C-CODE.Target ",jan.ccode.target,' :',jan.ccode.targetv.encode(encoding)
155     print "C-CODE.Style  ",jan.ccode.style,' :',jan.ccode.stylev.encode(encoding)
156     print "C-CODE.NDC    ",jan.ccode.ndc,':',jan.ccode.ndcv.encode(encoding)
157     print "PRICE   ",jan.price
158    print ""
159 
160  idx = 0
161  cc = 0
162  for c in CCode()._ndc:
163    print idx,
164    if c:
165      print c.encode(encoding)
166      cc += 1
167    else:
168      print c
169    idx += 1
170  print cc,'/',len(CCode()._ndc)
171 
172if __name__=='__main__':
173  encoding = "euc-jp"
174  if sys.platform == 'win32': encoding="cp932"
175  test(encoding)

半角全角関係のOracle ストアドファンクション

※2011.12.26 追記 最近のバージョンでは標準で用意されてるようです。コメントで教えて頂きました。

わかりやすさを優先してストアドファンクションにしていますが、パッケージにして、静的な変数を使ったほうが当然パフォーマンスはあがると思います。これは、Oracle9iで動くことは確認しました。

全角のアルファベット、数字を半角にする

01CREATE OR REPLACE FUNCTION alnum2han(str in VARCHAR2) RETURN VARCHAR2
02IS
03tmp varchar2(2000);
04BEGIN
05tmp := translate(
06str,
07'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
08'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
09);
10RETURN tmp;
11END alnum2han;
12/
13select alnum2han('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz') from dual;

全角のアルファベット、数字を半角にして、半角カナを全角にして、他の記号もいい感じにする

01CREATE OR REPLACE FUNCTION hanzen(str in VARCHAR2) RETURN VARCHAR2
02IS
03tmp varchar2(2000);
04BEGIN
05tmp := alnum2han(han2zen(str));
06tmp := translate(
07tmp,
08'|=+?/@!”’#$%&;: ',
09'|=+-/@!"''#$%&;: '
10);
11RETURN tmp;
12END hanzen;
13/
14select hanzen(' ィァァィ<><>;:”()=!%$#’アイウエオカ|=+?/@!”’#$%&();:[]【】〔〕<>¥^キクケコザズゾざずぞザズダド1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz') from dual;