Hive 正则函数详解与示例
目录
- 概述
- [Hive 中的正则表达式基础](#Hive 中的正则表达式基础)
2.1 元字符速查
2.2 捕获组与非捕获组
2.3 [⚠️ 双重转义规则(核心难点)](#⚠️ 双重转义规则(核心难点))- 正则函数详解与示例
3.1 [RLIKE/REGEXP-- 模式匹配](#RLIKE / REGEXP – 模式匹配)
3.2 [REGEXP_EXTRACT-- 提取首个匹配](#REGEXP_EXTRACT – 提取首个匹配)
3.3 [REGEXP_EXTRACT_ALL-- 提取所有匹配(数组)](#REGEXP_EXTRACT_ALL – 提取所有匹配(数组))
3.4 [REGEXP_REPLACE-- 替换匹配内容](#REGEXP_REPLACE – 替换匹配内容)
3.5 [REGEXP_COUNT-- 统计匹配次数](#REGEXP_COUNT – 统计匹配次数)- 实战案例
4.1 [日志解析 -- 提取 IP、状态码](#日志解析 – 提取 IP、状态码)
4.2 [数据脱敏 -- 手机号、身份证](#数据脱敏 – 手机号、身份证)
4.3 清洗非数字字符
4.4 [URL 解析 -- 提取域名和参数](#URL 解析 – 提取域名和参数)- 性能与最佳实践
- 常见陷阱与避坑指南
- 附录:常用正则表达式模式
1. 概述
Hive 提供了基于 Java 正则表达式 的内置函数,用于处理字符串的匹配、提取、替换和计数。这些函数在数据清洗、日志解析、格式校验、敏感信息脱敏等场景中非常实用。
Hive 中的正则函数包括:
| 函数名 | 功能 |
|---|---|
RLIKE / REGEXP |
判断字符串是否匹配正则(返回布尔值) |
REGEXP_EXTRACT |
提取第一个匹配的子串(支持捕获组) |
REGEXP_EXTRACT_ALL |
提取所有匹配的子串(返回数组) |
REGEXP_REPLACE |
替换匹配正则的部分 |
REGEXP_COUNT |
统计匹配正则的次数 |
注意 :
REGEXP_EXTRACT_ALL和REGEXP_COUNT在 Hive 1.2.0+ 版本才支持,低版本需升级或使用其他方法。
2. Hive 中的正则表达式基础
2.1 元字符速查
| 元字符 | 含义 | 示例 |
|---|---|---|
. |
任意单个字符(除换行符) | a.c 匹配 abc、a7c |
* |
前一个元素出现 0 次或多次 | ab*c 匹配 ac、abc、abbc |
+ |
前一个元素出现 1 次或多次 | ab+c 匹配 abc、abbc,不匹配 ac |
? |
前一个元素出现 0 次或 1 次 | ab?c 匹配 ac、abc |
| ` | ` | 或 |
^ |
字符串开头 | ^[0-9] 匹配以数字开头的字符串 |
$ |
字符串结尾 | [0-9]$ 匹配以数字结尾的字符串 |
\d |
数字 [0-9] |
\d+ 匹配一个或多个数字 |
\D |
非数字 | \D+ 匹配非数字字符 |
\w |
单词字符 [A-Za-z0-9_] |
\w+ 匹配单词 |
\W |
非单词字符 | \W+ 匹配标点、空格等 |
\s |
空白字符(空格、制表符等) | \s+ 匹配连续空白 |
\S |
非空白字符 | \S+ 匹配非空字符序列 |
2.2 捕获组与非捕获组
- 捕获组
( ):将括号内的匹配内容保存到组中,可通过$1、$2引用(在REGEXP_REPLACE中)或通过index参数提取(在REGEXP_EXTRACT中)。 - 非捕获组
(?: ):仅用于分组或量词作用域,不保存匹配内容。
示例:
sql
-- 捕获组:提取域名中的主体部分
SELECT REGEXP_EXTRACT('www.example.com', 'www\\.(.*?)\\.com', 1); -- 返回 'example'
2.3 ⚠️ 双重转义规则(核心难点)
Hive 中,正则表达式是写在字符串里的 ,Hive SQL 解析器会先对字符串进行一层转义,之后正则引擎再解析一次。因此,所有 Java 正则中的反斜杠 \ 在 Hive 中必须写成 \\。
| 你想匹配的字符 | Java 正则写法 | Hive SQL 中的写法 |
|---|---|---|
数字 \d |
\d |
'\\d' |
点号 .(字面量) |
\. |
'\\.' |
一个反斜杠 \ |
\\ |
'\\\\' |
单词边界 \b |
\b |
'\\b' |
错误示例(忘记转义):
sql
SELECT REGEXP_EXTRACT('123abc', '\d+', 0); -- 报错或返回 NULL
正确写法:
sql
SELECT REGEXP_EXTRACT('123abc', '\\d+', 0); -- 返回 '123'
3. 正则函数详解与示例
3.1 RLIKE / REGEXP -- 模式匹配
语法:
sql
string RLIKE pattern -- pattern 为正则表达式字符串
string REGEXP pattern -- 等价于 RLIKE
返回值 :BOOLEAN(TRUE / FALSE)
示例:
示例 1:判断字符串是否为纯数字
sql
SELECT '12345' RLIKE '^\\d+$'; -- 返回 TRUE
SELECT '12a45' RLIKE '^\\d+$'; -- 返回 FALSE
示例 2:筛选出邮箱为 @gmail.com 结尾的用户
sql
SELECT name, email
FROM users
WHERE email RLIKE '@gmail\\.com$';
示例 3:结合 NOT RLIKE 排除包含特殊字符的昵称
sql
SELECT nickname
FROM users
WHERE nickname NOT RLIKE '[!@#$%]';
示例 4:在 CASE WHEN 中使用
sql
SELECT comment,
CASE WHEN comment RLIKE '(垃圾|广告|色情)' THEN '违规'
ELSE '正常'
END AS tag
FROM forum_posts;
3.2 REGEXP_EXTRACT -- 提取首个匹配
语法:
sql
REGEXP_EXTRACT(string subject, string pattern, int index)
pattern:正则表达式,可包含捕获组()index:0:返回整个匹配的子串1,2,3...:返回第 N 个捕获组的内容- 若未匹配到,返回空字符串
''
示例:
示例 1:提取手机号中间四位
sql
SELECT REGEXP_EXTRACT('13812345678', '1(\\d{4})\\d{4}', 1); -- 返回 '1234'
正则解释:1 后跟 4 个数字(捕获组),再跟 4 个数字(不捕获)。
示例 2:提取 URL 中的域名
sql
SELECT REGEXP_EXTRACT('https://www.example.com/path', 'https?://(?:[^/]+\\.)?([^/]+)', 1);
-- 返回 'example.com'
示例 3:提取日期中的年、月、日
sql
SELECT REGEXP_EXTRACT('2026-04-16', '(\\d{4})-(\\d{2})-(\\d{2})', 1) AS year, -- 2026
REGEXP_EXTRACT('2026-04-16', '(\\d{4})-(\\d{2})-(\\d{2})', 2) AS month, -- 04
REGEXP_EXTRACT('2026-04-16', '(\\d{4})-(\\d{2})-(\\d{2})', 3) AS day; -- 16
3.3 REGEXP_EXTRACT_ALL -- 提取所有匹配(数组)
语法:
sql
REGEXP_EXTRACT_ALL(string subject, string pattern)
返回值 :array<string>,包含所有匹配 pattern 的子串(注意:该函数不支持捕获组,总是返回整个匹配)。
示例:
示例 1:提取字符串中所有数字
sql
SELECT REGEXP_EXTRACT_ALL('abc123def456ghi789', '\\d+');
-- 返回 ['123', '456', '789']
示例 2:配合 LATERAL VIEW EXPLODE 展开数组
sql
SELECT word
FROM (
SELECT REGEXP_EXTRACT_ALL('Hello, world! Hive is great.', '\\w+') AS words
) t
LATERAL VIEW EXPLODE(words) w AS word;
-- 返回多行:Hello, world, Hive, is, great
示例 3:提取所有邮箱地址
sql
SELECT REGEXP_EXTRACT_ALL('Contact: a@b.com, support@example.org', '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}');
-- 返回 ['a@b.com', 'support@example.org']
3.4 REGEXP_REPLACE -- 替换匹配内容
语法:
sql
REGEXP_REPLACE(string subject, string pattern, string replacement)
- 将
subject中所有匹配pattern的子串替换为replacement replacement中可以包含$1、$2引用捕获组的内容
示例:
示例 1:手机号脱敏(保留前三后四,中间用 **** 替换)
sql
SELECT REGEXP_REPLACE('13812345678', '(\\d{3})\\d{4}(\\d{4})', '$1****$2');
-- 返回 '138****5678'
示例 2:清洗字符串中的非数字字符
sql
SELECT REGEXP_REPLACE('订单号:ORD12345,金额:$99.9', '[^0-9]', '');
-- 返回 '12345999'(注意:会连小数点也去掉)
示例 3:将日期格式从 yyyy/MM/dd 转换为 yyyy-MM-dd
sql
SELECT REGEXP_REPLACE('2026/04/16', '/', '-');
-- 返回 '2026-04-16'
示例 4:使用捕获组交换位置(如将 last, first 转为 first last)
sql
SELECT REGEXP_REPLACE('Doe, John', '(\\w+),\\s+(\\w+)', '$2 $1');
-- 返回 'John Doe'
3.5 REGEXP_COUNT -- 统计匹配次数
语法:
sql
REGEXP_COUNT(string subject, string pattern)
返回值 :INT,表示 pattern 在 subject 中出现的次数。
示例:
示例 1:统计字符串中数字的个数
sql
SELECT REGEXP_COUNT('abc123def456', '\\d'); -- 返回 6(每个数字单独计数)
SELECT REGEXP_COUNT('abc123def456', '\\d+'); -- 返回 2("123" 和 "456" 各一次)
示例 2:统计单词个数(按非字母分隔)
sql
SELECT REGEXP_COUNT('Hello, world! Hive is powerful.', '\\w+'); -- 返回 5
示例 3:校验密码复杂度(至少包含大写、小写、数字)
sql
SELECT password,
CASE WHEN REGEXP_COUNT(password, '[A-Z]') >= 1
AND REGEXP_COUNT(password, '[a-z]') >= 1
AND REGEXP_COUNT(password, '[0-9]') >= 1
THEN '强' ELSE '弱' END AS pwd_strength
FROM users;
4. 实战案例
4.1 日志解析 -- 提取 IP、状态码
假设有一条 Nginx 日志:
192.168.1.1 - - [16/Apr/2026:10:30:00 +0800] "GET /index.html HTTP/1.1" 200 1024
sql
WITH log_line AS (
SELECT '192.168.1.1 - - [16/Apr/2026:10:30:00 +0800] "GET /index.html HTTP/1.1" 200 1024' AS raw
)
SELECT REGEXP_EXTRACT(raw, '^(\\S+)', 1) AS ip, -- IP
REGEXP_EXTRACT(raw, '\\[(.*?)\\]', 1) AS time_str, -- 时间
REGEXP_EXTRACT(raw, '"(.*?)"', 1) AS request, -- 请求
REGEXP_EXTRACT(raw, '\\s(\\d{3})\\s', 1) AS status_code -- 状态码
FROM log_line;
4.2 数据脱敏 -- 手机号、身份证
sql
-- 手机号脱敏:保留前3后4
SELECT REGEXP_REPLACE(mobile, '(\\d{3})\\d{4}(\\d{4})', '$1****$2') AS masked_mobile
FROM users;
-- 身份证脱敏:保留前6后4
SELECT REGEXP_REPLACE(id_card, '(\\d{6})\\d{8}(\\d{4})', '$1********$2') AS masked_id
FROM user_identity;
4.3 清洗非数字字符
从混合字段中提取纯数字(例如提取金额):
sql
SELECT REGEXP_REPLACE(price_str, '[^0-9.]', '') AS clean_price
FROM products;
-- 注意:可能保留多个小数点,需要进一步处理
更严谨的金额提取(提取第一个数字或小数):
sql
SELECT REGEXP_EXTRACT(price_str, '(\\d+(\\.\\d+)?)', 1) AS amount
FROM products;
4.4 URL 解析 -- 提取域名和参数
sql
WITH urls AS (
SELECT 'https://www.example.com:8080/path?id=123&name=test' AS url
)
SELECT REGEXP_EXTRACT(url, '://([^/]+)', 1) AS domain, -- www.example.com:8080
REGEXP_EXTRACT(url, '\\?(.*)', 1) AS params -- id=123&name=test
FROM urls;
5. 性能与最佳实践
-
能用简单函数就不写正则
- 判断前缀:
LIKE 'abc%'优于RLIKE '^abc' - 提取固定位置:
SUBSTR(col, 2, 3)优于REGEXP_EXTRACT
- 判断前缀:
-
避免在大表的
WHERE子句中使用复杂正则正则匹配消耗 CPU,若必须使用,尽量先用分区或其他条件过滤掉大部分数据。
-
警惕灾难性回溯
复杂的嵌套量词(如
(a+)*b)可能导致匹配时间指数级增长。应使用更具体的正则或拆分逻辑。 -
预编译正则(Hive 内部优化)
Hive 会对常量正则字符串进行预编译,因此尽量将正则写成字面量,而不是由列值动态拼接。
-
使用
LATERAL VIEW配合REGEXP_EXTRACT_ALL时注意数据膨胀如果数组很大,
EXPLODE会产生大量行,可能降低性能。
6. 常见陷阱与避坑指南
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 忘记双重转义 | \d 写成 '\\d' |
牢记:每个 \ 变 \\ |
REGEXP_EXTRACT 索引超出捕获组 |
正则中只有 1 个 (),却写 index=2 |
检查捕获组个数,索引从 1 开始 |
REGEXP_EXTRACT_ALL 不支持捕获组 |
该函数总是返回整个匹配,不能单独提取组内内容 | 若需提取组,可改用 REGEXP_EXTRACT 配合循环或多次调用 |
NULL 值处理 |
NULL RLIKE '.*' 返回 NULL 而非 FALSE |
使用 COALESCE(col, '') 将 NULL 转为空串 |
| 贪婪匹配导致结果过长 | '.*' 会匹配尽可能多的字符 |
使用非贪婪 '.*?' 或明确边界 |
| 版本兼容性 | 低版本 Hive 不支持 REGEXP_COUNT 和 REGEXP_EXTRACT_ALL |
升级或使用其他方式(如 LATERAL VIEW + parse_url 等) |
7. 附录:常用正则表达式模式
| 用途 | 正则表达式(Hive 写法) |
|---|---|
| 手机号(简单) | '1[3-9]\\d{9}' |
| 邮箱 | '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}' |
| IPv4 地址 | '\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}' |
日期 yyyy-MM-dd |
'\\d{4}-\\d{2}-\\d{2}' |
时间 HH:mm:ss |
'\\d{2}:\\d{2}:\\d{2}' |
| 纯数字 | '^\\d+$' |
| 纯字母 | '^[A-Za-z]+$' |
| URL 协议 | '^https?://' |
| HTML 标签 | '<[^>]+>' |
总结 :掌握 Hive 正则函数的关键在于理解双重转义规则和捕获组的用法。从简单的 RLIKE 匹配,到 REGEXP_EXTRACT 精准提取,再到 REGEXP_REPLACE 脱敏清洗,配合实战案例多加练习,即可高效处理各类文本数据。