重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

電子書籍の厳選無料作品が豊富!

正規表現の検索及び置換について質問させていただきます。

下記のような文字列があったとします。
「私は、<gaiji gaijisyurui="0001" gaijicode="F040" /><gaiji gaijisyurui="0002" gaijicode="F041" />で、 <gaiji gaijisyurui="0003" gaijicode="F042" />です。」

この文章から
 <gaiji gaijisyurui="0001" gaijicode="F040" />
 <gaiji gaijisyurui="0002" gaijicode="F041" />
 <gaiji gaijisyurui="0003" gaijicode="F042" />
の部分を検索して、それぞれ
 <外字0001F040>
 <外字0002F041>
 <外字0003F042>
と置き換え、最終的には、
 「私は、<外字0001F040><外字0002F041>で、<外字0003F042>です。」
という文字列にする関数を作成したいと思っておりますが、どのようにソースを作ればよろしいのでしょうか?
アドバイスや具体的なソースコードをいただけると非常に助かります。

現在、途中までソースを作成しているのですが、文字数制限でソースの半分も入りきらなかったため大雑把に書かせていただきます。

※※※以降の処理が分かりません。
検索結果に対して文字列置換を行っても元の文字列内の置換にはならない?

Private Function GaijiChange(ByVal pNaiyo As String) As String
Dim wNaiyo As String = "" ' 置換後文字列
Dim wGaijisyurui As String = ""
Dim wGaijicode As String = ""
Dim wChangeWord As String = ""
Dim wChangeStr As String = ""
Dim Work As String = ""

' 正規表現で<gaiji>タグを検索
Dim wSeikiHyogen As String
wSeikiHyogen = "<gaiji gaijisyurui=\" & """" & "[0-9a-zA-Z]{4}\" & """" & " gaijicode=\" & """" & "[0-9a-zA-Z]{4}\" & """" & " />" '正規表現
Dim wRegex As New System.Text.RegularExpressions.Regex( _
wSeikiHyogen, System.Text.RegularExpressions.RegexOptions.IgnoreCase)

' 文字列にに含まれる<gaiji>タグを全て検索
Dim wMc As System.Text.RegularExpressions.MatchCollection = wRegex.Matches(pNaiyo)

For Each m As System.Text.RegularExpressions.Match In wMc
' 検索結果からgaijisyuruiを取得
wGaijisyurui =
' 検索結果からgaijicodeを取得
wGaijicode =
' 置換文字列作成
wChangeWord = "<外字" & wGaijisyurui & wGaijicode & ">"

' 検索結果を置換
' ※※※

Next

Return wNaiyo
End Function

以上、宜しくお願いいたします。

A 回答 (5件)

#2です。


正規表現の構文は下記を参照してください。
http://msdn.microsoft.com/ja-jp/library/cc392020 …

いきなりコードを提示したので、説明を付け加えますね。

先ず、文中にあるタグ部分とそれ以外の部分を分けて
考える必要があります。タグ部分を取り出すには
"<"ではじまり、">"で終わるという部分列ですので、
正規表現は"<.+?>"となります。
"."は「任意の文字」、"+"は「1個以上」、"?"は「最短」
ということです。「最短」は次の意味です。
例:「AAA<P>BBB<U>CCC」
これに対して"<.+>"を指定すると、"<P>BBB<U>"が
抜き出されます。つまり、規定は「最長」なので、">"が
見つかるまで、なるべく沢山の文字を含めようとします。
"<.+?>"これだと、「最短」なので、最初に見つかる">"
までを含めるようにするので、"<P>"が抽出されます。
よって、"<[^>]+?>"は考えすぎでしたね、間違いでは
ないのですが、冗長でした。更に外字タグ以外のタグが
あると困るので、正確には以下のようになります。
長いので2行に分けます。

"<\s*gaiji\s+gaijisyurui\s*=\s*['""]{0,1}.{4}['""]{0,1}
\s+gaijicode\s*=\s*['""]{0,1}.{4}['""]{0,1}\s*/\s*>"

"\s"は空白(タブ含む)を表わし、"*"は「0以上」です。
よって、"<gaiji~"でも"< gaiji~"でもヒットします。
"gaiji"と"gaijisyurui"の間も空白が1とは限らないので
"\s+"にしています。"+"は前にも述べたように「1以上」
のことです。同様に"="の前後に空白があっても対応
可能です。また、"gaijisyuru=0001"のように引用符を
置かないで値を指定する可能性もあるので、"['""]{0,1}"
という表現にしました。['"] はアポストロフィかクォートと
いう意味です。"{0,1}"はご理解の通り、「0か1」です。

次に、このように抜き出した部分列(<gaiji~/>)から、
余分な文字を除去すれば目的のものが得られますね。
余分な所とは「gaijisyurui」の前、「gaijicode」までの間、
「gaijicode」の後の3部分です。1個ずつ3回に分けて
処理してもよいのですが、1度でやるため、3パターンを
"|"(垂線)でOR結合しました。

"(<\s*gaiji\s+gaijisyurui\s*=\s*['""]{0,1})|"
"(['""]{0,1}\s*gaijicode\s*=\s*['""]{0,1})|"
"(['""]{0,1}.+>)"

上記に該当する部分をReplaceメソッドで空文字列に
置換することで、種類とコードだけが残ることになります。
後は"外字"と">"を補えば目的の文字列になります。
尚、1パターンずつ処理する時は"()"は不要です。
以上の点を踏まえて、先に示したのコードを修正して下さい。

正規表現は「慣れ」が必要で、文献など読んでも実際に
「練習」を繰り返さないと身に付かないものです。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

とても親切なご説明ありがとうございました。
前回のソースコードから今回のご説明であったことを踏まえて、
改修することで、実装はもちろん内容も理解することができました。

今まで、本またはサイトなどで知識を磨いていましたが、
やはり実際に作ってみたほうが上達速度は速いですね。

ありがとうございました。

お礼日時:2011/01/10 18:40

私は、ここ最近いつも、Webサイト解析をしているので、以下のような書き方をしてしまいましたが、


>[0-9a-zA-Z]{4}と、[A-z\d]{4}の違いがよくわかりません。

正しくは、ignorecase で、[A-Z\d]{4} でよかったです。私の悪い癖です。問題は発生しないはずですが、他の人から、突っ込まれる前に書いておきます。すみません。

>IgnoreCaseは、大文字と小文字を区別するオプションだったと思うのですが、
それは逆です。Ignore は、無視するという意味ですから、CASE =Caps を Ignore 無視するという意味です。

Dim r As New System.Text.RegularExpressions.Regex( _
"[A-Z0-9]+")
', _ System.Text.RegularExpressions.RegexOptions.IgnoreCase)
で、AaBbCcDd を検索してみればその違いは分かるはずです。

$1$2は、正規表現のパターンの() で囲った順番です。本来は、同時に取れますが、元のご質問のコードを生かしました。

正規表現の置換はここにあります。
http://msdn.microsoft.com/ja-jp/library/ewy2t5e0 …
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

IgnoreCaseについてもう一度調べてみましたが、
ご指摘の通り、大文字小文字を区別しないオプションでした。

$1$2についても、MSDNライブラリで確認し理解することができました。

正規表現の置換については、もう一度勉強したいと思います。

ありがとうございました。

お礼日時:2011/01/10 18:44

たぶん、現実は、その部分を抜き出すところが肝心な所になるのではないかと思いますが、関数を修正してみました。

ところで、これって16進の文字コードですよね。[0-9a-zA-Z]{4}
IgnoreCaseを入れているわけだから、[A-z\d]{4} じゃないかしら。

質問コードをそのまま直してみました。

Private Function GaijiChange(ByVal pNaiyo As String) As String
  Dim re As New System.Text.RegularExpressions.Regex( _
    "<gaiji\s*gaijisyurui=""([A-z\d]{4})""\s*gaijicode=""([A-z\d]{4})""\s*/>", _
    System.Text.RegularExpressions.RegexOptions.IgnoreCase)
  Dim wNaiyo As String = "" ' 置換後文字列
  Dim wGaijisyurui As String = ""
  Dim wGaijicode As String = ""
  Dim wChangeWord As String = ""
  Dim wChangeStr As String = ""
  Dim Work As String = ""
  Dim wMc As System.Text.RegularExpressions.MatchCollection = re.Matches(pNaiyo)
  wNaiyo = pNaiyo
  For Each m As System.Text.RegularExpressions.Match In wMc
    wGaijisyurui = re.Replace(m.Value, "$1")
    wGaijicode = re.Replace(m.Value, "$2")
    wChangeWord = "<外字" & wGaijisyurui & wGaijicode & ">"
    wNaiyo = wNaiyo.Replace(m.Value, wChangeWord) '※
  Next
  Return wNaiyo
End Function
    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。

正規表現でのご指摘ありがとうございます。
当方、正規表現初心者なので間違った解釈をしているかもしれませんが、
IgnoreCaseは、大文字と小文字を区別するオプションだったと思うのですが、
[0-9a-zA-Z]{4}と、[A-z\d]{4}の違いがよくわかりません。
どちらも数字と英字4文字ということではないのでしょうか?

ご提示いただいたソースの動作確認が取れました。
とてもすっきりしていて、非常にわかりやすいソースでした。
ところで、wGaijisyurui = re.Replace(m.Value, "$1")
     wGaijicode = re.Replace(m.Value, "$2")
"$1"と"S2"とはいったい何なのでしょうか?GoogleやMSDNで調べて
色々な所で使われていたのですが、意味自体は分かりませんでした。
"S1"は1つ目の([A-z\d]{4})グループの内容
"S2"は2つ目の([A-z\d]{4})グループの内容
ということでしょうか?

質問ばかりで申し訳ありません、ありがとうございました。

お礼日時:2011/01/10 03:40

先ずタグ部分を抜き出し、変換して結合させます。



Function GaijiChange(ByVal 文字列 As String) As String
  '★タグ部分を抜き出す正規表現
  Dim 検索表現 As Regex = New Regex("<[^>]+?>")
  '★タグ内部の除去したい文字列を表わす正規表現
  Dim 置換表現 As Regex = New Regex( _
  "(<\s*gaiji\s+gaijisyurui\s*=\s*"")|" & _
  "(""\s+gaijicode\s*=\s*"")|" & _
  "(""\s*/\s*)", RegexOptions.IgnoreCase)

  Dim 一致 As Match  '★タグ部分を表わすオブジェクト
  Dim 戻り値 As String = "" '★戻り値用文字列
  Dim 前回位置 As Integer = 0 '★前回処理した文字位置

  '★タグ部分を抜き出してループする
  For Each 一致 In 検索表現.Matches(文字列) '一致集合
    '★タグとタグの間の文字列を結合させる
    戻り値 = 戻り値 & 文字列.Substring(前回位置, _
                    一致.Index + 1 - 前回位置)
    '★処理位置を更新する
    前回位置 = 一致.Index + 一致.Length
    '★"外字"とタグ内の不要な文字を除去して結合
    戻り値 = 戻り値 & "外字" & _
    置換表現.Replace(一致.Value, "")
  Next
  '★最後のタグの後方を結合する
  戻り値 = 戻り値 & 文字列.Substring(前回位置)
  '★戻り値を返す
  Return 戻り値
End Function

ポイント
(1)一致.Index + 1 - 前回位置
 これで1文字多く繋がる。1文字とは"<"である。
(2)置換表現
 要するに種類とコード以外の部分を全部除去する。
 パターンが3個あるのでOR結合させた。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。

文字列の処理位置を取得しつつ置換作業をしていくのは
まったく頭にありませんでした…。

ご提示いただいたソースで、動作の確認がとれました。
当方の勉強不足もあって、処理内容がまだ完ぺきにわかっていませんが
理解できるよう頑張りたいと思います。

また、アドバイスもいただきありがとうございました。

お礼日時:2011/01/10 03:26

VBAじゃ無さそうですが、VBAでやってみます。


ご提示のコードを少しひねって下記の様にできます。

Sub test()
Dim regEX As Object, Matches As Variant, match As Variant
Dim buf As String, replaceString As String

buf = "私は、<gaiji gaijisyurui=""0001"" gaijicode=""F040"" /><gaiji gaijisyurui=""0002"" gaijicode=""F041"" />で、<gaiji gaijisyurui=""0003"" gaijicode=""F042"" />です。"
Set regEX = CreateObject("VBScript.RegExp")
With regEX
.MultiLine = False
.Pattern = "<gaiji gaijisyurui=\" & """" & "([0-9a-zA-Z]{4})\" & """" & " gaijicode=\" & """" & "([0-9a-zA-Z]{4})\" & """" & " />"
.ignorecase = True
.Global = True
End With
Set Matches = regEX.Execute(buf)
For Each match In Matches
replaceString = "<外字" & match.submatches(0) & match.submatches(1) & ">"
regEX.Pattern = match
buf = regEX.replace(buf, replaceString)
Next match
Set Matches = Nothing
Set regEX = Nothing
Debug.Print buf
End Sub

でも、下記で十分な気もします。ご参考まで。
Sub test2()
Dim buf As String

buf = "私は、<gaiji gaijisyurui=""0001"" gaijicode=""F040"" /><gaiji gaijisyurui=""0002"" gaijicode=""F041"" />で、 <gaiji gaijisyurui=""003"" gaijicode=""F042"" />です。"
buf = replace(buf, "gaiji gaijisyurui=", "外字")
buf = replace(buf, " gaijicode=", "")
buf = replace(buf, """", "")
buf = replace(buf, " /", "")
Debug.Print buf
End Sub
    • good
    • 0
この回答へのお礼

ご回答ありがとうございました。

開発環境を書き忘れてしまいましたが、
当方ではVB2008を使っております。

ただVBAもVBとそこまで変わらなかったせいか、
ご提示いただいたソースを読むことはできました。

参考にして、是非実装まで持っていけるように頑張りたいと思います。

ありがとうございました。

お礼日時:2011/01/10 03:21

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!