10 posts tagged with "Regexp"
View All TagsPython: 正規表現の基本 - 最長、最短マッチング
直前の文字、メタ文字を繰り返しマッチングさせる量指定記号である「*」「+」「?」などは、テキスト中にその繰り返しパターンがマッチする箇所が複数ある場合は、通常最後にマッチした箇所をオブジェクトに記録します。このような最長マッチングに対して、マッチング箇所が複数の場合に最初にマッチした箇所を記録する最短マッチング方法があります。 最短マッチングを行う量指定記号は最長マッチングの記号の末尾に「?」を付けるだけです。すなわち、最長の量指定が「*」「+」「?」であるのに対して、**最短マッチングは「*?」「+?」「??」**となります。それでは、以下のコードで確認してみましょう。
ソースコード
# coding: Shift_JIS import re # 正規表現を扱うモジュールのインポート # 正規表現のチェックプリント用の関数 def PrintRegMatch(pat, txt): # 書式: re.search(パターン, テキスト) m = re.search(pat, txt) # パターンにマッチしなかった場合はNoneを返す if m != None: print 'パターン: "%s"nテキスト: "%s"nマッチ : する' % (pat, txt) print 'マッチング開始位置:', m.start() print 'マッチング終了位置:', m.end() else: print 'パターン: "%s"nテキスト: "%s"nマッチ: しない' % (pat, txt) print return m # サンプルテキスト txt = '
Hello! World!!
Goodbye World
' # 最長: .* # 最短: .*? PrintRegMatch('
.*
', txt) # 最長: 文字列末尾の
にマッチ PrintRegMatch('
.*?
', txt) # 最短: World!!
の
にマッチ # 最長: .+ # 最短: .+? PrintRegMatch('
.+
', txt) # +?は直前の文字の1文字以上の繰り返し PrintRegMatch('
.+?
', txt) # ↑の控えめマッチング # 最長: .? # 最短: .?? PrintRegMatch('B.?C', 'BCCC') # ?は直前の文字の0 or 1回の繰り返し PrintRegMatch('B.??C', 'BCCC') # ??はその控えめ(ry
re.searchはre.matchと異なり、パターンをテキスト文字列の先頭以外にもマッチさせます。
実行結果
パターン: "
.*
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 41 パターン: "
.*?
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 21 パターン: "
.+
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 41 パターン: "
.+?
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 21 パターン: "B.?C" テキスト: "BCCC" マッチ : する マッチング開始位置: 0 マッチング終了位置: 3 パターン: "B.??C" テキスト: "BCCC" マッチ : する マッチング開始位置: 0 マッチング終了位置: 2
リファレンス
Python: 正規表現の基本 - 文字範囲の指定「[ ]」
ソースコード
# coding: Shift_JIS
import re # 正規表現を扱うモジュールのインポート
# 正規表現のチェックプリント用の関数
def PrintRegMatch(pat, txt):
# 探索される文字列をテキスト
# 探索する 文字列をパターン
# 書式: re.match(パターン, テキスト)
m = re.match(pat, txt) # パターンにマッチしなかった場合はNoneを返す
if m != None: print 'パターン"%s"はテキスト"%s"にマッチ「する」' % (pat, txt)
else: print 'パターン"%s"はテキスト"%s"にマッチ「しない」' % (pat, txt)
# []で括られた文字の内どれか1つにマッチすれば真
PrintRegMatch('[ABC]D', 'CD') # A or B or C の次に D がくるパターンにマッチ
PrintRegMatch('[A-C]E', 'CE') # [A-C]は[ABC]と同意
print
# [A-Z]は全てのアルファベット大文字の内のどれか1つの意
PrintRegMatch('[A-Z]*END', 'CHSHSBSRAWRGARENDGREW')
PrintRegMatch('A[A-Z]*E', 'AgafgafE')
PrintRegMatch('A[a-z]*E', 'AewnglfngowE')
# [0-9]は0~9の数字の内どれか1つの意
PrintRegMatch('[0-9]*-[0-9]*-[0-9]*', '03-3333-2222')
print
# []内の「^」否定
PrintRegMatch('[^ABC]D', 'DD') # A or B or C でない文字の次に D がくるパターンにマッチ
PrintRegMatch('[^A-Z]*-[^A-Z]*', '164-9999')
print
PrintRegMatch('[ABC][DEF]', 'CE')
PrintRegMatch('[ABC][DEF]', 'CC')
PrintRegMatch('[a-zA-Z]*', 'FjAVzxRUqyOIn')
PrintRegMatch('[a-z][A-Z]*', 'FjAVzxRUqyOIn')
PrintRegMatch('[a-z]*[A-Z]*', 'FjAVzxRUqyOIn')
実行結果
パターン"[ABC]D"はテキスト"CD"にマッチ「する」
パターン"[A-C]E"はテキスト"CE"にマッチ「する」
パターン"[A-Z]*END"はテキスト"CHSHSBSRAWRGARENDGREW"にマッチ「する」
パターン"A[A-Z]*E"はテキスト"AgafgafE"にマッチ「しない」
パターン"A[a-z]*E"はテキスト"AewnglfngowE"にマッチ「する」
パターン"[0-9]*-[0-9]*-[0-9]*"はテキスト"03-3333-2222"にマッチ「する」
パターン"[^ABC]D"はテキスト"DD"にマッチ「する」
パターン"[^A-Z]*-[^A-Z]*"はテキスト"164-9999"にマッチ「する」
パターン"[ABC][DEF]"はテキスト"CE"にマッチ「する」
パターン"[ABC][DEF]"はテキスト"CC"にマッチ「しない」
パターン"[a-zA-Z]*"はテキスト"FjAVzxRUqyOIn"にマッチ「する」
パターン"[a-z][A-Z]*"はテキスト"FjAVzxRUqyOIn"にマッチ「しない」
パターン"[a-z]*[A-Z]*"はテキスト"FjAVzxRUqyOIn"にマッチ「する」
リファレンス
Python: 正規表現の基本 - 繰り返し「*」「+」「?」
ソースコード
# coding: Shift_JIS
import re # 正規表現を扱うモジュールのインポート
# 正規表現のチェックプリント用の関数
def PrintRegMatch(pat, txt):
# 書式: re.match(パターン, テキスト)
m = re.match(pat, txt) # パターンにマッチしなかった場合はNoneを返す
if m != None: print 'パターン"%s"はテキスト"%s"にマッチ「する」' % (pat, txt)
else: print 'パターン"%s"はテキスト"%s"にマッチ「しない」' % (pat, txt)
txt = 'ABCDEFGH' # 探索される文字列をテキスト
# 探索する 文字列をパターン
# 「*」: 直前の文字の0回以上の繰り返しにマッチ
PrintRegMatch('.*CDE', txt)
PrintRegMatch('A.*H', txt)
PrintRegMatch('A*BC', txt)
PrintRegMatch('A.*BC', txt)
PrintRegMatch('A*', '')
print
# 「+」: 直前の文字の1回以上の繰り返しにマッチ
PrintRegMatch('.+DEF', txt)
PrintRegMatch('A.+H', txt)
PrintRegMatch('A+BC', txt)
PrintRegMatch('A.+BC', txt)
PrintRegMatch('A+', '')
print
# 「?」: 直前の文字の0or1回の繰り返しにマッチ
PrintRegMatch('^A?.+$', txt)
PrintRegMatch('^B?$', '')
PrintRegMatch('^C?E', 'E')
PrintRegMatch('A.?H', txt)
PrintRegMatch('BBB?C', 'BBC')
実行結果
パターン".*CDE"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.*H"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A*BC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.*BC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A*"はテキスト""にマッチ「する」
パターン".+DEF"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.+H"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A+BC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"A.+BC"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"A+"はテキスト""にマッチ「しない」
パターン"^A?.+$"はテキスト"ABCDEFGH"にマッチ「する」
パターン"^B?$"はテキスト""にマッチ「する」
パターン"^C?E"はテキスト"E"にマッチ「する」
パターン"A.?H"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"BBB?C"はテキスト"BBC"にマッチ「する」
リファレンス
Python: 正規表現の基本 - メタ文字「.」「^」「$」
ソースコード
# coding: Shift_JIS
import re # 正規表現を扱うモジュールのインポート
# 正規表現のチェックプリント用の関数
def PrintRegMatch(pat, txt):
# 書式: re.match(パターン, テキスト)
m = re.match(pat, txt) # パターンにマッチしなかった場合はNoneを返す
if m != None: print 'パターン"%s"はテキスト"%s"にマッチ「する」' % (pat, txt)
else: print 'パターン"%s"はテキスト"%s"にマッチ「しない」' % (pat, txt)
txt = 'ABCDEFGH' # 探索される文字列を「テキスト」
# 探索する 文字列を「パターン」
PrintRegMatch('ABC', txt)
PrintRegMatch('FGH', txt)
print
# メタ文字を用いたマッチング
# 「.」任意の1文字にマッチ
PrintRegMatch('.BCD', txt)
PrintRegMatch('....EFGH', txt)
PrintRegMatch('...EFGH', txt)
print
# 「^」は文字列の先頭にマッチするパターン(MULTILINEフラグを設定すると行頭にもマッチ)
PrintRegMatch('^ABC', txt)
PrintRegMatch('^BCD', txt)
print
# 「$」は文字列の末尾にマッチするパターン(MULTILINEフラグを設定すると行末にもマッチ)
PrintRegMatch('EFGH$', txt)
PrintRegMatch('....EFGH$', txt)
実行結果
パターン"ABC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"FGH"はテキスト"ABCDEFGH"にマッチ「しない」
パターン".BCD"はテキスト"ABCDEFGH"にマッチ「する」
パターン"....EFGH"はテキスト"ABCDEFGH"にマッチ「する」
パターン"...EFGH"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"^ABC"はテキスト"ABCDEFGH"にマッチ「する」
パターン"^BCD"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"EFGH$"はテキスト"ABCDEFGH"にマッチ「しない」
パターン"....EFGH$"はテキスト"ABCDEFGH"にマッチ「する」
リファレンス
JavaプログラムからExcite翻訳を利用
POSTメソッドを用いてWebページのフォームにリクエストを送信し、そのレスポンスを取得するプログラム例として、エキサイト 翻訳を利用してみます。 送信クエリの1つは翻訳言語設定、2つ目は翻訳対象文字列でレスポンスのWebページから翻訳された文字列を抽出します。
ソースコード
import java.net.*;
import java.util.regex.*;
import java.io.*;
/**
* Excite翻訳(http://www.excite.co.jp/world/)を利用するクラス
*/
public class ExciteTrans {
private String direction; // 翻訳する言語設定 "ENJA" or "JAEN"
/** テスト用main() */
public static void main(String[] args) {
ExciteTrans et = new ExciteTrans();
System.out.println(et.getTransText("Hello!")); // 翻訳対象テキスト
}
/** コンストラクタ */
public ExciteTrans() { direction ="ENJA"; }
public ExciteTrans(String str) {
if (str.equals("JAEN") || str.equals("ENJA"))
direction = str;
else
direction ="JAEN";
}
/**
* テキストを翻訳
* @param before 翻訳前のテキスト
* @return 翻訳後のテキスト
*/
public String getTransText(String before) {
String afterText = null; // 翻訳されたテキスト
try {
// URLクラスのインスタンスを生成
URL exciteURL =
new URL("http://www.excite.co.jp/world/english/");
// 接続します
URLConnection con = exciteURL.openConnection();
// 出力を行うように設定
con.setDoOutput(true);
// 出力ストリームを取得
PrintWriter out = new PrintWriter(con.getOutputStream());
// クエリー文の生成・送信
out.print("before={"+ before +"}&wb_lp={"+ direction +"}");
out.close();
// 入力ストリームを取得
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
// 一行ずつ読み込む
String aline;
// 抽出用の正規表現
String regex = "<textarea [^>].*after.*>(.*)";
Pattern pattern = Pattern.compile(regex);
while ((aline = in.readLine()) != null) {
Matcher mc = pattern.matcher(aline);
if(mc.matches()) {
afterText = mc.group(1);
}
}
in.close(); // 入力ストリームを閉じる
} catch (IOException e) {
e.printStackTrace();
}
return afterText;
}
}
実行結果
こんにちは!Java MSN Messenger Library (JML)で翻訳ロボを作る
Java MSN Messenger Library (JML)とは Windows Live Messenger 上の通信プロトコルMSNP8-MSNP12をサポートするライブラリです。主に、メンバーの会話に自動応答するチャットボット(Chat Bot)や人工無脳などを作る際に用いられます。 今回は、ライブラリに付属しているサンプルプログラムのEchoMessengerクラスにこの翻訳クラスをかませて、メンバーの発言を翻訳し、その文字列色をランダムで選択した色で応答するチャットボットを作成してみました。 実行結果は下図になります。  コンストラクタでの翻訳言語設定が逆でも、文字列が英語or日本語に統一されていれば、Excite翻訳側がそれに合わせて適当な言語で翻訳するみたいです。 最後に、礼儀正しくクローリングする際は
- robots.txtやメタタグを守るように
- 相手サーバに負荷をかけすぎないように
- 相手Webサイトポリシー(利用規約)を守るように
- 頻繁に使いたいモノなら、同機能のWebサービスを使うこと(利用規約内で)
Webページから指定したタグの要素を抜き出すRuby関数
単一のWebページから抜き出した複数の要素を配列に格納して返します。 以下の例はaタグの要素(エレメント)を抽出した場合です。
Rubyコード
require 'net/http'
require 'kconv'
def parse_array(string, beg_tag, close_tag)
array = Array.new
string.scan(/#{beg_tag}(.*?)#{close_tag}/sm) { |matched|
#puts matched
array = array | matched
}
return array
end
Net::HTTP.version_1_2
Net::HTTP.start('b.hatena.ne.jp', 80) {|http|
response = http.get('/hotentry/')
str = Kconv.tosjis(response.body)
a_tag_array = parse_array(str, "" )
puts a_tag_array
}
学んだこと
- Net::HTTP.startメソッドをブロックを用いて呼び出すことによって、ブロックの間だけセッションを開いて接続し、ブロック終了とともに自動的にセッションを閉じる。Rubyに慣れてもJavaやCでの手続きを意識しておくこと。
- 配列の結合には和集合(演算子は「|」)を用いた。この場合、重複する要素は1つとして数えられる。別々に数えたい場合は「+」演算子を用いる。
- String#scanメソッドはブロックで用いるとブロック変数にマッチした部分を格納する。その際、正規表現の中で「()」が用いられると、()内の部分を配列にして返す。今回は別に配列にする必要はなかったかも。
参考サイト
Web サーバからドキュメントを得る - Rubyist Magazine - 標準添付ライブラリ紹介 【第 7 回】 net/http
タグの中の要素を抜き出すRuby関数
ライブラリを使えば簡単ですが、正規表現の学習の為に。
ソースコード
def return_between(unporsed, start, termi)
unporsed =~ /#{start}(.*?)#{termi}/
return $1
end
str = "<title>Trump Code</title>"
start = "<title>"
termi = "</title>"
puts return_between(str, start, termi)
#=> Trump Code
ここで学んだことは、正規表現の規則中に変数を用いる際は#{var_str}と表記すること。
POSTメソッドを用いてExcite翻訳を行うRubyコード
しかし、未完です。
Webの巡回などにはWWW::Mechanizeという便利なライブラリがありますが、あえてnet/httpのPOSTメソッドを使う理由は、単にPOSTそのものと正規表現の学習をするためです。
今回は正規表現で試行錯誤。
Rubyソースコード
#!/usr/bin/ruby
require 'net/http'
require 'kconv'
before = "hello"
http = Net::HTTP.new('www.excite.co.jp')
response = http.post('/world/english', "before=#{before}&wb_lp=ENJA")
result = Kconv.tosjis(response.body)
result =~ /"after"[^>]*>(.*)/ism
puts $1
実行結果
こんにちは
<中略>
ここで、正規表現
/"after"[^>]*>(.*)/ism
の部分を
/"after"[^>]*>(.*)< \/textarea>/ism
に変更するとマッチしなくなってしまいました。
オプション指定のmで複数行にマッチするはずなんですが・・・うーん、何を見落としているのだろう。
チャットログから本文を抽出
先日、メッセンジャーのチャットで会話するボットを作りました。 そのボットに「学習」させるネタに、会話文であるWindows Live Messengerのチャットログを用いることにしました。しかし、ログはXML形式なので、本文以外の余計なタグがはいっています。
まあ、エディタで不用な部分を置換していっても良いのですが...
抽出処理をRubyで自動化
仮に1時間かかる単調作業をするとしたら、私は50分かけても、その作業をこなすスクリプトを書く。
と言う言葉を思いだしたので、早速、Windows Live Messenger(前MSN)のチャットログから本文のみを抽出するコードを書いてみました。
#! ruby -Ks
require ‘kconv’
MSNRegexp =/(.*?)<.Text>/xm
data = ""
filename = ARGV[0]
begin
open(filename) do |f|
f.each do |line| # ファイルから1行ずつ読む
line = Kconv.kconv(line, Kconv::SJIS) # 文字コードを変換
line.chomp!
next if line.empty?
data.concat(line)
end
end
rescue => e
puts(e.message)
end
open(’msnLog.txt’, ‘w’) do |f| # 出力ファイル
data.scan(MSNRegexp){|tdata|
if tdata
f.puts(tdata)
end
}
end
上記のコードをプロンプトで抽出したいファイル名を引数に与えて 実行すると、msnLog.txtというファイルに本文が抽出されます。発言者なども抽出したい場合は、正規表現の部分を修正すれば良いですね。
では、今し方学習したボットとの会話はこんなです。 「とあるボットのチャットログ」 やってる本人は、結構楽しんでましたね^^;と言うか合せてました。
今後の改良点
当面の課題は、辞書のメンテツールの作成及びデータベース化、人格形成、文脈・話者理解と情報収集・加工機能を利用環境に合せたエージェント化とか(ぅぉぃ)。
