[实践OK]PHP正则实现天气预报UL表格及其里面天气信息内容获取。

jackxiang 2024-6-18 15:58 | |
捕获组与非捕获组概念:https://segmentfault.com/a/1190000021043947
文章中的工具用的正则表达式公式: https://regex101.com/
非捕获部分:(?:.*?<span title="(.*?)" .*?<\/span>)?




对天气中的温度和风力有时间只有一个温度和风力的情况的判断算法:
温度部分:
    $temperature = '';
    if (preg_match('/<span>(.*?)<\/span>\/<i>(.*?)<\/i>/', $matches[4], $temp_matches)) {
        $temperature = $temp_matches[1] . '/' . $temp_matches[2];
    } else if (preg_match('/<i>(.*?)<\/i>/', $matches[4], $temp_matches)) {
        $temperature = $temp_matches[1];
    }

风力部分:
问ChatGPT:我只想知道正则如何匹配到下面这段的:

<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i>&lt;3级</i>
</p>
对下面这段正则作下解释:

<p class="win">.*?<span title="(.*?)" .*?<\/span>(?:.*?<span title="(.*?)" .*?<\/span>)?
上面第一个.*?是非贪婪匹配它后面的:<span title=。
上在第二个(.*?)是匹配南风。
上在第三个.*?是非贪婪匹配它后面的:<\/span>。
是对的吧?
二、最想知道的是:
(?:.?<span title="(.?)" .?</span>)?
上在这段正则里面的:?:.? 是想干嘛的呢?
它最后的问号是用来匹配0次或1次用的,这个是这么理解吧。

ChatGPT回答如下:
你说得基本是对的。让我逐步解释一下你提到的正则表达式是如何匹配 `<p class="win">` 这段 HTML 代码的,以及 `(?:.*?<span title="(.*?)" .*?<\/span>)?` 这部分正则表达式的具体作用。

### 1. 正则表达式整体

```regex
<p class="win">.*?<span title="(.*?)" .*?<\/span>(?:.*?<span title="(.*?)" .*?<\/span>)?
```

#### 1.1. `<p class="win">.*?<span title="(.*?)" .*?<\/span>`

- `<p class="win">`: 匹配 `<p>` 标签的开头部分,class 属性为 `win`。
- `.*?`: 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- `<span title="(.*?)"`: 匹配第一个 `span` 标签,并捕获 `title` 属性的值。`(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容,这里会匹配到 `南风`。
- ` .*?<\/span>`: 非贪婪模式匹配任意字符,直到遇到 `</span>`。

这一部分的正则表达式解释如下:
- `<p class="win">` 精确匹配标签的开始部分。
- `.*?` 尽量少的匹配任意字符,直到遇到 `<span title=`。
- `<span title="(.*?)"` 匹配 `span` 标签的 `title` 属性,捕获组 `(.*?)` 非贪婪模式匹配 `title` 的内容,即 `南风`。
- ` .*?<\/span>` 匹配从 `title` 属性结束到 `</span>` 标签结束的所有字符。

#### 1.2. `(?:.*?<span title="(.*?)" .*?<\/span>)?`

- `(?: ... )`: 非捕获组,匹配括号内的内容,但不捕获它。
- `.*?`: 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- `<span title="(.*?)"`: 匹配第二个 `span` 标签,并捕获 `title` 属性的值。`(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容,这里会匹配到第二个 `南风`。
- ` .*?<\/span>`: 非贪婪模式匹配任意字符,直到遇到 `</span>`。
- `?`: 匹配前面的非捕获组 0 次或 1 次。即这部分是可选的。

这一部分的正则表达式解释如下:
- `(?: ... )` 表示一个非捕获组,不会保存匹配结果。
- `.*?` 尽量少的匹配任意字符,直到遇到 `<span title=`。
- `<span title="(.*?)"` 匹配 `span` 标签的 `title` 属性,捕获组 `(.*?)` 非贪婪模式匹配 `title` 的内容,即第二个 `南风`。
- ` .*?<\/span>` 匹配从 `title` 属性结束到 `</span>` 标签结束的所有字符。
- `?` 表示这个非捕获组是可选的,可以匹配 0 次或 1 次。

### 2. 匹配示例

以你提供的 HTML 代码为例:

```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i>&lt;3级</i>
</p>
```

- `<p class="win">`: 匹配 `<p class="win">`。
- `.*?`: 非贪婪模式匹配任意字符,直到遇到 `<span title=`。匹配到 `\n<em>\n`。
- `<span title="(.*?)"`: 匹配 `<span title="南风"` 并捕获 `title` 属性的值 `南风`。
- ` .*?<\/span>`: 匹配 `class="S"></span>\n`。

接下来是可选部分:

- `(?:.*?<span title="(.*?)" .*?<\/span>)?`:
  - `.*?`: 非贪婪模式匹配任意字符,直到遇到 `<span title=`。匹配到 `\n`。
  - `<span title="(.*?)"`: 匹配 `<span title="南风"` 并捕获 `title` 属性的值 `南风`。
  - ` .*?<\/span>`: 匹配 `class="S"></span>\n`。
  - `?`: 表示这个部分是可选的,可以匹配 0 次或 1 次。这次匹配到 1 次。

### 总结

- `<p class="win">.*?<span title="(.*?)" .*?<\/span>`: 匹配 `<p class="win">` 开始标签,直到第一个 `span` 标签的 `title` 属性,并捕获其值。
- `(?:.*?<span title="(.*?)" .*?<\/span>)?`: 匹配从第一个 `span` 标签结束到第二个 `span` 标签结束的内容,并捕获第二个 `span` 标签的 `title` 属性,整个部分是可选的。
- `?` 表示前面的非捕获组是可选的,可以出现 0 次或 1 次。



再对正则知识点细节性问:
(?: ... ) 表示一个非捕获组,不会保存匹配结果。
上面这个是什么意思,请再接合解释下面正则:
(?:.?<span title="(.?)" .*?</span>)?

ChatGPT回答:
好的,让我们详细解释一下 `(?: ... )` 和具体的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?`。

### 非捕获组 `(?: ... )`

- `(?: ... )` 是一个**非捕获组**,它的作用是将括号内的内容进行分组,但不保存这个组的匹配结果。这和普通的捕获组 `(...)` 不同,普通捕获组会保存匹配结果供后续引用,而非捕获组则不会保存。
- 非捕获组主要用于在需要进行分组但不希望保存匹配结果的情况下,这样可以节省内存,并且在使用正则表达式时不需要担心额外的捕获组会影响捕获组编号。

### 具体的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?`

#### 分解解释

1. **`(?: ... )`**:
   - 这是一个非捕获组,括号内的内容会被当作一个整体来处理,但不会保存匹配结果。

2. **`.*?`**:
   - 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
   - 这里匹配的是从 `<p class="win">` 到第一个 `<span title="...">` 之间的任意字符,包括换行符等。

3. **`<span title="(.*?)"`**:
   - 匹配 `<span title="` 字符串,并捕获 `title` 属性的值。
   - `(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容。例如,`南风`。

4. **` .*?<\/span>`**:
   - 非贪婪模式匹配任意字符,直到遇到 `</span>`。
   - 这里匹配的是从 `title` 属性结束到 `</span>` 标签结束的所有字符。

5. **`(?: ... )?`**:
   - 问号 `?` 表示前面的非捕获组是可选的,可以匹配 0 次或 1 次。
   - 整个非捕获组 `(?: ... )` 是可选的,意味着可以有一个额外的 `span` 标签,或者没有。

#### 具体示例

以你的 HTML 代码为例:

```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i>&lt;3级</i>
</p>
```

匹配过程如下:

1. **第一部分** `<p class="win">.*?<span title="(.*?)" .*?<\/span>`:
   - 匹配 `<p class="win">` 开始标签。
   - `.*?` 非贪婪模式匹配任意字符,直到遇到第一个 `<span title=`。
   - 匹配并捕获第一个 `span` 标签的 `title` 属性值 `南风`。
   - 匹配 `</span>` 标签。

2. **第二部分** `(?:.*?<span title="(.*?)" .*?<\/span>)?`:
   - `(?: ... )` 非捕获组,将内容作为整体处理。
   - `.*?` 非贪婪模式匹配任意字符,直到遇到第二个 `<span title=`。
   - 匹配并捕获第二个 `span` 标签的 `title` 属性值 `南风`。
   - 匹配 `</span>` 标签。
   - `?` 表示非捕获组是可选的,可以匹配 0 次或 1 次。

### 完整正则表达式的匹配过程

完整的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?` 在匹配过程中的作用如下:

1. 从 `<p class="win">` 开始,匹配任意字符直到第一个 `<span title=`。
2. 捕获第一个 `span` 标签的 `title` 属性值。
3. 匹配 `</span>` 标签。
4. 非捕获组 `(?: ... )?` 的内容(可选):
   - 匹配任意字符直到第二个 `<span title=`。
   - 捕获第二个 `span` 标签的 `title` 属性值。
   - 匹配 `</span>` 标签。

这段正则表达式会成功匹配以下内容,并分别捕获两个 `span` 标签的 `title` 属性值:

```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i>&lt;3级</i>
</p>
```

结果是捕获两个 `span` 标签的 `title` 属性值 `南风` 和 `南风`。如果只有一个 `span` 标签,那么也能成功匹配,只会捕获第一个 `title` 属性值。

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:http://jackxiang.com/post/12440/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2024-6-18 16:49
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]