京都大学コーパスのパーズ

コードを見る限り cabocha -f1 としたときの出力の解析みたいだけど、あれは CaboCha 特有のフォーマットじゃなくて京都大学コーパス由来のフォーマットだと思います!
で、ちょっと真面目に解析するように書き直してみた。

  • やっていることは analyze じゃなくて parse だと思うので関数名を変更
  • dictionary じゃなくてちゃんとオブジェクトに入れる
  • 文字列では対象が大きいときに何かと不便なので、適当に iterable なオブジェクトを受け取るように。
    • file object でも list でも tuple でも generator でも何でも可。
#!/usr/bin/env python

import sys

class Token(object):
    __slots__ = 'surface read base pos ctype cform ne'.split(' ')

    def __init__(self, **kwd):
        for name, value in kwd.iteritems():
            if name in self.__slots__:
                setattr(self, name, value)
            else:
                raise KeyError()

    def __str__(self):
        return '\t'.join(getattr(self, name) for name in self.__slots__)

class Chunk(object):
    __slots__ = 'id link rel score head func tokens'.split(' ')

    def __init__(self, **kwd):
        self.tokens = []
        for name, value in kwd.iteritems():
            if name in self.__slots__:
                setattr(self, name, value)
            else:
                raise KeyError()

    def __str__(self):
        s = '* %d %d%s %s/%s %f\n' % \
            (self.id, self.link, self.rel,
             self.head, self.func, self.score)
        for token in self.tokens:
            s += str(token)
            s += '\n'
        return s
    
class Tree(object):
    __slots__ = ['chunks']

    def __init__(self, chunks):
        self.chunks = chunks

    def __str__(self):
        s = ''
        for chunk in self.chunks:
            s += str(chunk)
        s += 'EOS\n'
        return s
            
def parse(source):
    chunks = []
    current_chunk = None

    for line in source:
        line = line.rstrip('\r\n')

        if len(line) == 0 or line.startswith('#'): continue
       
        if line == 'EOS':
            # end of sentence
            yield Tree(chunks)
            chunks = []
        elif line.startswith('* '):
            # start of chunk
            (cid, link_rel, head_func, score) = line[2:].split(' ')
            cid = int(cid)
            link = int(link_rel[:-1])
            rel = link_rel[-1]
            head_func = head_func.split('/')
            head = int(head_func[0])
            func = int(head_func[1])
            score = float(score)

            current_chunk = Chunk(id = cid, link = link, rel = rel,
                                  head = head, func = func, score = score)
            chunks.append(current_chunk)
        else:
            # token
            (surface, read, base, pos, ctype, cform, ne) = line.split('\t')
            token = Token(surface = surface, read = read, base = base,
                          pos = pos, ctype = ctype, cform = cform, ne = ne)
            current_chunk.tokens.append(token)

if __name__ == '__main__':
    import fileinput
    import locale
    import optparse

    default_encoding = locale.getpreferredencoding()

    parser = optparse.OptionParser()
    parser.add_option('-i', '--input', dest='input_encoding', metavar='INPUT_ENCODING', default=default_encoding)
    parser.add_option('-o', '--output', dest='output_encoding', metavar='OUTPUT_ENCODING', default=default_encoding)
    (options, args) = parser.parse_args()
        
    source = (line.decode(options.input_encoding) for line in fileinput.input(args))
    for tree in parse(source):
        for chunk in tree.chunks:
            for token in chunk.tokens:
                print token.surface.encode(options.output_encoding)