FC2ブログ

Ruby on RailsでWebサイト公開!に挑戦中

レンタルサーバーでWebサイトを公開すべく、Ruby on Railaの勉強をする日々を語ります。

PREV | PAGE-SELECT | NEXT

≫ EDIT

Rubyで文字列のマッチング

Rubyで正規表現などを使って文字列をマッチングさせてデータを抽出する方法を確認しました。ここでは、HTMLのテーブルデータ内からデータを抽出する場合を例に確認しました。



●サンプルデータ

・ヒアドキュメントを使って文字列リテラルを定義

〇ヒアドキュメント
・`<<識別子' を含む行の次の行から `識別子'だけの行の直前までを文字列とする行指向のリテラル。
https://docs.ruby-lang.org/ja/2.4.0/doc/spec=2fliteral.html#here

str = <・・・
<a href="#href1" class="#class1">リンク1</a>
<a href="#href2" class="#class2">リンク2</a>
<a href="#href3" class="#class3">リンク3</a>
<table border="1" cellspacing="0" cellpadding="5" width="50%" class="datatable">
<tbody>
<tr>
<th>
<p align="center">町丁名</p>
</th>
<th>
<p align="center">男</p>
</th>
<th>
<p align="center">女</p>
</th>
<th>
<p align="center">合計</p>
</th>
</tr>
<tr>
<td>
<p align="right">芝一丁目</p>
</td>
<td>
<p align="right">722</p>
</td>
<td>
<p align="right">735</p>
</td>
<td>
<p align="right">1,457</p>
</td>
</tr>
・・・
<tr>
<td>
<p align="right">合計</p>
</td>
<td>
<p align="right">6,378</p>
</td>
<td>
<p align="right">6,760</p>
</td>
<td>
<p align="right">12,138</p>
</td>
</tr>
</tbody>
</table>
EOS


1)Rubyの正規表現について
https://docs.ruby-lang.org/ja/2.4.0/doc/spec=2fregexp.html

2)正規表現でパターンマッチして文字列抽出

・Stringクラスのscanメソッドを使用
https://docs.ruby-lang.org/ja/2.4.0/class/String.html#I_SCAN

〇scan(pattern) -> [String] | [[String]]
・patternを繰り返しマッチし、マッチした部分文字列の配列を返す。
・patternが正規表現で括弧を含む場合は、 括弧で括られたパターンにマッチした部分文字列の配列の配列を返す。

例1)マッチしなかった場合
・上記サンプルデータに対し、マッチしないパターンで実行した場合
result = str.scan(/<ul\s.*?<\/ul>/)
puts result.class      →Array
puts result[0].class    →NilClass
puts result.empty?     →true

NillClassの配列を返す。

例2)正規表現内に括弧を使用しない場合の例

result1 = str.scan(/<a\s.*?<\/a>/)
result1.each{|x|
print x,"\n"
}

<a href="#href1" class="#class1">リンク1</a>
<a href="#href2" class="#class2">リンク2</a>
<a href="#href3" class="#class3">リンク3</a>

上記サンプルデータ内には三つのリンクタグが含まれているので、
["1つ目のリンクタグ","2つ目","3つ目"]
の配列がリターンされる。

例3)正規表現内に括弧を使用する場合
・マッチした文字列の中からさらに部分的に文字列を抽出する場合は正規表現に丸括弧をしていする。この場合は、scanメソッドは配列の配列を返す。

result2 = str.scan(/<a\shref="(.*?)".*>(.*?)<\/a>/)
result2.each{|x|
print "href:#{x[0]},リンク名:#{x[1]}\n"
}

href:#href1,リンク名:リンク1
href:#href2,リンク名:リンク2
href:#href3,リンク名:リンク3

上記例の場合、丸括弧が二つ指定されているので、以下のような配列の配列がリターンされる。
[
["一つ目のリンクのhrefのデータ","一つ目のリンクのclassのデータ"],
["二つ目のリンクのhrefのデータ","一つ目のリンクのclassのデータ"],
["三つ目のリンクのhrefのデータ","一つ目のリンクのclassのデータ"]
]

3)正規表現を使って文字列を置換

・Stringグラスのgsubメソッドを使用。
https://docs.ruby-lang.org/ja/2.4.0/class/String.html#I_GSUB

〇gsub(pattern, replace) -> String
・文字列中でpatternにマッチする部分全てを文字列replaceで置き換えた文字列を生成して返す。

例)テーブルのtdタグ内の不要な文字列を置換して除去
・サンプルデータ内から"tbodyタグ内の文字列を部分文字列で抽出。
・数値データ以外の不要なタグ、改行、スペースなどの文字列を除去。

a_tbody = str.scan(/<tbody>(.*?)<\/tbody>/m)
s_tbody = a_tbody[0][0].gsub(/(<p align="right">|\r\n|\r|\n|\s|<p>|<\/p>| )/, "")

4)配列、文字列の存在確認

上記3)の例では、strリテラルに対するscanメソッドの結果(配列)に対して、配列の配列に対する参照、gsubメソッドを実行しています。

1行目でパターンがマッチされなかった場合は、NilClassの配列がリターンされるため、2行目の配列の配列(a_tbody[0][0])に対する参照、Stringオブジェクトのscanメソッドはエラーとなります。

以下、配列、文字列のチェックなどの方法のいくつかを示します。

①Arrayオブジェクトのempty?メソッド
自身の要素の数が 0 の時に真を返す。そうでない場合に false を返す。

例)

a_tbody = str.scan(/<tbody>(.*?)<\/tbody>/m)
if !a_tbody.empty?
s_tbody = a_tbody[0][0].gsub(/(<p align="right">|\r\n|\r|\n|\s|<p>|<\/p>| )/, "")

②Objectクラスのclassメソッド
オブジェクトのクラスを確認
例)
puts a_tbody[0][0].class

③Objectクラスのinstance_of?メソッド
オブジェクトが指定したクラスの直接のインスタンスであるかチェック。

例)

if !a_tbody.empty?
if a_tbody[0][0].instance_of?(String)
s_tbody = a_tbody[0][0].gsub(/(<p align="right">|\r\n|\r|\n|\s|<p>|<\/p>| )/, "")

5)正規表現を使ってパターンマッチして真偽の判定

〇文字列 =~ 正規表現 -> Integer
https://docs.ruby-lang.org/ja/2.4.0/method/String/i/=3d=7e.html
・正規表現とのマッチを行い、マッチが成功すればマッチした位置のインデックスを、そうでなければ nil を返す。
・Rubyではfalseまたはnilだけが偽で、それ以外は0や空文字列も含め全て真なので、真偽の判定が出来る。

例)テーブル内のtdタグ内のデータを抽出するが、項目名に(総数|合計|計)が含まれる行のデータは除外する

a_tbody = str.scan(/<tbody>(.*?)<\/tbody>/m)
if !a_tbody.empty?
puts a_tbody[0][0].class
if a_tbody[0][0].instance_of?(String)
s_tbody = a_tbody[0][0].gsub(/(<p align="right">|\r\n|\r|\n|\s|<p>|<\/p>| )/, "")
a_tr = s_tbody.scan(/<tr>(.*?)<\/tr>/m)
a_tr.each{|tr|
a_td = tr[0].scan(/<td>(.*?)<\/td>/m)
if !a_td.empty?
if a_td[0][0] !~ /(総数|合計|計)/
print "#{a_td[0][0]}の男は#{a_td[1][0]}人、女は#{a_td[2][0]}人、合計#{a_td[3][0]}人です。\n"
end
end
}
end
end

| Ruby | 10:37 | comments:0 | trackbacks:0 | TOP↑

COMMENT















非公開コメント

TRACKBACK URL

http://hbnist76.blog.fc2.com/tb.php/573-579a3c5f

TRACKBACK

PREV | PAGE-SELECT | NEXT