htmlの要素にマッチするためのパターンの変数、 pattern_all内のワイルドカードについて 正規表現

 

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個まで**」という意味



タグの属性を複数サポートしたい場合は

 `*` を使うのが適切です。




コメント