「XMLファイルの中から特定のデータを取り出したい」
「タグや属性を一括で置き換えたい」
そんなときに便利なのが正規表現(Regex)です。
ただし、XMLは構造化されたデータなので、正規表現で処理する際には思わぬ落とし穴もあります。
この記事では、XMLを正規表現で扱うときの基本的な使い方、よく使うパターン例、そして注意すべきポイントを初心者にもわかりやすく解説します。
XMLと正規表現の基本的な関係

正規表現とは何か
正規表現(Regular Expression)は、文字列のパターンマッチングを行うための記法です。
テキストの検索、抽出、置換において強力なツールとして活用されています。
XMLの特徴を理解する
XMLを正規表現で処理する前に、XMLの特徴を理解しておくことが重要です:
- 階層構造:タグが入れ子(ネスト)になっている
- 開始タグと終了タグ:
<tag>
と</tag>
がペアになっている - 属性:
<tag attribute="value">
のような形式 - 大文字小文字の区別:XMLは大文字小文字を区別する
- 特殊文字:
<
、>
、&
などのエスケープが必要
XMLを正規表現で扱うときの基本方針

正規表現が適している作業
単純なパターンマッチング
- 特定のタグの内容を抽出
- 属性値の取得や置換
- 単純なテキスト整形
- ログファイルからXML部分を抜き出し
簡単な一括処理
- 同じパターンのタグを一括置換
- 属性名の統一
- 不要なタグの削除
正規表現が適さない作業
複雑な構造解析
- ネストしたタグの正確な解析
- 親子関係の判定
- スキーマに従った厳密な処理
- 名前空間を含むXMLの処理
重要なデータ処理
- 金融データなどミスが許されない処理
- XMLの妥当性検証
- 複雑なデータ変換
XMLパーサとの使い分け
正規表現を使う場面:
- プロトタイピングや調査作業
- 単純で一時的な処理
- ログ解析などテキスト処理寄りの作業
XMLパーサを使う場面:
- 本格的なアプリケーション開発
- 複雑なXML構造の処理
- エラーハンドリングが重要な処理
よく使うXML正規表現パターン

パターン1:特定のタグの内容を抽出
基本形
<tagname>(.*?)</tagname>
具体例:タイトルの抽出
<title>(.*?)</title>
実装例(Python)
import re
xml = """
<article>
<title>XML入門ガイド</title>
<title>正規表現の基本</title>
<content>記事の内容...</content>
</article>
"""
titles = re.findall(r"<title>(.*?)</title>", xml)
print(titles) # → ['XML入門ガイド', '正規表現の基本']
ポイント
(.*?)
:非貪欲マッチで最短一致()
:キャプチャグループで内容を取得?
がないと最長一致になり、複数タグがあるときに問題
パターン2:属性値の抽出
基本形
attribute="([^"]*)"
具体例:IDの抽出
id="([^"]*)"
実装例(JavaScript)
const xml = '<product id="123" name="商品A" price="1000" />';
const idMatch = xml.match(/id="([^"]*)"/);
const nameMatch = xml.match(/name="([^"]*)"/);
console.log(idMatch[1]); // → 123
console.log(nameMatch[1]); // → 商品A
より柔軟な属性抽出
\b(\w+)="([^"]*)"
この方法なら、すべての属性を一度に取得できます:
import re
xml = '<product id="123" name="商品A" price="1000" />'
attributes = re.findall(r'\b(\w+)="([^"]*)"', xml)
print(attributes) # → [('id', '123'), ('name', '商品A'), ('price', '1000')]
パターン3:タグの置換
基本的な置換
# 検索パターン
<old>(.*?)</old>
# 置換パターン
<new>$1</new>
実装例(Python)
import re
xml = "<old>古いデータ</old><old>別のデータ</old>"
new_xml = re.sub(r"<old>(.*?)</old>", r"<new>\1</new>", xml)
print(new_xml) # → <new>古いデータ</new><new>別のデータ</new>
属性付きタグの置換
# より複雑なパターン
xml = '<item status="old">データ</item>'
new_xml = re.sub(r'<item status="old">(.*?)</item>',
r'<item status="new">\1</item>', xml)
パターン4:複数行にまたがるXMLの処理
複数行モードの活用
import re
xml = """<description>
これは複数行にわたる
説明文です。
</description>"""
# re.DOTALLフラグで改行も含めてマッチ
content = re.search(r"<description>(.*?)</description>", xml, re.DOTALL)
print(content.group(1))
パターン5:自己閉じタグの処理
自己閉じタグのパターン
<tagname\s*([^>]*?)\s*/>
実装例
import re
xml = '<img src="image.jpg" alt="画像" /><br /><input type="text" />'
self_closing = re.findall(r'<(\w+)\s*([^>]*?)\s*/>', xml)
print(self_closing) # → [('img', 'src="image.jpg" alt="画像"'), ('br', ''), ('input', 'type="text"')]
エディタ別の正規表現活用法

Visual Studio Code
検索・置換での正規表現
Ctrl + H
で置換ダイアログを開く- 正規表現ボタン(.*)をクリック
- 検索パターンと置換パターンを入力
具体例:タグ名の一括変更
- 検索:
<section>(.*?)</section>
- 置換:
<div>$1</div>
Notepad++
正規表現の有効化
- 検索→置換(Ctrl + H)
- 検索モードで「正規表現」を選択
実用例:属性値の統一
- 検索:
class="oldclass"
- 置換:
class="newclass"
IntelliJ IDEA / PyCharm
強力な正規表現機能
- 構造検索・置換(Structural Search and Replace)
- XMLの構造を理解した高度な置換
オンラインツール
regex101.com
- 正規表現のテストに最適
- マッチ結果をリアルタイムで確認
- 説明機能で正規表現の意味を理解
regexr.com
- 視覚的な正規表現エディタ
- サンプルデータでのテスト機能
注意点と制限事項

正規表現の限界
ネストした構造への対応困難
問題のあるXML例:
<item>
<description>商品説明<note>注記</note>詳細</description>
<item>ネストしたアイテム</item>
</item>
なぜ問題なのか:
- 最初の
<item>
がどの</item>
と対応するかわからない - 正規表現では文脈を理解できない
推奨される対処法
# XMLパーサーを使用(推奨)
import xml.etree.ElementTree as ET
xml_string = """<item>
<description>商品説明<note>注記</note>詳細</description>
<item>ネストしたアイテム</item>
</item>"""
root = ET.fromstring(xml_string)
description = root.find('description').text
よくある間違いとその対策
問題1:貪欲マッチによる誤抽出
間違った正規表現:
<title>(.*)</title>
問題:
<title>タイトル1</title><content>内容</content><title>タイトル2</title>
この場合、「タイトル1」から「タイトル2」までのすべてがマッチしてしまいます。
正しい正規表現:
<title>(.*?)</title>
問題2:名前空間の無視
問題のあるXML:
<ns:book xmlns:ns="http://example.com">
<ns:title>書籍名</ns:title>
</ns:book>
対応方法:
<[^:]*:?title[^>]*>(.*?)</[^:]*:?title>
問題3:特殊文字のエスケープ
問題:
<description><重要>注意事項&規約</description>
対処法:
import html
# XMLエンティティをデコード
content = html.unescape(content)
パフォーマンスの考慮
大きなXMLファイルでの注意点
問題:
- 正規表現は大きなファイルでメモリを大量消費
- 複雑なパターンは処理が遅い
対策:
- ストリーミング処理の検討
- 部分的な処理での分割
- XMLパーサーへの移行
実践的な使用例

例1:ログファイルからエラー情報を抽出
import re
log_content = """
<log level="error" timestamp="2024-01-01 10:00:00">
<message>データベース接続エラー</message>
<details>Connection timeout</details>
</log>
<log level="info" timestamp="2024-01-01 10:01:00">
<message>処理完了</message>
</log>
"""
# エラーレベルのログのみ抽出
errors = re.findall(r'<log level="error"[^>]*>.*?<message>(.*?)</message>.*?</log>',
log_content, re.DOTALL)
print(errors) # → ['データベース接続エラー']
例2:設定ファイルの値を一括変更
import re
config_xml = """
<config>
<database host="localhost" port="3306" />
<cache host="localhost" port="6379" />
<api host="localhost" port="8080" />
</config>
"""
# localhostを本番サーバーに変更
updated_config = re.sub(r'host="localhost"', 'host="production.server.com"', config_xml)
print(updated_config)
例3:HTMLからXMLへの簡易変換
import re
html = '<div class="content"><p>段落1</p><p>段落2</p></div>'
# HTMLタグをXMLタグに変換
xml = re.sub(r'<div class="([^"]*)">', r'<section type="\1">', html)
xml = re.sub(r'</div>', r'</section>', xml)
xml = re.sub(r'<p>', r'<paragraph>', xml)
xml = re.sub(r'</p>', r'</paragraph>', xml)
print(xml)
# → <section type="content"><paragraph>段落1</paragraph><paragraph>段落2</paragraph></section>
XMLパーサーとの比較
処理速度の比較
ファイルサイズ | 正規表現 | XMLパーサー | 推奨 |
---|---|---|---|
小(~1MB) | 高速 | 高速 | 用途次第 |
中(1~10MB) | 普通 | 高速 | XMLパーサー |
大(10MB~) | 遅い | 高速 | XMLパーサー |
安全性の比較
観点 | 正規表現 | XMLパーサー |
---|---|---|
エラーハンドリング | 限定的 | 充実 |
妥当性検証 | なし | あり |
メモリ効率 | 場合による | 最適化済み |
まとめ
XMLを正規表現で扱う際のベストプラクティス
適用場面を見極める
- 単純な抽出・置換:正規表現が効率的
- 複雑な構造解析:XMLパーサーを使用
- 本格的な開発:必ずXMLパーサーを使用
安全な正規表現の書き方
- 非貪欲マッチ(
?
)を活用 - 文字クラスで不正な文字をブロック
- エスケープ処理を忘れずに
- テストデータで十分に検証
パフォーマンス最適化
- シンプルなパターンを心がける
- 部分処理で大きなファイルに対応
- コンパイル済み正規表現を再利用
コメント