1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

小道具:合成用の濁点(U+3099)と半濁点(U+309A)を排除する

Last updated at Posted at 2022-12-14

macOS の標準アプリで日本語テキストを弄っていると何故か合成用の濁点(U+3099)や半濁点(U+309A)が入り込んできます。合成用の濁点や半濁点で文字化けする相手向けに変換が必要になったので作ってみました。

HTML+JavaScript版

濁点(U+3099)と半濁点(U+309A)は解析部分(出力と同じ文字列)で色を付けています。

See the Pen 合成用(半)濁点文字[U+3099,U+309A]の変換 by Ikiuo (@ikiuo) on CodePen.

動作確認は Google Chrome バージョン: 108.0.5359.98 (macOS版)で行っています。

HTML スクリーンショット

スクリーンショット.png

dakuten.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>合成用の濁点と半濁点を排除する</title>
  </head>
  <body>
    <h1>合成用の濁点と半濁点を排除する</h1>
    <table border="1">
      <tr>
        <th>入力</th>
        <td>
          <textarea id="inp" rows="12" cols="80" oninput="onInput()"></textarea>
        </td>
      </tr>
      <tr><th>出力</th><td id="out"></td></tr>
      <tr><th>解析</th><td id="chk"></td></tr>
    </table>
    <script>

      const dakuten = {
          '':'', '':'',

          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',

          '':'',
          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',

          '':'', '':'',
          '':'',
          '':'', '':'', '':'', '':'',
      }

      const handakuten = {
          '':'', '':'', '':'', '':'', '':'',
          '':'', '':'', '':'', '':'', '':'',
      }

      function onInput()
      {
          const tagInp = document.getElementById('inp');
          const tagOut = document.getElementById('out');

          let inp = tagInp.value;
          let out = '';
          let chk = [];
          for (let i = 0; i < inp.length; i++) {
              const code = inp.charCodeAt(i);

              if (code == 0x3099) {
                  let addchr = ''
                  if (i) {
                      const pre = dakuten[inp[i - 1]];
                      if (pre) {
                          out = out.slice(0, out.length - 1);
                          chk.pop();
                          addchr = pre;
                      }
                  }
                  chk.push(1);
                  out += addchr
                  continue;
              }
              if (code == 0x309a) {
                  let addchr = '';
                  if (i) {
                      const pre = handakuten[inp[i - 1]];
                      if (pre) {
                          out = out.slice(0, out.length - 1);
                          chk.pop();
                          addchr = pre;
                      }
                  }
                  chk.push(2);
                  out += addchr;
                  continue;
              }

              out += inp[i];
              chk.push(0);
          }
          tagOut.innerText = out;

          const tagChk = document.getElementById('chk');

          while (tagChk.firstChild)
              tagChk.removeChild(tagChk.firstChild);

          const appendSpan = function(msg, flg) {
              const span = document.createElement('span');
              if (flg) {
                  span.style.setProperty('color', '#000');
                  span.style.setProperty('background-color', '#f88');
              }
              span.innerText = msg;
              tagChk.append(span);
          }

          let last = 0;
          for (let i = 1; i < out.length; i++) {
              if (chk[i - 1] == chk[i])
                  continue;
              appendSpan(out.slice(last, i), chk[i - 1]);
              last = i;
          }
          if (last < out.length)
              appendSpan(out.slice(last, out.length), chk[out.length - 1]);
      }

    </script>
  </body>
</html>

Python 版

dakuten.py
#!/usr/bin/env python3

import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('-N', '--CHR', action='store_true', default=False, help='濁点と半濁点の合成用文字を通常文字にする')
parser.add_argument('-J', '--JIS', action='store_true', default=False, help='JIS X 0208 を濁点・半濁点変換の対象にする')
parser.add_argument('INPUT', help='入力ファイル (\'-\' で標準入力)')
parser.add_argument('OUTPUT', help='出力ファイル (\'-\' で標準出力)')

args = parser.parse_args()

onlychar = args.CHR
inpfile = args.INPUT
outfile = args.OUTPUT

ifp = sys.stdin if inpfile == '-' else open(inpfile, 'r')
ofp = sys.stdout if outfile == '-' else open(outfile, 'w')


# JIS X 0208
dakuten_jisx0208 = {
    '': '', '': '',

    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',

    '': '',
    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',
}

# CJK Miscellaneous
dakuten_unicode = {
    '': '', '': '',
    '': '',
    '': '', '': '', '': '', '': '',
}

dakuten = dakuten_jisx0208
if not args.JIS:
    for k in dakuten_unicode:
        dakuten[k] = dakuten_unicode[k]

handakuten = {
    '': '', '': '', '': '', '': '', '': '',
    '': '', '': '', '': '', '': '', '': '',
}

for line in ifp.readlines():
    new_line = ''
    for ch in line:
        code = ord(ch)
        if code == 0x3099:
            if not onlychar and new_line and new_line[-1] in dakuten:
                new_line = new_line[:-1] + dakuten[new_line[-1]]
                continue
            new_line += ''
            continue
        if code == 0x309a:
            if not onlychar and new_line and new_line[-1] in handakuten:
                new_line = new_line[:-1] + handakuten[new_line[-1]]
                continue
            new_line += ''
            continue

        new_line += ch
    ofp.write(new_line)

合成用濁点と半濁点を使ったサンプル

sample.txt
ヾ ゞ

が ぎ ぐ げ ご
ざ じ ず ぜ ぞ
だ ぢ づ で ど
ば び ぶ べ ぼ

ヴ
ガ ギ グ ゲ ゲ
ザ ジ ズ ゼ ゾ
ダ ヂ ヅ デ ド
バ ビ ブ ベ ボ

〱゙ 〳゙
ゔ
ヷ ヸ ヹ ヺ

ぱ ぴ ぷ ぺ ぽ
パ ピ プ ペ ポ

テスト

実行結果
$ python3 dakuten.py sample.txt -
ヾ ゞ

が ぎ ぐ げ ご
ざ じ ず ぜ ぞ
だ ぢ づ で ど
ば び ぶ べ ぼ

ヴ
ガ ギ グ ゲ ゲ
ザ ジ ズ ゼ ゾ
ダ ヂ ヅ デ ド
バ ビ ブ ベ ボ

〲 〴
ゔ
ヷ ヸ ヹ ヺ

ぱ ぴ ぷ ぺ ぽ
パ ピ プ ペ ポ
合成文字の通常文字化
$ python3 dakuten.py sample.txt - -N
ヽ゛ ゝ゛

か゛ き゛ く゛ け゛ こ゛
さ゛ し゛ す゛ せ゛ そ゛
た゛ ち゛ つ゛ て゛ と゛
は゛ ひ゛ ふ゛ へ゛ ほ゛

ウ゛
カ゛ キ゛ ク゛ ケ゛ ケ゛
サ゛ シ゛ ス゛ セ゛ ソ゛
タ゛ チ゛ ツ゛ テ゛ ト゛
ハ゛ ヒ゛ フ゛ ヘ゛ ホ゛

〱゛ 〳゛
う゛
ワ゛ ヰ゛ ヱ゛ ヲ゛

は゜ ひ゜ ふ゜ へ゜ ほ゜
ハ゜ ヒ゜ フ゜ ヘ゜ ホ゜
JIS X 0208 のみ
$ python3 dakuten.py sample.txt - -J
ヾ ゞ

が ぎ ぐ げ ご
ざ じ ず ぜ ぞ
だ ぢ づ で ど
ば び ぶ べ ぼ

ヴ
ガ ギ グ ゲ ゲ
ザ ジ ズ ゼ ゾ
ダ ヂ ヅ デ ド
バ ビ ブ ベ ボ

〱゛ 〳゛
う゛
ワ゛ ヰ゛ ヱ゛ ヲ゛

ぱ ぴ ぷ ぺ ぽ
パ ピ プ ペ ポ

変換する文字が足りていないかもしれません…

1
1
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?