LoginSignup
5
5

More than 5 years have passed since last update.

メロスを形態素解析せよ

Posted at

問題:https://codeiq.jp/ace/masui_yuichiro/q506

私の答案

#!/usr/bin/ruby
#coding:utf-8

require 'stringio'

# MultiSAX: Ruby Gem to handle multiple SAX libraries
# https://github.com/cielavenir/multisax
module MultiSAX # minimal version
    class SAX
        @parser=nil
        def open(*list)
            list=[:libxml,:rexmlstream] if list.size==0
            list.each{|e_module|
                case e_module
                    when :libxml
                        begin
                            require 'rubygems'
                            require 'libxml'
                        rescue LoadError;next end
                        @parser=e_module;break
                    when :rexmlstream
                        require 'rexml/document'
                        require 'rexml/parsers/streamparser'
                        require 'rexml/streamlistener'
                        @parser=e_module;break
                end
            }
            @parser
        end
        def reset() @parser=nil;@saxmodule=nil end

        def parse(source,listener)
            open if !@parser
            case @parser
                when :libxml
                    listener.instance_eval{
                        extend LibXML::XML::SaxParser::Callbacks
                        alias :on_start_element :sax_tag_start
                        alias :on_end_element :sax_tag_end
                        alias :on_cdata_block :sax_cdata
                        alias :on_characters :sax_text
                        alias :on_comment :sax_comment
                    }
                when :rexmlstream
                    listener.instance_eval{
                        extend REXML::StreamListener
                        alias :tag_start :sax_tag_start
                        alias :tag_end :sax_tag_end
                        alias :cdata :sax_cdata
                        alias :text :sax_text
                        alias :comment :sax_comment
                    }
            end

            if source.is_a?(String)
                case @parser
                    when :libxml       then parser=LibXML::XML::SaxParser.string(source);parser.callbacks=listener;parser.parse
                    when :rexmlstream  then REXML::Parsers::StreamParser.new(source,listener).parse
                end
            else
                case @parser
                    when :libxml       then parser=LibXML::XML::SaxParser.io(source);parser.callbacks=listener;parser.parse
                    when :rexmlstream  then REXML::Parsers::StreamParser.new(source,listener).parse
                end
            end
            listener
        end

    end
    Sax=SAX.new

    module Callbacks
        def sax_tag_start(tag,attrs) end
        def sax_tag_end(tag) end
        def sax_comment(text) end
        def sax_cdata(text) end
        def sax_text(text) end
    end
end

listener=MultiSAX::Sax.parse($<,Class.new{
    include MultiSAX::Callbacks
    def initialize
        @in_main_text=false
        @main_text_div_depth=0
        @content=''
        @current_tag=[]
    end
    attr_reader :content

    def sax_tag_start(tag,attrs)
        @current_tag.push(tag)
        h_attrs=Hash[*[attrs]]
        @in_main_text=true if tag=='div'&&h_attrs['class']=='main_text' #class="main_text"ならば本文
        @main_text_div_depth+=1 if @in_main_text&&tag=='div'
    end
    def sax_tag_end(tag)
        if (t=@current_tag.pop)!=tag then raise "xml is malformed /#{t}" end #タグの入れ子が誤っていたらエラーとする
        if @in_main_text&&tag=='div'
            @in_main_text=false if (@main_text_div_depth-=1)==0
        end
    end
    def sax_text(txt)
        @content+=txt.gsub(/[\s\t ]/,'') if @in_main_text&&@main_text_div_depth==1&&!['rp','rt'].include?(@current_tag.last) #rp,rtの中なら捨てる
    end
}.new)

mecab_input=listener.content.encode('EUC-JP')
mecab_output=''

IO.popen('mecab -b 100000','r+b',external_encoding:'EUC-JP'){|io|
    io.puts mecab_input
    io.close_write
    mecab_output=io.read.encode('UTF-8')
}
#puts mecab_output
#exit
io=StringIO.new(mecab_output)
nouns=[]
verbs=[]
io.each{|e|
    e.chomp!
    break if e=='EOS'
    idx=e.index("\t")
    word=e[0...idx]
    meta=e.chomp[idx+1..-1].split(',')
    nouns<<word if meta[0]=='名詞' && meta[1]=='一般'
    verbs<<word if meta[0]=='動詞' && meta[1]=='自立'
}
nouns=nouns.sample(2)
puts "#{nouns[0]}は最近、#{nouns[1]}#{verbs.sample}している。"

前半のMultiSAX部分は、require 'rubygems';require 'multisax'で代用可。
後は普通にテキスト化し、EUC-JPに変換してmecabに投げるだけ。

私はこの問題について正解扱いにならなかったので、不明瞭な点について整理のみしておく。

  • テキスト化の方法

rp、rtタグはフリガナに関わるHTMLタグであるので、今回は取り除いて考えた。
しかし、取り除くべきではなかったらしい。取り除くと例えば動詞の語幹が変わったりする。

  • mecabの実行方法

今回初めてmecabを扱ってわかったことだが、バッファサイズを予め指定しないと正しい解析ができないらしい。
そこで-b 100000を指定して実行している。
ところが、解説記事の出力を見ると、EOSが何度も出現しているし、途中いくつかの文が抜けてもいる。

正しさについては議論の余地がありますが、まあ、問題作るって難しいですよね。

5
5
0

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
5
5