LoginSignup
5
0

More than 5 years have passed since last update.

素人の言語処理100本ノック:23

Last updated at Posted at 2016-10-15

言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。

第3章: 正規表現

Wikipediaの記事を以下のフォーマットで書き出したファイルjawiki-country.json.gzがある.
・1行に1記事の情報がJSON形式で格納される
・各行には記事名が"title"キーに,記事本文が"text"キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
・ファイル全体はgzipで圧縮される
以下の処理を行うプログラムを作成せよ.

23. セクション構造

記事中に含まれるセクション名とそのレベル(例えば"== セクション名 =="なら1)を表示せよ.

出来上がったコード:

main.py
# coding: utf-8
import gzip
import json
import re
fname = 'jawiki-country.json.gz'


def extract_UK():
    '''イギリスに関する記事本文を取得

    戻り値:
    イギリスの記事本文
    '''

    with gzip.open(fname, 'rt') as data_file:
        for line in data_file:
            data_json = json.loads(line)
            if data_json['title'] == 'イギリス':
                return data_json['text']

    raise ValueError('イギリスの記事が見つからない')


# 正規表現のコンパイル
pattern = re.compile(r'''
    ^       # 行頭
    (={2,}) # キャプチャ対象、2個以上の'='
    \s*     # 余分な0個以上の空白('哲学'や'婚姻'の前後に余分な空白があるので除去)
    (.+?)   # キャプチャ対象、任意の文字が1文字以上、非貪欲(以降の条件の巻き込み防止)
    \s*     # 余分な0個以上の空白
    \1      # 後方参照、1番目のキャプチャ対象と同じ内容
    .*      # 任意の文字が0文字以上
    $       # 行末
    ''', re.MULTILINE + re.VERBOSE)

# 抽出
result = pattern.findall(extract_UK())

# 結果表示
for line in result:
    level = len(line[0]) - 1    # '='の数-1
    print('{indent}{sect}({level})'.format(
        indent='\t' * (level - 1), sect=line[1], level=level))

実行結果:

端末
国名(1)
歴史(1)
地理(1)
    気候(2)
政治(1)
外交と軍事(1)
地方行政区分(1)
    主要都市(2)
科学技術(1)
経済(1)
    鉱業(2)
    農業(2)
    貿易(2)
    通貨(2)
    企業(2)
交通(1)
    道路(2)
    鉄道(2)
    海運(2)
    航空(2)
通信(1)
国民(1)
    言語(2)
    宗教(2)
    婚姻(2)
    教育(2)
文化(1)
    食文化(2)
    文学(2)
    哲学(2)
    音楽(2)
        イギリスのポピュラー音楽(3)
    映画(2)
    コメディ(2)
    国花(2)
    世界遺産(2)
    祝祭日(2)
スポーツ(1)
    サッカー(2)
    競馬(2)
    モータースポーツ(2)
脚注(1)
関連項目(1)
外部リンク(1)

セクションとは?

問題26で参考情報として紹介されているマークアップ早見表の「見出し」を指しているようなので、これに従いコーディングしました。

複数のキャプチャ

今回の条件ではキャプチャ対象が2つあるので、resultの各要素は2要素のタプルが返ります。例えば最初のセクションは('==', '国名')が返ります。

後方参照

セクションは同じ数の=で括られる必要があるので、終わりの部分は始めの部分と同じものをマッチさせる必要があります。\numberで前出のグループを指定できるのでそれを使いました。後方参照という仕組みです。なお、前出のグループを指定するための番号は0ベースではなく1ベースなので注意してください。1が最初のグループを指します。

余計な空白の除去

対象データは=== 哲学 ===という風にセクションの前後に空白の入っているものが混ざっていました。そのため、\s*で空白を除去する条件にしています。

 
24本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。


実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第3章で用いているデータのライセンスはクリエイティブ・コモンズ 表示-継承 3.0 非移植日本語訳)です。

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