LoginSignup
5
6

言語処理100本ノックで MATLAB 入門!第3章: 正規表現 20-29

Last updated at Posted at 2020-05-18

はじめに

もしかして 「え?MATLAB で言語処理やるの??」と思いました・・?(3回目)

に引き続き、言語処理 100 本ノック 2020 で MATLAB の練習をするシリーズ第三弾。今回は正規表現です。使う関数は主に regexp 関数と regexprep 関数の2つ。正規表現については「正規表現」(MATLAB 公式ページ)も参考にどうぞ。

同じ課題を実現する方法は沢山ある思います。正直なところ正規表現あまり自信ありません。もしもっといい(効率がいい、面白い)方法があれば是非コメントください。お願いします。

他章へのリンク

実行環境

MATLAB R2020a (Windows 10)
この記事は Livescript から生成した markdown を使用しているため、出力結果が途中で打ち切られているところがありますがご了承ください。

Livescript 版(MATLAB)は GitHub: NLP100-MATLAB1 に置いてあります。そしてノックを一緒にやってくれる MATLAB 芸人は引き続き募集中です!詳細は GitHub の方で。

第3章: 正規表現

Wikipediaの記事を以下のフォーマットで書き出したファイル jawiki-country.json.gz がある.

  • 1行に1記事の情報がJSON形式で格納される
  • 各行には記事名が”title”キーに,記事本文が”text”キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
  • ファイル全体はgzipで圧縮される

以下の処理を行うプログラムを作成せよ.

20. JSONデータの読み込み

Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ.

データファイル準備

gunzip 関数に URL を渡すと jawiki-country.json が作られます。既に gunzip 実行済みでファイルが存在する場合と処理を分けています。

Code
if exist('jawiki-country.json','file')
    filename = 'jawiki-country.json';
else
    filename = gunzip('https://nlp100.github.io/data/jawiki-country.json.gz');
end

JSON処理

json 形式の処理は jsondecode 関数、情報を構造体として読み取ります。

試しに1行だけ読み込んでみると、

Code
fid = fopen(filename);
raw = fgets(fid);
str = jsondecode(raw)
Output
str = 
    title: 'エジプト'
     text: '{{otheruses|主に現代のエジプト・アラブ共和国|古代|古代エジプト}}↵{{基礎情報 国↵|略名 =エジプト↵|漢字書き=埃及↵|日本語国名 =エジプト・アラブ共和国↵|公式国名 ={{lang|ar|'''جمهورية مصر العربية'''}}↵|国旗画像 =Flag of Egypt.svg↵|国章画像 =[[ファイル:Coat_of_arms_of_Egypt.svg|100px|エジプトの国章]]↵|国章リンク =([[エジプトの国章|国章]])↵|標語 =なし↵|位置画像 =Egypt (orthographic projection).svg↵|公用語 =[[アラビア語]]↵|首都 =[[File:Flag of Cairo.svg|24px]] [[カイロ]]↵|最大都市 =カイロ↵|元首等肩書 =[[近代エジプトの国家元首の一覧|大統領]]↵|元首等氏名 =[[アブドルファッターフ・アッ=シーシー]]↵|首相等肩書 ={{ill2|エジプトの首相|en|Prime Minister of Egypt|label=首相}}↵|首相等氏名 ={{仮リンク|ムスタファ・マドブーリー|ar|مصطفى مدبولي|en|Moustafa Madbouly}}↵|面積順位 =29↵|面積大きさ =1 E12↵|面積値 =1,010,408↵|水面積率 =0.6%↵|人口統計年 =2012↵|人口順位 =↵|人口大きさ =1 E7↵|人口値 =1億人↵|人口密度値 =76↵|GDP統計年元 =2018↵|GDP値元 =4兆4,374億<ref name="economy">IMF Data and Statistics 2020年2月3日閲覧(中略)

Code
fclose(fid);

こんな感じです。titletext が char 型で取れています。

fgets を繰り返し実行して一行づつ処理してもいいですが、ここでは

  • fileread 関数で一気に読み込んで、
  • string 型にして、splitlines 関数で行ごとに分割
  • 毎行 jsondecode で処理して table

でやってみます。

Code
text = string(fileread(filename));
jsontexts = splitlines(text); % 分割
jsontexts(strlength(jsontexts) == 0) = []; % 空の行を削除

% table 型変数を事前定義
T = table('Size',[length(jsontexts),2], ...
    'VariableTypes',["string","string"], ...
    'VariableNames',["title","text"]);

% 各行処理
for ii=1:length(jsontexts)
    % 構造体を table 型に変換
    tmp = struct2table(jsondecode(jsontexts(ii)));
    
    % string 型にしておくとセルを使わず結合できるので便利。
    tmp.title = string(tmp.title); % string 型に変更
    tmp.text = string(tmp.text); % string 型に変更
    
    T(ii,:) = tmp; % 連結
end

メモ:texttitle は char 型で返ってくるので明示的に string 型に変更していますが、T(ii,:) = tmp; だけでもデータ型は自動で変更されます。T の変数を string 型だと事前定義しているため。

Code
head(T)
title text
1 "エジプト" "{{otheruses|主に現代の...
2 "オーストリア" "{{基礎情報 国
|略名 = オー...
3 "インドネシア" "{{基礎情報 国
| 略名 =イン...
4 "イラク" "{{複数の問題
| 参照方法 = ...
5 "イラン" "{{半保護}}
{{未検証...
6 "チュニジア" "{{基礎情報 国
| 略名 = チ...
7 "トルコ" "{{Otheruses}}
...
8 "ロシア" "{{Otheruses|'''[[...

こんな感じ。

イギリス抽出

イギリスだけ取り出せばよいので、

Code
UKWiki = T(T.title == "イギリス",:)
title text
1 "イギリス" "{{redirect|UK}}...

21. カテゴリ名を含む行を抽出

記事中でカテゴリ名を宣言している行を抽出せよ.

マークアップ早見表(Wikipedia)によるとカテゴリ名のマークアップは

  • [[Category:ヘルプ|はやみひよう]]

であり、テキストを直接と確認すると以下の行が該当する模様です。Wikipedia ではページ最下部のカテゴリの欄にリンクが追加されるマークアップだそうな。メモ:カテゴリ名は分かるが、 | のあと(上の例だと「はやみひよう」)は何だろう・・


[[Category:イギリス|*]]\n'
[[Category:英連邦王国|*]]\n'
[[Category:G8加盟国]]\n'
[[Category:欧州連合加盟国]]\n'
[[Category:海洋国家]]\n'
[[Category:君主国]]\n'
[[Category:島国|くれいとふりてん]]\n'
[[Category:1801年に設立された州・地域]]'

これを見つける正規表現を考えます。使う関数は regexp 関数。

Code
startIndex = regexp(UKWiki.text,'\[{2}Category:[^\]]*\]{2}')'
Output
startIndex = 9x1    
       67808
       67828
       67851
       67872
       67891
       67914
       67932
       67953
       67969

9つ見つかりました。それぞれの意図は・・

Code(Display)
\[{2}     % カッコ [ が2つ(\ を前に付けて文字としての [ として識別させます)
Category: % 後に続く文字列
[^\]]*    % ^\] は \] 以外の文字列。 * は 0 回以上の繰り返し。+ にすると 1 回以上。
\]{2}     % カッコ ] が2つ

です。記号の場合、正規表現の演算子と区別するために \ をいちいち付ける必要がある点は注意。

実際に合致した文字列を出すにはオプションに 'match' を加えます。

Code
match = regexp(UKWiki.text,'\[{2}Category:[^\]]*\]{2}','match')'
Output
match = 9x1 string    
"[[Category:イギリス|*]]"          
"[[Category:イギリス連邦加盟国]]"       
"[[Category:英連邦王国|*]]"         
"[[Category:G8加盟国]]"           
"[[Category:欧州連合加盟国|元]]"       
"[[Category:海洋国家]]"            
"[[Category:現存する君主国]]"         
"[[Category:島国]]"              
"[[Category:1801年に成立した国家・領域]]" 

22. カテゴリ名の抽出

記事のカテゴリ名を(行単位ではなく名前で)抽出せよ.

カテゴリ名だけを取り出す場合はトークン(正規表現の一部をかっこで囲んで定義した、一致テキストの一部)を取ります。

先ほど 'match' にしていたところを 'tokens' に変えます。そしてカテゴリ名のところをカッコで囲んでいる点に注意。

Code
out = regexp(UKWiki.text,'\[{2}Category:([^\]]*)\]{2}','tokens')'
1
1 "イギリス|*"
2 "イギリス連邦加盟国"
3 "英連邦王国|*"
4 "G8加盟国"
5 "欧州連合加盟国|元"
6 "海洋国家"
7 "現存する君主国"
8 "島国"
9 "1801年に成立した国家・領域"

カテゴリ名として | より前の部分だけ取り出す場合は・・

Code(Display)
\[{2}      % カッコ [ が2つ(\ を前に付けて文字としての [ として識別させます)
Category:  % 後に続く文字列
([^\]\|]*) % ^\]\| は \] と \| 以外の文字列。カッコで囲んでトークン化。* は 0 回以上の繰り返しを意味。
\|?        % \| が無いものもあるので、0 回、または 1 回を意味する ?
[^\]\|]*   % \] と \| 以外の文字列が 0 回以上
\]{2}      % カッコ ] が2つ
Code
out = regexp(UKWiki.text,'\[{2}Category:([^\]\|]*)\|?[^\]\|]*\]{2}','tokens')'
1
1 "イギリス"
2 "イギリス連邦加盟国"
3 "英連邦王国"
4 "G8加盟国"
5 "欧州連合加盟国"
6 "海洋国家"
7 "現存する君主国"
8 "島国"
9 "1801年に成立した国家・領域"

23. セクション構造

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

マークアップ早見表(Wikipedia)によるとセクションのマークアップは

  • == Level 2 ==
  • === Level 3 ===

の様です。セクション名(Leve 2 や Level 3)とそのレベル感(== の数から 1 引いた数字)を取ってきます。

Code
regexp(UKWiki.text,'(={2,})([^=]+)\1','match')'
Output
ans = 55x1 string    
"==国名=="          
"==歴史=="          
"==地理=="          
"===主要都市==="      
"===気候==="        
"==政治=="          
"===元首==="        
"===法==="         
"===内政==="        
"===地方行政区分==="    

それらしいものが検出されています。正規表現の意味は

Code(Display)
(={2,})   % = が 2 回以上。() で囲んでトークン化。
([^=]+)   % = 以外の文字列を 1 回以上。() で囲んでトークン化。
\1        % 1つ目のトークンの内容をリピート。

です。

1 つ目のトークン(= の繰り返し)と 2 つ目のトークン(セクション名)を取ってきて、セクション名とそのレベルを table に纏めます。

Code
out = regexp(UKWiki.text,'(={2,})([^=]+)\1','tokens')';
levels = cellfun(@(x) strlength(x(1)),out)-1;
sectionNames = cellfun(@(x) x(2),out);
sections = table(sectionNames,levels);
head(sections)
sectionNames levels
1 "国名" 1
2 "歴史" 1
3 "地理" 1
4 "主要都市" 2
5 "気候" 2
6 "政治" 1
7 "元首" 2
8 "法" 2

24. ファイル参照の抽出

記事から参照されているメディアファイルをすべて抜き出せ.

またまたマークアップ早見表(Wikipedia)によるとメディアファイルは

[[ファイル:Wikipedia-logo-v2-ja.png|thumb|説明文]]

とのこと。

ただ、thumb や 説明文が付いていないものもあります・・ややこしい。

例:[[ファイル:United States Navy Band - God Save the Queen.ogg]]

まずは該当箇所を検出してみます。

Code
regexp(UKWiki.text,'\[{2}ファイル:[^\]]+\]{2}','match')'
Output
ans = 27x1 string    
"[[ファイル:Royal Coat of Arms o…  
"[[ファイル:United States Navy B…  
"[[ファイル:Descriptio Prime Tab…  
"[[ファイル:Lenepveu, Jeanne d'A…  
"[[ファイル:London.bankofengland…  
"[[ファイル:Battle of Waterloo 1…  
"[[ファイル:Uk topo en.jpg|thumb…  
"[[ファイル:BenNevis2005.jpg|thu…  
"[[ファイル:Population density U…  
"[[ファイル:2019 Greenwich Penin…  

正規表現を分解すると・・

Code(Display)
\[{2}     % [ を 2 つ
ファイル:  % ファイル:という文字列があって
[^\]]+    % ] という文字列以外の文字列が 1 回以上繰り返されて
\]{2}     % ] が 2 つ

に合致する箇所を検索しました。

ここからファイル名だけ取り出します。基本構文での「説明文」のところが複雑なので、1つ目の | が出てくるまで(もしくはそのまま終了するまで)の文字列をファイル名として取り出してみます。

Code
tmp = regexp(UKWiki.text,'\[{2}ファイル:([^\]\|]*)(?:\]{2}|\|)','tokens')'; % cell 配列での出力
string(tmp)
Output
ans = 28x1 string    
"Royal Coat of Arms of the U…  
"United States Navy Band - G…  
"Descriptio Prime Tabulae Eu…  
"Lenepveu, Jeanne d'Arc au s…  
"London.bankofengland.arp.jpg" 
"Battle of Waterloo 1815.PNG"  
"Uk topo en.jpg"               
"BenNevis2005.jpg"             
"Population density UK 2011 …  
"2019 Greenwich Peninsula & …  

正規表現を分解すると・・

Code(Display)
\[{2}         % [ を 2 つ
ファイル:      % ファイル:という文字列があって
([^\]\|]*)    % ] と | の文字列以外の文字列が 0 回以上繰り返されて
(?:\}{2}|\|)  % ] が 2 つもしくは | が登場するまで。(?:exp1|exp2) は exp1 or exp2 でグループ化するがトークンは取らない

を探しています。ファイル名部分をトークン化している点にも注意。

確認ですが、これだと

|国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]

のファイル名までしか検出していません。最後の ]] まで検出するなら(他によいやり方あれば教えてください・・)

Code
regexp(UKWiki.text,'\[{2}ファイル:([^\]\|]*)(?:\]{2}|\|[^\]\|]*)+','tokens');
Code(Display)
\[{2}                  % [ を 2 つ
ファイル:               % ファイル:という文字列があって
([^\]\|]*)             % ] と | の文字列以外の文字列が 0 回以上繰り返されて
(?:\}{2}|\|)           % ] | が2つが登場するまで。(?:exp1|exp2) は exp1 or exp2 でグループ化するがトークンは取らない
(?:\]{2}|\|[^\]\|]*)+  % ] が2つ もしくは \| のあとに [^\]\|]* (] と | の文字列以外の文字列が 0 回以上繰り返され) る
                       % という条件を 1 回以上繰り返し。
                       % すなわち ] が2つ 出てくるまでを検出!

かなり苦しい正規表現となりました・・。

25. テンプレートの抽出

記事中に含まれる「基礎情報」テンプレートのフィールド名と値を抽出し,辞書オブジェクトとして格納せよ.

まずは基礎情報部分抽出

テンプレートの例:Template:基礎情報 国 (Wikipedia) によると

Code(Display)
{{基礎情報  
|自治領等 = 
|略名 =  
|日本語国名 =  
|公式国名 = 
(中略)
}}

のような構文の様です。これは「国」の基本情報ですが他にもいろいろあるんでしょうね。

では早速

Code(Display)
\{{2}    % { を 2 つ
基礎情報  % 「基礎情報」という文字列があって
[^}]*    % } 以外の文字列が 0 個以上
\}{2}    % } 2 つで閉じられるところ。
Code
out = regexp(UKWiki.text,'\{{2}基礎情報[^}]*\}{2}','match')
Output
out = 
    "{{基礎情報 国
     |略名  =イギリス
     |日本語国名 = グレートブリテン及び北アイルランド連合王国
     |公式国名 = {{lang|en|United Kingdom of Great Britain and Northern Ireland}}"

これはいまいち。途中で終わってしまっています。

基礎情報の要素の中に }} があるので、}} でとじられる迄、という検索方法はうまくいきません。かといって

Code(Display)
regexp(str,'\{{2}基礎情報.*\}{2}','match')

もうまくいきません。

前後参照アサーション

ここでは、基礎情報が }} の直前に改行が存在していることを利用します。

Code(Display)
(略)
|国際電話番号 = 44
|注記 = <references/>
}}

こんな感じ。

前後参照アサーション(Link)と呼ばれる構文を使います。4 種類あります。

  • expr(?=test): falling など後ろに ing が続く語句と一致させる時に使います。例:\w*(?=ing) など。
  • expr(?!test): 上の逆で ing が続かない語句との一致。
  • (?<=test)expr: 前に特定の一致を求める場合。例:(?<=re)\w* は、renew など接頭辞 re が付いた文字を探す際に使えますね。
  • (?<!test)expr: 上の逆です。

ここでは }} の前に \n (改行) が存在するということにして・・ (?<=\n) を追加。

Code
baseinfo = regexp(UKWiki.text,'\{{2}基礎情報.*(?<=\n)\}{2}','match')
Output
baseinfo = 
    "{{基礎情報 国
     |略名  =イギリス
     |日本語国名 = グレートブリテン及び北アイルランド連合王国
     |公式国名 = {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />
     *{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}([[スコットランド・ゲール語]])
     *{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}([[ウェールズ語]])
     *{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}([[アイルランド語]])
     *{{lang|kw|An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh}}([[コーンウォール語]])
     *{{lang|sco|Unitit Kinrick o Great Breetain an Northren Ireland}}([[スコットランド語]])
     **{{lang|sco|Claught Kängrick o Docht Brätain an Norlin Airlann}}、{{lang|sco|Unitet Kängdom o Great Brittain an Norlin Airlann}}(アルスター・スコットランド語)</ref>
     |国旗画像 = Flag of the United Kingdom.svg
     |国章画像 = [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
     |国章リンク =([[イギリスの国章|国章]])
     |標語 = {{lang|fr|[[Dieu et mon droit]]}}<br />([[フランス語]]:[[Dieu et mon droit|神と我が権利]])
     |国歌 = [[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />''神よ女王を護り賜え''<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}
     |地図画像 = Europe-UK.svg
     |位置画像 = United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
     |公用語 = [[英語]]
     |首都 = [[ロンドン]](事実上)
     |最大都市 = ロンドン
     |元首等肩書 = [[イギリスの君主|女王]]
     |元首等氏名 = [[エリザベス2世]]
     |首相等肩書 = [[イギリスの首相|首相]]
     |首相等氏名 = [[ボリス・ジョンソン]]
     |他元首等肩書1 = [[貴族院 (イギリス)|貴族院議長]]
     |他元首等氏名1 = [[:en:Norman Fowler, Baron Fowler|ノーマン・ファウラー]]
     |他元首等肩書2 = [[庶民院 (イギリス)|庶民院議長]]
     |他元首等氏名2 = {{仮リンク|リンゼイ・ホイル|en|Lindsay Hoyle}}
     |他元首等肩書3 = [[連合王国最高裁判所|最高裁判所長官]]
     |他元首等氏名3 = [[:en:Brenda Hale, Baroness Hale of Richmond|ブレンダ・ヘイル]]
     |面積順位 = 76
     |面積大きさ = 1 E11
     |面積値 = 244,820
     |水面積率 = 1.3%
     |人口統計年 = 2018
     |人口順位 = 22
     |人口大きさ = 1 E7
     |人口値 = 6643万5600<ref>{{Cite web|url=https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/populationestimates|title=Population estimates - Office for National Statistics|accessdate=2019-06-26|date=2019-06-26}}</ref>
     |人口密度値 = 271
     |GDP統計年元 = 2012
     |GDP値元 = 1兆5478億<ref name="imf-statistics-gdp">[http://www.imf.org/external/pubs/ft/weo/2012/02/weodata/weorept.aspx?pr.x=70&pr.y=13&sy=2010&ey=2012&scsm=1&ssd=1&sort=country&ds=.&br=1&c=112&s=NGDP%2CNGDPD%2CPPPGDP%2CPPPPC&grp=0&a=IMF>Data and Statistics>World Economic Outlook Databases>By Countrise>United Kingdom]</ref>
     |GDP統計年MER = 2012
     |GDP順位MER = 6
     |GDP値MER = 2兆4337億<ref name="imf-statistics-gdp" />
     |GDP統計年 = 2012
     |GDP順位 = 6
     |GDP値 = 2兆3162億<ref name="imf-statistics-gdp" />
     |GDP/人 = 36,727<ref name="imf-statistics-gdp" />
     |建国形態 = 建国
     |確立形態1 = [[イングランド王国]]/[[スコットランド王国]]<br />(両国とも[[合同法 (1707年)|1707年合同法]]まで)
     |確立年月日1 = 927年/843年
     |確立形態2 = [[グレートブリテン王国]]成立<br />(1707年合同法)
     |確立年月日2 = 1707年{{0}}5月{{0}}1日
     |確立形態3 = [[グレートブリテン及びアイルランド連合王国]]成立<br />([[合同法 (1800年)|1800年合同法]])
     |確立年月日3 = 1801年{{0}}1月{{0}}1日
     |確立形態4 = 現在の国号「'''グレートブリテン及び北アイルランド連合王国'''」に変更
     |確立年月日4 = 1927年{{0}}4月12日
     |通貨 = [[スターリング・ポンド|UKポンド]] (£)
     |通貨コード = GBP
     |時間帯 = ±0
     |夏時間 = +1
     |ISO 3166-1 = GB / GBR
     |ccTLD = [[.uk]] / [[.gb]]<ref>使用は.ukに比べ圧倒的少数。</ref>
     |国際電話番号 = 44
     |注記 = <references/>
     }}"

うまくいきました。

フィールド名と値の抽出

ここから | のあとの組み合わせ(フィールド名と値)を取得します。= の前後で分割すればいい・・と思いきや

|GDP/人 = 36,727<ref name="imf-statistics-gdp"/>

こんな項目があります。

よく見ると (フィールド名)空白 = という形になっているようなので・・

Code(Display)
(?<=\n)\|  % 改行文字に続く | 文字 
[^\|\=]*   % ! と = 以外の文字列 0 個以上(フィールド名)
 =         % 空白1つ明けて = 文字
[^\n]*     % 改行以外の何らかの文字列 0 個以上
\n         % 改行

でやってみます。

Code
regexp(baseinfo,'(?<=\n)\|[^\|\=]* =[^\n]*\n','match')'
Output
ans = 58x1 string    
"|略名  =イギリス↵"                  
"|日本語国名 = グレートブリテン及び北アイルランド連…  
"|公式国名 = {{lang|en|United Ki…  
"|国旗画像 = Flag of the United …  
"|国章画像 = [[ファイル:Royal Coat o…  
"|国章リンク =([[イギリスの国章|国章]])↵"    
"|標語 = {{lang|fr|[[Dieu et m…  
"|国歌 = [[女王陛下万歳|{{lang|en|Go…  
"|地図画像 = Europe-UK.svg↵"       
"|位置画像 = United Kingdom (+ov…  

フィールド名と値をトークン化すると・・

Code
baseTokens = regexp(baseinfo,'(?<=\n)\|([^\|\=]*) =([^\n]*)\n','tokens')';
baseTokens{1:2}
Output
ans = 1x2 string    
"略名 "        "イギリス"       

ans = 1x2 string    
"日本語国名"      " グレートブリテン及び北アイルランド連合王国"    

うまくいっていそう。table 型変数にまとめてみます。

Code
fields = cellfun(@(x) x(1), baseTokens);
elements = cellfun(@(x) x(2), baseTokens);
UKinfo = table(fields,elements);
head(UKinfo)
fields elements
1 "略名 " "イギリス"
2 "日本語国名" " グレートブリテン及び北アイルランド連...
3 "公式国名" " {{lang|en|United...
4 "国旗画像" " Flag of the United...
5 "国章画像" " [[ファイル:Royal Coat ...
6 "国章リンク" "([[イギリスの国章|国章]])"
7 "標語" " {{lang|fr|[[Dieu...
8 "国歌" " [[女王陛下万歳|{{lang|...

辞書オブジェクトとは?

要は「キーと値のペアを登録できるデータ構造」みたいなので、機能を果たしそうな containers.Map 値を一意のキーにマップするオブジェクト を使ってみます。

Code
M = containers.Map(fields,elements);
M('首相等氏名')
Output
ans = ' [[ボリス・ジョンソン]]'

26. 強調マークアップの除去

25の処理時に,テンプレートの値からMediaWikiの強調マークアップ(弱い強調,強調,強い強調のすべて)を除去してテキストに変換せよ(参考: マークアップ早見表).

例えばこれ:'''グレートブリテン及び北アイルランド連合王国'''

このはじめと終わりにあるのが強調マークアップです。マークアップ早見表によると

  • 他との区別(斜体):''他との区別''
  • 強調(太字):'''強調'''
  • 斜体と強調:'''''斜体と強調'''''

ということで2個、3個、5個のケースがある模様。

Code
samplestr = '''''グレートブリテン及び北アイルランド連合王国''''';
tokens = regexp(samplestr,'(\''{2,5})(.*?)(\1)','tokens');
tokens{:}
Output
ans = 1x3 cell    
''''         'グレートブリテン及び北アイルランド連合王国'    ''''         

2つ目のトークンとして取られていますね。regexprep 関数を使うと便利そう。

これは正規表現を使った文字置換ができます。置換文字を直接指定もできますし、以下の例だと $2 と2番目のトークンで置き換える、という処理をしています。今回のケースだと 2 つめのトークンで置換してみると、

Code
regexprep(samplestr,'(\''{2,5})(.*?)(\1)','$2')
Output
ans = 'グレートブリテン及び北アイルランド連合王国'

UKinfo への処理は 27 と一緒に 28 で実施します。

27. 内部リンクの除去

26の処理に加えて,テンプレートの値からMediaWikiの内部リンクマークアップを除去し,テキストに変換せよ(参考: マークアップ早見表).

マークアップ早見表によると内部リンクには

  • [[記事名]]
  • [[記事名|表示文字]]
  • [[記事名#節名|表示文字]]

の3種類がある模様。例えば: [[ボリス・ジョンソン]] は簡単です。かっこの中を単純に取り出せばOK。

Code
samplestr = '[[ボリス・ジョンソン]]';
tokens = regexp(samplestr,'\[{2}(.*)\]{2}','tokens');
tokens{1}
Output
ans = 
    {'ボリス・ジョンソン'}

記事名と表示文字がある場合は表示文字だけを残します。| のあとをトークン化。

Code(Display)
\[{2}    % [ 2個
[^\|]+   % | 以外の文字が 1 個以上で、
\|       % | が 1 個あった後に
([^\|]+) % | 以外の文字が 1 個以上 (トークン化)
\]{2}    % ] 2個
Code
samplestr = '[[イギリスの国章|国章]]';
tokens = regexp(samplestr,'\[{2}[^\|]+\|([^\|]+)\]{2}','tokens');
tokens{1}
Output
ans = 
    {'国章'}

条件演算子

ボリス・ジョンソンなど「記事名」だけの場合と、表示文字がある場合で同じ正規表現にしたい・・。ただいい手が思いつかないので、単純に expr1|expr2 と組み合わせてみます。この場合 expr1 と一致する場合、expr2 は無視されます。

Code
expr1 = '\[{2}[^\|]+\|([^\|]+)\]{2}';
expr2 = '\[{2}(.*)\]{2}';

samplestr = '[[ボリス・ジョンソン]]';
tokens = regexp(samplestr,[expr1,'|',expr2],'tokens');
tokens{1}
Output
ans = 
    {'ボリス・ジョンソン'}

Code
samplestr = '[[イギリスの国章|国章]]';
tokens = regexp(samplestr,[expr1,'|',expr2],'tokens');
tokens{1}
Output
ans = 
    {'国章'}

いい感じ。

regexprep 関数で置換

ここも regexprep 関数を使って文字置換してみます。

Code
samplestr = '[[イギリスの国章|国章]]';
regexprep(samplestr,[expr1,'|',expr2],'$1')
Output
ans = '国章'
Code
samplestr = '[[ボリス・ジョンソン]]';
regexprep(samplestr,[expr1,'|',expr2],'$1')
Output
ans = 'ボリス・ジョンソン'

UKinfo への処理は 26 と一緒に 28 で実施します。

28. MediaWikiマークアップの除去

27の処理に加えて,テンプレートの値からMediaWikiマークアップを可能な限り除去し,国の基本情報を整形せよ.

他に何があるのか・・。UKinfo を目で確認しながらそれっぽいものを処理していきます。24, 26, 27 の処理と合わせて実施します。

Code
baseinfo = regexp(UKWiki.text,'\{{2}基礎情報.*(?<=\n)\}{2}','match');

% <ref name= *</ref>, <ref name= * />, <br /> の除去
%
% 正規表現
% <        : <
% \/?      : / が 0 回か 1 回
% [br|ref] : br もしくは ref
% [^>]*?   : > 以外の文字を 0 回以上(非貪欲)
% >        : >
baseinfo1 = regexprep(baseinfo,'<\/?[br|ref][^>]*?>','');

% 参照:24. ファイル参照の抽出(ファイル名だけ残します。)
baseinfo1 = regexprep(baseinfo1,'\[{2}ファイル:([^\]\|]*)(?:\]{2}|\|[^\]\|]*)+','$1');

% 参照:26. 強調マークアップの除去
baseinfo1 = regexprep(baseinfo1,'(\''{2,5})(.*?)(\1)','$2');

% 参照:27. 内部リンクの除去
expr1 = '\[{2}[^\|\]]+\|([^\|\]]+)\]{2}';
expr2 = '\[{2}([^\[\]]+)\]{2}';
baseinfo1 = regexprep(baseinfo1,[expr1,'|',expr2],'$1');

% 追加:lang テンプレートの除去 {{lang|言語タグ|文字列}} 文字列だけを残します。
%
% 正規表現
% \{{2}        : { 2 回
% lang         : lang 文字列
% \|           : | 1 回
% \w+          : 任意の文字 1 回以上
% \|           : | 1 回
% ([^\}\|]*)   : } と | 以外の文字 0 回以上(トークン化)
% \}{2}        : } 2 回
baseinfo1 = regexprep(baseinfo1,'\{{2}lang\|\w+\|([^\}\|]*)\}{2}','$1');

この時点でさらに以下の MediaWiki マークアップのようなものがみられますので、個別に処理します。

|国歌 = 女王陛下万歳|God Save the Queen{{en icon}} 神よ女王を護り賜え {{center|United States Navy Band - God Save the Queen.ogg}}
|他元首等氏名2 = {{仮リンク|リンゼイ・ホイル|en|Lindsay Hoyle}}
|人口値 = 6643万5600 {{Cite web|url=https://www.o
|GDP値元 = 1兆5478億 [http://www.imf.org/ex
|確立年月日2 = 1707年{{0}}5月{{0}}1日
Code
% |国歌 = 女王陛下万歳|God Save the Queen{{en icon}} 
% 神よ女王を護り賜え {{center|United States Navy Band - God Save the Queen.ogg}}
baseinfo1 = regexprep(baseinfo1,'\{{2}en icon\}{2}','');
baseinfo1 = regexprep(baseinfo1,'\{{2}center\|([^\}]+)\}{2}','$1');

% |他元首等氏名2 = {{仮リンク|リンゼイ・ホイル|en|Lindsay Hoyle}}
% 参照:24. ファイル参照の抽出
baseinfo1 = regexprep(baseinfo1,'\{{2}仮リンク\|([^\}\|]*)(?:\}{2}|\|[^\}\|]*)+','$1');

% |人口値 = 6643万5600 {{Cite web|url=https://www.o
% |GDP値元 = 1兆5478億 [http://www.imf.org/ex
baseinfo1 = regexprep(baseinfo1,'\[http:.*\]','');
baseinfo1 = regexprep(baseinfo1,'\{{2}Cite web\|url\=https:[^\}]+\}{2}','');

% |確立年月日2 = 1707年{{0}}5月{{0}}1日
baseinfo1 = regexprep(baseinfo1,'\{{2}(\d)\}{2}','$1');

以上!長かった。。

結果をテーブルにまとめておきます。

Code
baseTokens = regexp(baseinfo1,'(?<=\n)\|([^\|\=]*) =\s?([^\|]*)\n','tokens')';
fields = cellfun(@(x) x(1), baseTokens);
elements = cellfun(@(x) x(2), baseTokens);
join([fields+":"+elements],newline)
Output
ans = 
    "略名 :イギリス
     日本語国名:グレートブリテン及び北アイルランド連合王国
     公式国名:United Kingdom of Great Britain and Northern Ireland英語以外での正式国名:
     *An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath(スコットランド・ゲール語)
     *Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon(ウェールズ語)
     *Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann(アイルランド語)
     *An Rywvaneth Unys a Vreten Veur hag Iwerdhon Glédh(コーンウォール語)
     *Unitit Kinrick o Great Breetain an Northren Ireland(スコットランド語)
     **Claught Kängrick o Docht Brätain an Norlin Airlann、Unitet Kängdom o Great Brittain an Norlin Airlann(アルスター・スコットランド語)
     国旗画像:Flag of the United Kingdom.svg
     国章画像:Royal Coat of Arms of the United Kingdom.svg
     国章リンク:(国章)
     標語:Dieu et mon droit(フランス語:神と我が権利)
     地図画像:Europe-UK.svg
     位置画像:United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
     公用語:英語
     首都:ロンドン(事実上)
     最大都市:ロンドン
     元首等肩書:女王
     元首等氏名:エリザベス2世
     首相等肩書:首相
     首相等氏名:ボリス・ジョンソン
     他元首等肩書1:貴族院議長
     他元首等氏名1:ノーマン・ファウラー
     他元首等肩書2:庶民院議長
     他元首等氏名2:リンゼイ・ホイル
     他元首等肩書3:最高裁判所長官
     他元首等氏名3:ブレンダ・ヘイル
     面積順位:76
     面積大きさ:1 E11
     面積値:244,820
     水面積率:1.3%
     人口統計年:2018
     人口順位:22
     人口大きさ:1 E7
     人口値:6643万5600
     人口密度値:271
     GDP統計年元:2012
     GDP値元:1兆5478億
     GDP統計年MER:2012
     GDP順位MER:6
     GDP値MER:2兆4337億
     GDP統計年:2012
     GDP順位:6
     GDP値:2兆3162億
     GDP/人:36,727
     建国形態:建国
     確立形態1:イングランド王国/スコットランド王国(両国とも1707年合同法まで)
     確立年月日1:927年/843年
     確立形態2:グレートブリテン王国成立(1707年合同法)
     確立年月日2:1707年05月01日
     確立形態3:グレートブリテン及びアイルランド連合王国成立(1800年合同法)
     確立年月日3:1801年01月01日
     確立形態4:現在の国号「グレートブリテン及び北アイルランド連合王国」に変更
     確立年月日4:1927年04月12日
     通貨:UKポンド (£)
     通貨コード:GBP
     時間帯:±0
     夏時間:+1
     ISO 3166-1:GB / GBR
     ccTLD:.uk / .gb使用は.ukに比べ圧倒的少数。
     国際電話番号:44
     注記:"

Map オブジェクトも作っておきます。

Code
M = containers.Map(fields,elements);
M('国旗画像')
Output
ans = 'Flag of the United Kingdom.svg'

29. 国旗画像のURLを取得する

テンプレートの内容を利用し,国旗画像のURLを取得せよ.(ヒント: MediaWiki APIimageinfoを呼び出して,ファイル参照をURLに変換すればよい)

上の結果より、国旗画像のファイル名は Flag of the United Kingdom.svg ということでした。

MediaWiki APIの imageinfo にある GET request の箇所を確認すると、以下のフォーマットでファイルの情報が取れるとあります。

api.php?action=query&format=json&prop=imageinfo&titles=File:Billy_Tipton.jpg

URL を取るには、追加で iiprop に url を入れておけば良いようです。MATLAB では webread 関数を使えばいいですね。

Code
baseurl = 'https://commons.wikimedia.org/w/api.php';

data = webread(baseurl,'action','query',...
    'format','json',...
    'prop','imageinfo',...
    'iiprop','url',...
    'titles','File:Flag_of_the_United_Kingdom.svg');

data.query.pages.x347935.imageinfo
Output
ans = 
                    url: 'https://upload.wikimedia.org/wikipedia/commons/a/ae/Flag_of_the_United_Kingdom.svg'
         descriptionurl: 'https://commons.wikimedia.org/wiki/File:Flag_of_the_United_Kingdom.svg'
    descriptionshorturl: 'https://commons.wikimedia.org/w/index.php?curid=347935'

URL とれました。

  1. Livescript から markdown への変換は livescript2markdown​: MATLAB's live scripts to markdown を使っています。

5
6
2

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
6