読者です 読者をやめる 読者になる 読者になる

WHITELEAF:Kindle応援サイト

KindleでWEB小説を読もう! Narou.rb 公開中

オーバーロードWEB版のルビを青空文庫形式に

オーバーロードWEB版のテキスト中のルビを、青空文庫形式の《》に変換します。魔法名は区切りが分かりやすいのですが、他の固有名詞は区切りが明確ではないため、MeCabによる形態素解析を用いてある程度単語の区切りを推測し、最終的に人間が区切りを判断するハイブリッド型になっています。

文章中ルビとしてあらわれるのは <> <> 〈〉 ですが、ごくわずかに()が使われているので、手動での置換が必要です(前編で2箇所程度)

使用言語Ruby(1.9以上必須)

# -*- Encoding: Windows-31J -*-
#
# Copyright 2012 whiteleaf. All rights reserved.
#

# オーバーロードの 固有名詞<読み方> を |固有名詞《読み方》に変換する

require_relative "mecab"

=begin
訂正画面イメージ
┌─────────────────────────────────┐
│...シャルティアの|妾である吸血鬼の花嫁《ヴァンパイア・ブライド》 │
│                 ^                                               │
│ルビ開始位置の確認 (Yes/Skip/Forward/Backward):ffff               │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│...シャルティアの妾である|吸血鬼の花嫁《ヴァンパイア・ブライド》 │
│                         ^                                       │
│ルビ開始位置の確認 (Yes/Skip/Forward/Backward):y                  │
└─────────────────────────────────┘
=end

def make_text(prev_text, name, ruby)
  "#{prev_text}#{name}#{ruby}"
end

def output_input_message
  print "ルビ開始位置の確認 (Yes/Skip/Forward*/Backward*): "
end

def output_interface(prev_text, name, ruby)
  is_omit = prev_text.length > 7
  omit_text = (is_omit ? "‥‥" : "") + prev_text[(is_omit ? -7 : 0)..-1]
  puts make_text(omit_text, name, ruby)
  puts " " * omit_text.length + ""
  output_input_message
end

def wait_user_input(prev_text, name, ruby)
  output_interface(prev_text, name, ruby)
  while input = STDIN.gets
    case input[0].downcase
    when "y"
      return make_text(prev_text, name, ruby)
    when "s"
      return nil
    when "f"
      count = input.match(/(f+)/i)[1].length
      prev_text += name[0...count]
      _name = name[count..-1]
      name = (_name ? _name : "")
      output_interface(prev_text, name, ruby)
    when "b"
      count = input.match(/(b+)/i)[1].length
      _prev_text = prev_text[-count..-1]
      name = (_prev_text ? _prev_text : prev_text) + name
      prev_text = (_prev_text ? prev_text[0...-count] : "")
      output_interface(prev_text, name, ruby)
    else
      output_input_message
    end
  end
end

Mecab = MecabLib::Mecab.new("")

def extract_ruby(line)
  line.gsub(/(.+?)[<<〈](.+?)[>>〉]/) do
    match_name_message = $1
    match_ruby = $2
    match_all = $&
    node = Mecab.sparse_tonode(match_name_message.force_encoding(Encoding::Shift_JIS))
    nodes = []
    while node.hasNext
      node = node.next
      break if node.surface == "EOS"
      nodes << {
        surface: node.surface.force_encoding(Encoding::Windows_31J),
        pos: node.pos.force_encoding(Encoding::Windows_31J),
        root: node.root.force_encoding(Encoding::Windows_31J),
        reading: node.reading.force_encoding(Encoding::Windows_31J),
        pronunciation: node.pronunciation.force_encoding(Encoding::Windows_31J)
      }
    end
    name = ""
    prev_text = ""
    is_before_word_alphabet = false
    detected = false
    _debug_before_sujou = ""
    nodes.reverse.each do |node|
      unless detected
        sujou = node[:pos].split(",")
        if ["接頭詞", "名詞", "助詞", "助動詞", "形容詞"].include?(sujou[0])
          if (sujou[0] == "助詞" && sujou[1] != "連体化") ||
             (sujou[0] == "助動詞" && sujou[5] != "体言接続")
            detected = true
          else
            if node[:surface] =~ /^[a-zA-Z]+$/
              if is_before_word_alphabet
                name = " " + name
              end
              is_before_word_alphabet = true
            else
              is_before_word_alphabet = false
            end
            name = node[:surface] + name
            next
          end
        else
          detected = true
        end
      end
      prev_text = node[:surface] + prev_text
    end
    result = wait_user_input(prev_text, name, match_ruby)
    result ? result : match_all
  end
end

if ARGV.count == 0
  puts "ファイル名を指定して下さい"
  exit
end

ARGV.each do |fname|
  puts "#{fname} の処理を開始します"
  puts "-" * 70
  open(fname) do |read_fp|
    open("[OLルビ変換]#{fname}", "w") do |write_fp|
      read_fp.each do |line|
        result = extract_ruby(line)
        write_fp.puts(result)
      end
    end
  end
  puts "-" * 30
  puts "#{fname} の変換が完了しました。"
end

Mecab.destroy