import re
test_cases = [
"<div></div>",
"<section id='content'></section>",
'<div class="box" id="main" data-value="123"></div>',
"<div />",
"<section disabled></section>", # 属性値なし
"<p>Paragraph</p>",
"<a href='https://example.com'>Link</a>",
# セルフクローズ
"<br />",
# カスタムタグ
"<custom-tag data-x='123'></custom-tag>",
]
pattern_all = r"<([a-zA-Z0-9-]+)(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))*\s*/?>"
for tag_str in test_cases:
match = re.search(pattern_all, tag_str)
if match:
print(f"Matched tag: {match.group(1)}")
pattern_all = r"<([a-zA-Z0-9-]+)(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))*\s*/?>"
上記のpythonのコードのうち、
変数pattern_allのパターン文字列のうち、
(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))*
の部分の末尾「*」についてですが、
これは0回以上の繰り返しのためのワイルドカードでしょうか?
「?」とはどのような違いがあるのでしょうか?
↓
`*` と `?` の違いは、繰り返し回数に関するルールの違いです。
### `*`(0回以上の繰り返し)
`*` は、直前の要素が **0回以上** 繰り返されることを許可します。
```regex
(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))*
```
この部分の `*` は、
「属性 `key="value"`」のペアを 0回以上 繰り返すことを意味します。
つまり、`<div>` のような属性なしのタグにもマッチし、
`<div class="box" id="main">` のように
複数の属性があるタグにもマッチします。
---
### `?`(0回または1回)
一方、`?` は、
直前の要素が **0回または1回だけ** 出現することを意味します。
例えば、
`(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))?` にすると、
- 属性がない場合(`<div>`)でもOK
- **属性が1つだけある場合** にマッチ
- **2つ以上の属性がある場合はマッチしない**
したがって、
`?` を使うと「属性のあるタグ or ないタグ」には対応できますが、
複数の属性を持つタグにはマッチしません。
---
### `*` と `?` の比較
| 記号 | 意味 | マッチする回数 |
|-------|------|--------------|
| `*` | 0回以上の繰り返し | 0回, 1回, 2回, 3回, ... |
| `?` | 0回または1回のみ | 0回, 1回 |
---
### 例を使った動作の違い
#### `*` を使う場合
```python
import re
pattern = r"<([a-zA-Z0-9-]+)(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))*>"
test_strings = [
"<div>", # 属性なし
# 1つの属性
'<div class="box">',
# 2つの属性
'<div class="box" id="main">',
]
for s in test_strings:
match = re.match(pattern, s)
if match:
print(f"Matched: {s}")
```
**出力**
```
Matched: <div>
Matched: <div class="box">
Matched: <div class="box" id="main">
```
→ `*` なので、属性のない `<div>` も、
1つ以上の属性があるものもマッチする。
---
#### `?` を使う場合
```python
import re
pattern = r"<([a-zA-Z0-9-]+)(\s+[a-zA-Z0-9-]+\s?=\s?(\"[^\"]*\"|'[^']*'))?>"
test_strings = [
"<div>", # 属性なし
'<div class="box">', # 1つの属性
# 2つの属性 (マッチしない)
'<div class="box" id="main">',
]
for s in test_strings:
match = re.match(pattern, s)
if match:
print(f"Matched: {s}")
```
**出力**
```
Matched: <div>
Matched: <div class="box">
```
→ `?` なので、属性なしのタグと、
1つの属性を持つタグにはマッチするが、
2つ以上の属性を持つタグにはマッチしない。
---
### 結論
- `*` は「**属性が0個以上(複数もOK)**」という意味
- `?` は「**属性が0個か1個まで**」という意味
タグの属性を複数サポートしたい場合は
`*` を使うのが適切です。
コメント
コメントを投稿