HiveQL和SparkSQL中的正则

HiveQL和SparkSQL中的正则

1. 正则操作符

  • hive

hive中的正则查找属于关系操作符,rlikeregexp二者用法完全相同。

css 复制代码
a rlike 正则表达式
a regexp 正则表达式
  • spark

spark中正则查找即可以写成关系操作符又可以写成函数形式,在3.2.0之前只有rlike,并且3.2.0之前的文档中写法是str rlike regexp,从3.2.0开始的文档中写成了rlike(str, regexp)形式,但是从实际测试情况看3.2.0之前的版本中也可以写成rlike(str, regexp)形式。

同时spark中支持 str not rlike regexp 的用法

从3.2.0开始增加了regexp用法,格式regexp(str, regexp)。同时增加类似于presto中的regexp_like函数,格式regexp_like(str, regexp)

2. 正则提取

通过 regexp_extract(string subject, string pattern, int index) UDF完成。

  • 函数:regexp_extract(string subject, string pattern, int index)
  • 返回结果:string
  • 使用说明:
    • param1:要匹配的字符串(非字符串也可以)或字段
    • param2:正则表达式
    • param3:是 Java regex Matcher group() 方法索引,表示要返回的部分。0表示把整个正则表达式对应的结果全部返回。

例如,regexp_extract('foothebar', 'foo(.*?)(bar)', 2) 返回 'bar'

请注意,在使用预定义的字符类时需要进行转义,同java中的使用方式 :使用 '\s' 将匹配字母 s'\\s' 将会匹配任何空白字符。

官方示例中的执行结果

sql 复制代码
-- 其中param2表示返回正则表达式中$2的结果,即(bar)对应的结果
> select regexp_extract('foothebar', 'foo(.*?)(bar)', 2);
+------+
| _c0  |
+------+
| bar  |
+------+
-- 如果param2值为0,则返回pattern匹配的整个结果
> select regexp_extract('foothebar', 'foo(.*?)(bar)', 0);
+------------+
|    _c0     |
+------------+
| foothebar  |
+------------+

param1并非只可以是string类型,如下示例中param1的type为int

sql 复制代码
> select regexp_extract(5454310223, '10', 0);
+------+
| _c0  |
+------+
| 10   |
+------+

如果param2的pattern中不包含(),则param3只可以是0,其他值会报错

sql 复制代码
> select regexp_extract('100.034万人民币', '^\\d+\\.*\\d+', 0) as result;
+----------+
|  result  |
+----------+
| 100.034  |
+----------+

> select regexp_extract('100.034万人民币', '^\\d+\\.*\\d+', 1) as result;
Error: Error while compiling statement: FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments '1': org.apache.hadoop.hive.ql.metadata.HiveException: Unable to execute method public java.lang.String org.apache.hadoop.hive.ql.udf.UDFRegExpExtract.evaluate(java.lang.String,java.lang.String,java.lang.Integer) with arguments {100.034万人民币,^\d+\.*\d+,1}:No group 1 (state=42000,code=10014)

3. 转义的使用

通过\对特殊符号进行转义,在正则表达式中如何正确使用\

需要使用转义的情形。

情形一:正则表达式中表示特殊含义的符号,如表示匹配任意字符的.,还有?+等。

对于在正则中有特殊含义的符号来说,如果想要正则匹配符号本身,则需要在标点符号前面加上\进行转义,如\.匹配符号.\\匹配符号\\?匹配符号?等等

情形二:正则表达式中预定义了很多带有\的模式,如表示匹配任意数字的\d模式,这里的\是并不表示转义,而是作为特殊符号与d组成一个整体。同类的还有\w匹配字母数字及下划线,\s匹配任意空白字符,\t匹配tab符号等等

在代码中无法直接使用\d来匹配任意数字,因为在代码中单独出现\时表示转义含义,所以代码中单独出现\d时并不表示\d本身,而是表示的是转义符d,d被转义后仍然是d,所以直接只用时匹配的是字母d而不是任意数字。

正确使用:需要再\再加一个\,将\d中的\转移成反斜线本身,即\\d

结论

对于这种本身就带有\符号的模式来说,如果直接使用,则并不是模式本身的含义。在使用时需要在\符号前面再加一个\ 。 如使用\d时,则需要写成\\d才表示\d模型本身的含义;

如果模式本身是不带有\符号的,则在需要转义时直接在前面加一个\就行 。 如需要转义"符号直接写成\"即可(转义符"符号无特殊含义,所以\"还是"本身)。

转义的终极目的是在最终在使用模式的地方保证出现的是模式本身的符号

注意,在hive、spark或presto中正则匹配时是区分大小写的。

sql 复制代码
-- 以下结果为true
select rlike(lower('Nespresso 胶囊咖啡 瑞士进口 意式浓缩黑咖啡胶囊 Nespresso咖啡机适用 Capriccio 10颗装'),'.*capriccio.*')
-- 以下结果为false
select rlike('Nespresso 胶囊咖啡 瑞士进口 意式浓缩黑咖啡胶囊 Nespresso咖啡机适用 Capriccio 10颗装','.*capriccio.*')

3.1. 验证

  • 示例1:
sql 复制代码
> select regexp_extract('wab23cd','(\d)',1);
+------+
| _c0  |
+------+
| d    |
+------+

> select regexp_extract('wab23cd','(\\d)',1);
+------+
| _c0  |
+------+
| 2    |
+------+

> select regexp_extract('wab23cd','(\a)',1);
+------+
| _c0  |
+------+
| a    |
+------+

> select regexp_extract('wab23cd','(\\a)',1);
+------+
| _c0  |
+------+
|      |
+------+

结果解释:

  • 第一个SQL结果返回了字母d,并不是数字2,这是因为直接在代码中写\d所表达的含义是转义符+d,而不是反斜线+d,转义符+d的结果仍然是d,所以在正则匹配时匹配到了d。
  • 第二个SQL结果返回了数字2,这是因为\\d中,第一个\表示转义符,第二个\表示反斜线,第一个将第二个转义成了反斜线字面意思,所以\\d表示达的含义是反斜线+d,是表示匹配数字。
  • 第三个SQL结果返回了字母a,这是因为直接在代码中写\a所表达的含义是转义符+a,转义符+a的结果仍然是a,所以在SQL结果匹配到了a。
  • 第四个SQL结果返回了空,这是因为代码中写的\\a在正则中表示反斜线+a,而反斜线+a在正则中无其他含义,因此SQL结果无匹配结果。

对于\t则稍有不同,参见示例如下

sql 复制代码
> select 'w\tab23d';
+----------+
|   _c0    |
+----------+
| w	ab23d  |
+----------+

> select regexp_extract('w\tab23d','(w\ta)',1);
+------+
| _c0  |
+------+
| w	a  |
+------+

> select regexp_extract('w\tab23d','(w\\ta)',1);
+------+
| _c0  |
+------+
| w	a  |
+------+

注意第二个SQL和第三个SQL结果相同,第二个SQL中\t表示转义符+t转义符+t结果表示tab符号,则结果返回字符串中的制表符。第三个SQL中\\t,第一个\表示转义符,第二个\表示反斜线,第一个将第二个转义成了反斜线字面意思,所以\\t表示达的含义是反斜线+t,在模式中表示匹配制表符。

  • 示例2
sql 复制代码
> select regexp_extract('wab23?cd','(?)',1);
Error: Error while compiling statement: FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments '1': org.apache.hadoop.hive.ql.metadata.HiveException: Unable to execute method public java.lang.String org.apache.hadoop.hive.ql.udf.UDFRegExpExtract.evaluate(java.lang.String,java.lang.String,java.lang.Integer) with arguments {wab23?cd,(?),1}:No group 1 (state=42000,code=10014)

> select regexp_extract('wab23?cd','(\?)',1);
Error: Error while compiling statement: FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments '1': org.apache.hadoop.hive.ql.metadata.HiveException: Unable to execute method public java.lang.String org.apache.hadoop.hive.ql.udf.UDFRegExpExtract.evaluate(java.lang.String,java.lang.String,java.lang.Integer) with arguments {wab23?cd,(?),1}:No group 1 (state=42000,code=10014)

> select regexp_extract('wab23?cd','(\\?)',1);
+------+
| _c0  |
+------+
| ?    |
+------+

结果解释:

第一个SQL和第二个SQL都发生了报错,在正则表达式中?具有特殊含义,并且不会单独出现,所以SQL报错,第二个SQL写成\?表示转义符+?,而?被转义后还是?,所以和第一个SQL和第二个SQL报相同的错误。

在正则匹配中想要匹配?,本身需要的是\?(反斜线+?),第三个sql中,第一个反\被当成转义符将第二个\转义成反斜线字面含义,因此\\?表达的是反斜线+?的含义,从而实现正则匹配问号。

  • 示例3
sql 复制代码
> select regexp_extract('wab23?c.d','(.)',1);
+------+
| _c0  |
+------+
| w    |
+------+

> select regexp_extract('wab23?c.d','(\.)',1);
+------+
| _c0  |
+------+
| w    |
+------+

> select regexp_extract('wab23?c.d','(\\.)',1);
+------+
| _c0  |
+------+
| .    |
+------+

结果解释同示例2。

  • 示例4
sql 复制代码
> select 'w ab\23?c.d';
+-------------+
|     _c0     |
+-------------+
| w ab23?c.d  |
+-------------+

> select 'w ab\\23?c.d';
+--------------+
|     _c0      |
+--------------+
| w ab\23?c.d  |
+--------------+

> select regexp_extract('w ab\\23?c.d','(\)',1);
+------+
| _c0  |
+------+
|      |
+------+

> select regexp_extract('w ab\\23?c.d','(\\)',1);
Error: Error while compiling statement: FAILED: SemanticException Line 1:7 Wrong arguments '1': org.apache.hadoop.hive.ql.metadata.HiveException: Unable to execute method public java.lang.String org.apache.hadoop.hive.ql.udf.UDFRegExpExtract.evaluate(java.lang.String,java.lang.String,java.lang.Integer) with arguments {w ab23?c.d,(\),1}:Unclosed group near index 3
(\) (state=42000,code=40000)

> select regexp_extract('w ab\\23?c.d','(\\\\)',1);
+------+
| _c0  |
+------+
| \    |
+------+

结果解释:

第三个SQL结果为空,直接在正则表达式中写\表示转义空字符串,而空字符串转义后仍为空字符串,所以无结果。

第四个SQL结果报错,是因为写成\\,第一个\将第二个\转义成反斜线字面含义,而在正则表达式中单独的一个反斜线无法直接使用,因此报错。 正则表达式中想要匹配反斜线,本身需要的是\\(反斜线+反斜线),第五个SQL中,第一个\将第二个\转义成反斜线,第三个\将第四个\转义成反斜线,因此代码中写的\\\\表示的含义的反斜线+反斜线,因此结果正则匹配到反斜线。

注意:在presto引擎中上述规则并不适用,在presto中使用自身带有\符合的模式,代码中直接使用即可,并不需要其他额外处理。

相关推荐
快手技术7 小时前
Blaze RangePartitioning 算子Native实现全解析
spark·naive
知初~1 天前
出行项目案例
hive·hadoop·redis·sql·mysql·spark·database
努力的小T1 天前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
Java资深爱好者1 天前
在Spark中,如何使用DataFrame进行高效的数据处理
大数据·分布式·spark
阿里云大数据AI技术2 天前
美的楼宇科技基于阿里云 EMR Serverless Spark 构建 LakeHouse 湖仓数据平台
大数据·阿里云·spark·serverless·emr
python资深爱好者2 天前
什么容错性以及Spark Streaming如何保证容错性
大数据·分布式·spark
猪猪果泡酒2 天前
spark
spark
weixin_307779132 天前
PySpark实现MERGE INTO的数据合并功能
大数据·python·spark
lucky_syq3 天前
Spark算子:大数据处理的魔法棒
大数据·分布式·spark
D愿你归来仍是少年3 天前
解决Python升级导致PySpark任务异常方案
大数据·开发语言·python·spark