<think>
我们首先处理Hadoop相关的两个问题(1和2),然后处理正则表达式问题(3、4、5)。
### 1. Hadoop处理大数据的两个范式及其作用
**答案:**
- **分布式存储(HDFS)**:将大文件分割成块(通常64MB或128MB)并分散存储在集群的多个节点上,通过多副本机制(默认3副本)保障容错性。该方式突破单机存储限制,支持横向扩展。
- **并行计算(MapReduce)**:将计算任务拆分为Map(分散处理)和Reduce(汇总结果)两个阶段,由集群节点并行处理各自的数据块。通过“数据本地化”减少网络传输,极大提升处理效率。
### 2. MapReduce工作原理及示例
**答案:**
MapReduce分为两个阶段:
- **Map阶段**:将输入数据拆分为键值对(key-value pairs),由多个Mapper并行处理。例如统计文档词频时,Mapper将每行文本拆解为`<单词, 1>`的临时键值对。
- **Reduce阶段**:Shuffle过程将相同key的键值对聚合到同一Reducer,Reducer对值进行汇总。延续上例,Reducer输出`<单词, 总次数>`。
**示例代码(伪代码)**:
```python
# Map函数
def map(doc):
for word in doc.split():
emit(word, 1)
# Reduce函数
def reduce(word, counts):
total = sum(counts)
emit(word, total)
```
### 3. 加拿大邮编正则表达式
**要求格式**:
`[A-Z][0-9][A-Z] ?[0-9][A-Z][0-9]`
(第四位空格可选,其他位置严格遵循大小写和数字要求)
**正则表达式**:
`^[A-Z]\d[A-Z] ?\d[A-Z]\d$`
- `^`:匹配行首
- `[A-Z]`:大写字母
- `\d`:数字(等价于[0-9])
- ` ?`:0个或1个空格
- `$`:匹配行尾
**测试结果**:
| 输入 | 是否匹配 | 原因 |
|----------|----------|--------------------------|
| T3Z 3N7 | ✅ | 符合格式 |
| D8R 8C4 | ✅ | 符合格式 |
| RR4 88D | ❌ | 第三位非大写字母(是数字)|
| P9C 3Q4 | ✅ | 符合格式 |
| V2X 3RU | ❌ | 第六位应为大写字母(此处是数字)|
| V5R8S4 | ✅ | **无空格也匹配**(?允许空格可选)|
| M8N 7LK | ❌ | 第七位应为数字(此处是字母)|
| J1M6U4 | ✅ | 无空格,符合格式 |
| S1B 2R9 | ✅ | 符合格式 |
| 88B U2L | ❌ | 首位非大写字母(是数字) |
| D7R 7L2 | ✅ | 符合格式 |
| F9Z6G4 | ✅ | 无空格,符合格式 |
| F3_ 3N7 | ❌ | 第三位应为大写字母(此处是下划线)|
| W4w 4P5 | ❌ | 第三位应为大写字母(此处是小写w)|
| D8R | ❌ | 长度不足 |
| 8C4 | ❌ | 首位非大写字母 |
### 4. 匹配姓氏(last name)以S开头的人名
**要求**:仅匹配姓氏以S开头,且排除名字(given name)以S开头的情况。样本数据格式为`姓氏, 名字`。
**正则表达式**:
`^[^,]*S[^,]*,\s*[^S][^,\n]*$`
- `^[^,]*S[^,]*`:匹配姓氏部分中包含S(且S前无逗号),即姓氏以S开头
- `,\s*`:逗号后可能有空格
- `[^S]`:名字的第一个字符不能是S(排除名字以S开头)
- `[^,\n]*$`:名字剩余部分无逗号或换行
**测试结果**(高亮应匹配:Schmidt, Paul; Sells, Simon; Smith, Peter; Stephens, Sheila):
| 输入 | 是否匹配 | 原因 |
|-------------------------|----------|--------------------------|
| Schmidt, Paul | ✅ | 姓氏以S开头,名字不以S开头 |
| Sells, Simon | ❌ | **注意:名字Simon以S开头,应排除** |
| Smith, Peter | ✅ | 符合要求 |
| Stephens, Sheila | ❌ | 名字Sheila以S开头 |
**修正表达式**:
原需求描述中要求“排除名字以S开头”,但示例高亮了Sells和Stephens(与需求矛盾)。根据问题描述,应只匹配姓氏以S开头且名字不以S开头的行。因此**正确匹配结果只有Schmidt, Paul和Smith, Peter**。
**调整后表达式**:
`^S[^,]*,\s*[^S\n][^,\n]*$`
- `^S`:行首后紧跟S(确保姓氏以S开头)
- `[^,]*`:姓氏剩余部分
- `,\s*`:逗号和空格
- `[^S\n]`:名字第一个字符非S或换行
- `[^,\n]*$`:名字剩余部分
**最终匹配**:
- Schmidt, Paul ✅
- Smith, Peter ✅
- Sells, Simon ❌(名字以S开头)
- Stephens, Sheila ❌(名字以S开头)
### 5. 匹配时间格式 HH:MM AM/PM
**要求**:
- 小时:01-12(允许单数字或双数字,但小时部分不能是0开头的两位数如03)
- 分钟:00-59(必须两位数字)
- 后缀:AM或PM(大小写敏感,必须大写)
**正则表达式**:
`^(1[0-2]|[1-9]):[0-5][0-9] (AM|PM)$`
- `^(1[0-2]|[1-9])`:小时部分为1-9或10-12(排除01、02等前导零格式)
- `:[0-5][0-9]`:分钟必须两位,范围00-59
- ` (AM|PM)$`:空格后接大写的AM或PM
**测试结果**:
| 输入 | 是否匹配 | 原因 |
|-----------|----------|--------------------|
| 12:00 AM | ✅ | 符合要求 |
| 1:30 AM | ✅ | 小时为单数字 |
| 03:40 PM | ❌ | 小时不能有前导零 |
| 04:10 pm | ❌ | pm应为大写 |
| 7:30 am | ❌ | am应为大写 |
| 08:15 AM | ❌ | 小时前导零+非10-12|
| 10:20 PM | ✅ | 符合要求 |
---
### 最终答案整合
**1. Hadoop的两个核心范式**
Hadoop通过以下两种范式处理大数据:
- **分布式文件系统(HDFS)**:将大文件分割存储在多台机器,通过冗余备份提高容错性,解决单机存储瓶颈。
- **MapReduce计算模型**:将任务分解为Map(分布式处理)和Reduce(汇总结果),利用集群并行计算能力加速处理。
**2. MapReduce工作流程示例**
以词频统计为例:
- **Map阶段**:多个Mapper并行读取文本块,每遇到一个单词输出`<单词, 1>`。
- **Reduce阶段**:Reducer接收相同单词的所有计数(如`<apple, [1,1]>`),求和后输出`<apple, 2>`。
**3. 加拿大邮编正则表达式**
正则表达式:`^[A-Z]\d[A-Z] ?\d[A-Z]\d$`
**匹配结果**:T3Z 3N7, D8R 8C4, P9C 3Q4, V5R8S4, J1M6U4, S1B 2R9, D7R 7L2, F9Z6G4
**4. 姓氏以S开头的正则表达式**
正则表达式:`^S[^,]*,\s*[^S\n][^,\n]*$`
**匹配结果**:
- Schmidt, Paul
- Smith, Peter
**5. 时间格式正则表达式**
正则表达式:`^(1[0-2]|[1-9]):[0-5][0-9] (AM|PM)$`
**匹配结果**:
- 12:00 AM
- 1:30 AM
- 10:20 PM
§§相关问题§§
1. 为什么Hadoop的MapReduce模型更适合处理非实时批量数据而非流数据?
2. 如何优化加拿大邮编的正则表达式以同时支持小写字母转换后的校验?
3. 若需匹配姓氏以"S"开头且名字长度超过3个字符的人名,应如何修改正则表达式?
4. 在时间匹配正则表达式中,如何扩展以支持12小时制下的"00:00 AM"格式(小时为00)?
5. 当HDFS集群新增节点时,数据重新均衡(rebalancing)可能引发哪些问题?
</think>
### Hadoop 核心范式解析与正则表达式实战
#### **1. Hadoop 处理大数据的两个关键范式**
- **分布式存储 (HDFS)**
将海量数据分割为块(默认 128MB)分散存储于廉价服务器集群,通过多副本机制保障容错性。避免单点存储瓶颈,支持横向扩展至 PB 级数据。
- **并行计算 (MapReduce)**
将计算任务拆分为 `Map`(分布式处理)和 `Reduce`(结果聚合)阶段。每个节点就近处理本地存储的数据块,大幅减少网络传输开销,实现线性加速比。
#### **2. MapReduce 工作流程示例**
**场景**:统计文档中单词频次
- **Map 阶段**(并行处理):
```python
# 输入:文本行 (key: 行号, value: 文本)
def map(key, value):
for word in value.split():
emit(word, 1) # 输出: (单词, 1)
```
- **Shuffle 阶段**:按单词分组传递数据
- **Reduce 阶段**(聚合结果):
```python
# 输入: (单词, [1,1,...])
def reduce(key, values):
emit(key, sum(values)) # 输出: (单词, 总次数)
```
---
### 正则表达式实现
#### **3. 加拿大邮编匹配**
**正则表达式**:`^[A-Z]\d[A-Z] ?\d[A-Z]\d$`
- **测试结果**:
| 输入 | 匹配 | 原因 |
|-----------|------|---------------|
| `T3Z 3N7` | ✅ | 符合规则 |
| `V5R8S4` | ✅ | 无空格也有效 |
| `RR4 88D` | ❌ | 第三位非字母 |
| `W4w 4P5` | ❌ | 小写字母无效 |
| `D8R` | ❌ | 长度不足 |
#### **4. 匹配姓氏以 S 开头(排除名字以 S 开头)**
**正则表达式**:`^.*, [^S][^,\n]*$`
**原理**:
- `^.*, `:匹配姓氏部分(逗号前全部内容)
- `[^S]`:确保名字首字母**不是 S**
- `[^,\n]*$`:匹配名字剩余部分
**测试结果**:
```
Schmidt, Paul ✅ # 姓氏Schmidt以S开头,名字Paul不以S开头
Sells, Simon ❌ # 名字Simon以S开头(被排除)
Smith, Peter ✅ # 符合规则
Stephens, Sheila ❌ # 名字Sheila以S开头
```
#### **5. 时间格式匹配 (HH:MM AM/PM)**
**正则表达式**:`^(1[0-2]|[1-9]):[0-5]\d [AP]M$`
**规则分解**:
- `(1[0-2]|[1-9])`:小时为 1-12(排除前导零如 03)
- `:[0-5]\d`:分钟为 00-59(必须两位)
- ` [AP]M$`:空格后接 AM 或 PM(大写)
**测试结果**:
```
12:00 AM ✅ 1:30 AM ✅ 10:20 PM ✅
03:40 PM ❌ # 小时前导零无效
04:10 pm ❌ # pm 必须大写
7:30 am ❌ # 分钟缺位且 am 小写
```
---