postgreSQLでひらがな・カタカナのローマ字変換をする関数


postgreSQL(ポスグレ)で、ひらがな・カタカナをローマ字変換する関数が欲しかったので作成しました。

仕様としては、kana_to_roman(‘きょうのカムチャッカのてんきは?’) と入れると、
KYONOKAMUCHAKKANOTENKIHA?
というtextを返してくれるものです。

変換方法はヘボン式のローマ字変換です。
ヘボン式については、こちらのサイト(http://www.seikatubunka.metro.tokyo.jp/hebon/)を参考にしました。

ヘボン式など意識せずに簡単にやるなら、1対1で対応するひらがな・カナを対応する英字に変換すればよい訳ですが、そうは行かず。
以下の特殊ルールが少し面倒でしたが対応しています。

■「ん」は「N」で表記。
ただし、B・M・Pの前では、「ん」は「M」で表記(なんば NAMBA/ほんま HOMMA/まんぽ MAMPO)

■「っ」は子音を重ねる→ (例) べっぷ BEPPU/いっしき ISSHIKI
ただし、CHの前では、「っ」は「T」で表記。(えっちゅう ETCHU/はっちょう HATCHO)

■長音:「O」や「U」は記入しない→ (例) おおの ONO/さいとう SAITO

postgreSQL(ポスグレ)の関数のソースコードは以下です。
PostgreSQL 9.3.4 で動作確認済みです。
↓ バージョン9.1以上用のソースコード

CREATE OR REPLACE FUNCTION kana_to_roman(TEXT)
RETURNS TEXT
AS $$
  DECLARE
    _input_s ALIAS FOR $1;
    s TEXT;
    i INTEGER :=1;
    len INTEGER ;
    hebon TEXT :='' ;
    lastHebon TEXT := '';
    lastChar TEXT := '';
    nextChar TEXT := '';
    c TEXT ;  -- カレント文字
    c2 TEXT ; -- カレント2文字
    h TEXT ; -- hebon
    nc TEXT ;  -- 次の文字
    nc2 TEXT ; -- 次の2文字
    nh TEXT ; --次のヘボン
    testH TEXT :=''; --長音のテスト用ヘボン
    a TEXT;
    cnt INTEGER;
    target TEXT[] := ARRAY[
     'A','I','U','E','O'
    ,'KA','KI','KU','KE','KO'
    ,'SA','SHI','SU','SE','SO'
    ,'TA','CHI','TSU','TE','TO'
    ,'NA','NI','NU','NE','NO'
    ,'HA','HI','FU','HE','HO'
    ,'MA','MI','MU','ME','MO'
    ,'YA','YU','YO'
    ,'RA','RI','RU','RE','RO'
    ,'WA','I','E','O'
    ,'A','I','U','E','O'
    ,'GA','GI','GU','GE','GO'
    ,'ZA','JI','ZU','ZE','ZO'
    ,'DA','JI','ZU','DE','DO'
    ,'BA','BI','BU','BE','BO'
    ,'PA','PI','PU','PE','PO'
    ,'KYA','KYU','KYO'
    ,'SYA','SHU','SYO'
    ,'CHA','CHU','CHO','CHE'
    ,'NYA','NYU','NYO'
    ,'HYA','HYU','HYO'
    ,'MYA','MYU','MYO'
    ,'RYA','RYU','RYO'
    ,'GYA','GYU','GYO'
    ,'JA','JU','JO'
    ,'BYA','BYU','BYO'
    ,'PYA','PYU','PYO'
    ,'V'
    ];
    source TEXT[] := ARRAY[
     'ア','イ','ウ','エ','オ'
    ,'カ','キ','ク','ケ','コ'
    ,'サ','シ','ス','セ','ソ'
    ,'タ','チ','ツ','テ','ト'
    ,'ナ','ニ','ヌ','ネ','ノ'
    ,'ハ','ヒ','フ','ヘ','ホ'
    ,'マ','ミ','ム','メ','モ'
    ,'ヤ','ユ','ヨ'
    ,'ラ','リ','ル','レ','ロ'
    ,'ワ','ヰ','ヱ','ヲ'
    -- ン は別途
    ,'ァ','ィ','ゥ','ェ','ォ'
    ,'ガ','ギ','グ','ゲ','ゴ'
    ,'ザ','ジ','ズ','ゼ','ゾ'
    ,'ダ','ジ','ヅ','デ','ド'
    ,'バ','ビ','ブ','ベ','ボ'
    ,'パ','ピ','プ','ペ','ポ'
    ,'キャ','キュ','キョ'
    ,'シャ','シュ','ショ'
    ,'チャ','チュ','チョ','チェ'
    ,'ニャ','ニュ','ニョ'
    ,'ヒャ','ヒュ','ヒョ'
    ,'ミャ','ミュ','ミョ'
    ,'リャ','リュ','リョ'
    ,'ギャ','ギュ','ギョ'
    ,'ジャ','ジュ','ジョ'
    ,'ビャ','ビュ','ビョ'
    ,'ピャ','ピュ','ピョ'
    ,'ヴ'
    ];
  BEGIN
    cnt := 0;
    s := translate(_input_s
,'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよ
らりるれろ
わをんがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽっゃゅょーゐゑ'
,'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨ
ラリルレロ
ワヲンガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポッャュョーイエ'
);
    len := LENGTH(s);
    i := 1;
    WHILE i<=len LOOP
      h := '';
      c := '';
      c2 := '';
      cnt := 0;
      -- 1文字
      c := SUBSTR(s,i,1);
      -- 2文字
      IF (i<len) THEN
	c2 := SUBSTR(s,i,2);
      ELSE
        c2 := '';
      END IF;
      cnt:=0;
      FOREACH a IN ARRAY source LOOP
        cnt:=cnt+1;
        IF source[cnt] = c2 THEN
          -- 2文字がマップにあれば2文字分の変換後文字をマップからhに格納
          h := target[cnt];
        END IF;
      END LOOP;
      IF (h='') THEN
        c2 := '';
        cnt := 0; 
	FOREACH a IN ARRAY source LOOP
	  cnt:=cnt+1;
	  IF source[cnt] = c THEN
	    -- 2文字がマップになければ1文字分の変換後文字をマップからhに格納
            h := target[cnt];
          END IF;
        END LOOP;
      END IF;

      IF h='' THEN
        h:=c;
      END IF;

      IF c2<>'' THEN 
        c:=c2 ;
      END IF;

      IF i<len THEN
        nc := SUBSTR(s,i+1,1);
      ELSE 
        nc := '';
      END IF;
      
      IF c = 'ッ' THEN
	nh := '';
	nc2 := '';
	IF(i+2<=len) THEN
	  nc2 := SUBSTR(s,i+1,2);
          cnt:=0;
          FOREACH a IN ARRAY source LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF (i+1<=len) AND nh='' THEN
	  nc2 := SUBSTR(s,i+1,1);
          cnt:=0;
          FOREACH a IN ARRAY source LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF nh != '' THEN
	  IF POSITION('CH' in nh)=1 THEN
	    h:='T';
	  ELSE
	    h:=SUBSTRING(nh,1,1);
	  END IF;
	END IF;
	
      ELSEIF c = 'ン' THEN
	nh := '';
	nc2 := '';
        h:='n';
	IF(i+2<=len) THEN
	  nc2 := SUBSTR(s,i+1,2);
          cnt:=0;
          FOREACH a IN ARRAY source LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF (i+1<=len) AND nh='' THEN
	  nc2 := SUBSTR(s,i+1,1);
          cnt:=0;
          FOREACH a IN ARRAY source LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF nh != '' AND ( POSITION('B' in nh)=1 OR POSITION('M' in nh)=1 OR POSITION(
'P' in nh) =1 ) THEN
	  h:='M';
	ELSE
	  h:='N';
	END IF;

      ELSEIF c = 'ー' THEN
        h:='';
      END IF;

      IF h != '' THEN
        IF lastHebon!='' THEN
          testH := lastHebon || h;
          IF LENGTH(testH) >= 2 THEN
            testH := SUBSTRING(testH,LENGTH(testH)-1,2);
            IF (testH='AA' OR testH='II' OR testH='UU' OR testH='EE' OR testH=
'OO' OR testH='OU') THEN
              h:='';
            END IF;
          END IF;
        END IF;
      END IF;

      hebon:=hebon || h;

      lastChar := c;
      lastHebon := h;
      i := i+LENGTH(c);
      
    END LOOP;
    RETURN hebon;
  END;
$$
LANGUAGE 'plpgsql';

上記ソースは、plpgsql の FOREACH 文を使っていますが、FOREACH は バージョン9.1以上から使える制御文です。
配列走査には、このFOREACHを使った方が速いそうです。
バージョン9.0以下でも動作するソースも、以下に貼り付けておきます。
PostgreSQL 8.2.9 で動作確認済みです。
↓FOREACHが使えないバージョン9.0以下用のソースコード

CREATE OR REPLACE FUNCTION kana_to_roman(TEXT)
RETURNS TEXT
AS $$
  DECLARE
    _input_s ALIAS FOR $1;
    s TEXT;
    i INTEGER :=1;
    len INTEGER ;
    hebon TEXT :='' ;
    lastHebon TEXT := '';
    lastChar TEXT := '';
    nextChar TEXT := '';
    c TEXT ;  -- カレント文字
    c2 TEXT ; -- カレント2文字
    h TEXT ; -- hebon
    nc TEXT ;  -- 次の文字
    nc2 TEXT ; -- 次の2文字
    nh TEXT ; --次のヘボン
    testH TEXT :=''; --長音のテスト用ヘボン
    a TEXT;
    cnt INTEGER;
    target TEXT[] := ARRAY[
     'A','I','U','E','O'
    ,'KA','KI','KU','KE','KO'
    ,'SA','SHI','SU','SE','SO'
    ,'TA','CHI','TSU','TE','TO'
    ,'NA','NI','NU','NE','NO'
    ,'HA','HI','FU','HE','HO'
    ,'MA','MI','MU','ME','MO'
    ,'YA','YU','YO'
    ,'RA','RI','RU','RE','RO'
    ,'WA','I','E','O'
    ,'A','I','U','E','O'
    ,'GA','GI','GU','GE','GO'
    ,'ZA','JI','ZU','ZE','ZO'
    ,'DA','JI','ZU','DE','DO'
    ,'BA','BI','BU','BE','BO'
    ,'PA','PI','PU','PE','PO'
    ,'KYA','KYU','KYO'
    ,'SYA','SHU','SYO'
    ,'CHA','CHU','CHO','CHE'
    ,'NYA','NYU','NYO'
    ,'HYA','HYU','HYO'
    ,'MYA','MYU','MYO'
    ,'RYA','RYU','RYO'
    ,'GYA','GYU','GYO'
    ,'JA','JU','JO'
    ,'BYA','BYU','BYO'
    ,'PYA','PYU','PYO'
    ,'V'
    ];
    source TEXT[] := ARRAY[
     'ア','イ','ウ','エ','オ'
    ,'カ','キ','ク','ケ','コ'
    ,'サ','シ','ス','セ','ソ'
    ,'タ','チ','ツ','テ','ト'
    ,'ナ','ニ','ヌ','ネ','ノ'
    ,'ハ','ヒ','フ','ヘ','ホ'
    ,'マ','ミ','ム','メ','モ'
    ,'ヤ','ユ','ヨ'
    ,'ラ','リ','ル','レ','ロ'
    ,'ワ','ヰ','ヱ','ヲ'
    -- ン は別途
    ,'ァ','ィ','ゥ','ェ','ォ'
    ,'ガ','ギ','グ','ゲ','ゴ'
    ,'ザ','ジ','ズ','ゼ','ゾ'
    ,'ダ','ジ','ヅ','デ','ド'
    ,'バ','ビ','ブ','ベ','ボ'
    ,'パ','ピ','プ','ペ','ポ'
    ,'キャ','キュ','キョ'
    ,'シャ','シュ','ショ'
    ,'チャ','チュ','チョ','チェ'
    ,'ニャ','ニュ','ニョ'
    ,'ヒャ','ヒュ','ヒョ'
    ,'ミャ','ミュ','ミョ'
    ,'リャ','リュ','リョ'
    ,'ギャ','ギュ','ギョ'
    ,'ジャ','ジュ','ジョ'
    ,'ビャ','ビュ','ビョ'
    ,'ピャ','ピュ','ピョ'
    ,'ヴ'
    ];
  BEGIN
    cnt := 0;
    s := translate(_input_s
,'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよ
らりるれろ
わをんがぎぐげござじずぜぞだぢづでどばびぶべぼぱぴぷぺぽっゃゅょーゐゑ'
,'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨ
ラリルレロ
ワヲンガギグゲゴザジズゼゾダヂヅデドバビブベボパピプペポッャュョーイエ'
);
    len := LENGTH(s);
    i := 1;
    WHILE i<=len LOOP
      h := '';
      c := '';
      c2 := '';
      cnt := 0;
      -- 1文字
      c := SUBSTR(s,i,1);
      -- 2文字
      IF (i<len) THEN
	c2 := SUBSTR(s,i,2);
      ELSE
        c2 := '';
      END IF;
      cnt:=0;
--      FOREACH a IN ARRAY source LOOP
      FOR j IN ARRAY_LOWER(source,1)..ARRAY_UPPER(source,1) LOOP
        cnt:=cnt+1;
        IF source[cnt] = c2 THEN
          -- 2文字がマップにあれば2文字分の変換後文字をマップからhに格納
          h := target[cnt];
        END IF;
      END LOOP;
      IF (h='') THEN
        c2 := '';
        cnt := 0; 
--	FOREACH a IN ARRAY source LOOP
        FOR j IN ARRAY_LOWER(source,1)..ARRAY_UPPER(source,1) LOOP
	  cnt:=cnt+1;
	  IF source[cnt] = c THEN
	    -- 2文字がマップになければ1文字分の変換後文字をマップからhに格納
            h := target[cnt];
          END IF;
        END LOOP;
      END IF;

      IF h='' THEN
        h:=c;
      END IF;

      IF c2<>'' THEN 
        c:=c2 ;
      END IF;

      IF i<len THEN
        nc := SUBSTR(s,i+1,1);
      ELSE 
        nc := '';
      END IF;
      
      IF c = 'ッ' THEN
	nh := '';
	nc2 := '';
	IF(i+2<=len) THEN
	  nc2 := SUBSTR(s,i+1,2);
          cnt:=0;
--          FOREACH a IN ARRAY source LOOP
	    FOR j IN ARRAY_LOWER(source,1)..ARRAY_UPPER(source,1) LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF (i+1<=len) AND nh='' THEN
	  nc2 := SUBSTR(s,i+1,1);
          cnt:=0;
--          FOREACH a IN ARRAY source LOOP
	    FOR j IN ARRAY_LOWER(source,1)..ARRAY_UPPER(source,1) LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF nh != '' THEN
	  IF POSITION('CH' in nh)=1 THEN
	    h:='T';
	  ELSE
	    h:=SUBSTRING(nh,1,1);
	  END IF;
	END IF;
	
      ELSEIF c = 'ン' THEN
	nh := '';
	nc2 := '';
        h:='n';
	IF(i+2<=len) THEN
	  nc2 := SUBSTR(s,i+1,2);
          cnt:=0;
--          FOREACH a IN ARRAY source LOOP
	  FOR j IN ARRAY_LOWER(source,1)..ARRAY_UPPER(source,1) LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF (i+1<=len) AND nh='' THEN
	  nc2 := SUBSTR(s,i+1,1);
          cnt:=0;
--          FOREACH a IN ARRAY source LOOP
	  FOR j IN ARRAY_LOWER(source,1)..ARRAY_UPPER(source,1) LOOP
            cnt:=cnt+1;
            IF source[cnt] = nc2 THEN
              -- 2文字がマップにあれば2文字分の変換後文字をマップからnhに格納
              nh := target[cnt];
            END IF;
          END LOOP;
	END IF;
	IF nh != '' AND ( POSITION('B' in nh)=1 OR POSITION('M' in nh)=1 OR POSITION(
'P' in nh) =1 ) THEN
	  h:='M';
	ELSE
	  h:='N';
	END IF;

      ELSEIF c = 'ー' THEN
        h:='';
      END IF;

      IF h != '' THEN
        IF lastHebon!='' THEN
          testH := lastHebon || h;
          IF LENGTH(testH) >= 2 THEN
            testH := SUBSTRING(testH,LENGTH(testH)-1,2);
            IF (testH='AA' OR testH='II' OR testH='UU' OR testH='EE' OR testH=
'OO' OR testH='OU') THEN
              h:='';
            END IF;
          END IF;
        END IF;
      END IF;

      hebon:=hebon || h;

      lastChar := c;
      lastHebon := h;
      i := i+LENGTH(c);
      
    END LOOP;
    RETURN hebon;
  END;
$$
LANGUAGE 'plpgsql';
タグ:
カテゴリー: データベース

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

カテゴリー