暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[Hive系列05] 正则匹配抽取&替换字符串

数据分析师的FIRE人生 2021-06-09
6326

点击上方「数据分析师的FIRE人生」→

点击右上角「...」→设为星标


大家好,我是风影楼,一名互联网公司的数据分析师。之前我曾在CSDN编写了Hive系列的付费博客专栏,目前收获了25W次的访问,不过由于CSDN的付费设置无法取消,所以我决定在公众号重新免费分享一遍。这一篇主要介绍Hive中正则表达式的相关函数。

1. 正则表达式简介

1.1 定义

正则表达式(Regular Expression,在代码中常Hive简写为regexp),又称规则表达式,是计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。


1.2 字符构成

正则表达式由一些普通字符和一些元字符组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,我们下面解释。在最简单的情况下,一个正则表达式看上去就是一个普通的查找串。例如,正则表达式"testing"中没有包含任何元字符,它可以匹配"testing"、"testing123"、"123testing"等任何包含"testing"的字符串,但是不能匹配"Testing"。


不过要想真正的用好正则表达式,最重要的是正确的使用各种元字符。下表列出了常用的元字符及对其简短的描述:


元字符

描述

\

转义字符。例如,“\n”代表换行符,如果想表示"\n"这个字符串,需要使用"\\n"来表示,也就是说"\\"代表字符"\"

^

匹配输入字行首。"^a"代表以a开头的任意字符串

$

匹配输入行尾。"a$"代表以a结尾的任意字符串

*

匹配前面的子表达式0次或多次。例如,zo*能匹配“z”,也能匹配“zo”以及“zoo”。*等价于{0,}。

+

匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。

?

匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”。?等价于{0,1}。

{n}

n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配到“Bob”中的“o”,但是能匹配“food”中的两个o。

{n,}

n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。

{n,m}

mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。

?

当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多地匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少地匹配“o”,得到结果 ['o', 'o', 'o', 'o']

.

匹配除“\n”和"\r"之外的任何单个字符。要匹配包括“\n”和"\r"在内的任何字符,请使用像“[\s\S]”的模式。

x|y

匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[z|f]ood”则匹配“zood”或“food”。

[xyz]

字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。

[^xyz]

负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”任一字符。

[a-z]

字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。

注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身.

[^a-z]

负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。

\d

匹配一个数字字符。等价于[0-9]。

\D

匹配一个非数字字符。等价于[^0-9]。

\n

匹配一个换行符。

\r

匹配一个回车符。

\s

匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。

\S

匹配任何可见字符。等价于[^ \f\n\r\t\v]。

\t

匹配一个制表符。

\v

匹配一个垂直制表符。

\w

匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。

\W

匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。

( )将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。


1.3 速记指南

.   [ ]  ^  $ 这四个字符是基础的正则表达式。正则难理解因为里面有一个等价的概念,这个概念大大增加了理解难度,让很多初学者看起来会懵,如果把等价都恢复成原始写法,自己书写正则就超级简单了,就像说话一样去写你的正则了:

等价:
等价是等同于的意思,表示同样的功能,用不同符号来书写。
?,*,+,\d,\w 都是等价字符
  ?等价于匹配长度{0,1}
  *等价于匹配长度{0,} 
  +等价于匹配长度{1,}
  \d等价于[0-9]
       \D等价于[^0-9]
  \w等价于[A-Za-z_0-9]
       \W等价于[^A-Za-z_0-9]


常用运算符与表达式:
  ^ 开始
  () 域段
  [] 包含,默认是一个字符长度
  [^] 不包含,默认是一个字符长度
  {n,m} 匹配长度 
  . 任何单个字符(\. 字符点)
  | 或
  \ 转义
  $ 结尾
  [A-Z] 26个大写字母
  [a-z] 26个小写字母
  [0-9] 0至9数字
       [A-Za-z0-9] 26个大写字母、26个小写字母和0至9数字
  , 分割
  分割语法:
  [A,H,T,W] 包含A或H或T或W字母
  [a,h,t,w] 包含a或h或t或w字母
  [0,3,6,8] 包含0或3或6或8数字


语法与释义:

基础语法 "^([]{})([]{})([]{})$"

正则字符串 = "开始([包含内容]{长度})([包含内容]{长度})([包含内容]{长度})结束" 
  
?,*,+,\d,\w 这些都是简写的,完全可以用[]和{}代替,初学者可以忽略?,*,+,\d,\w一些简写标示符,学会了基础使用再按表自己去等价替换。

  
实例:
字符串:tel:086-0666-88810009999


原始正则:"^tel:[0-9]{1,3}-[0][0-9]{2,3}-[0-9]{8,11}$" 


速记理解:开始 "tel:普通文本"[0-9数字]{1至3位}"-普通文本"[0数字][0-9数字]{2至3位}"-普通文本"[0-9数字]{8至11位} 结束"


等价简写后正则写法:"^tel:\d{1,3}-[0]\d{2,3}-\d{8,11}$" 

2. 正则抽取字符串函数 regexp_extract

regexp_extract(string subject, string pattern, int index)


功能:

将字符串subject按照pattern正则表达式的规则拆分,返回index指定的字符。注意,在有些情况下要使用转义字符。


  • 第一参数 subject:  要处理的字段

  • 第二参数 pattern:  用正则表达式匹配整个字段,然后用括号括出想要抽取的部分

  • 第三个参数 index:

    1. 0是显示与之匹配的整个字符串

    2. 1 是显示第一个括号里面的

    3. 2 是显示第二个括号里面的字段,依此类推

举例:

    当第三个参数取0时,返回匹配到的整个字符串
    hive (app)> select regexp_extract('wwwbaiducom','(www)(b.*)(com)',0);
    wwwbaiducom


    如果第二个参数传入的正则表达式只匹配部分字符串的话,第三个参数传0,只会返回正则表达式能够匹配到的字符串
    hive (app)> select regexp_extract('wwwbaiducom','b.*',0) ;
    baiducom


    当第三个参数取1时,返回匹配到的字符串中第1个括号内所匹配的字符串
    hive (app)> select regexp_extract('wwwbaiducom','(www)(b.*)(com)',1);
    www


    当第三个参数取2时,返回匹配到的字符串中第2个括号内所匹配的字符串
    hive (app)> select regexp_extract('wwwbaiducom','(www)(b.*)(com)',2);
    baidu


    当第三个参数取3时,返回匹配到的字符串中第3个括号内所匹配的字符串
    hive (app)> select regexp_extract('wwwbaiducom','(www)(b.*)(com)',3);
    com


    当第三个参数取4时,因为超出括号的对数3,所以报错
    hive (app)> select regexp_extract('wwwbaiducom','(www)(b.*)(com)',4);
    FAILED: SemanticException [Error 10014]: Line 1:7 Wrong arguments '4': 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)  on object org.apache.hadoop.hive.ql.udf.UDFRegExpExtract@267b678f of class org.apache.hadoop.hive.ql.udf.UDFRegExpExtract with arguments {wwwbaiducom:java.lang.String, (www)(b.*)(com):java.lang.String, 4:java.lang.Integer} of size 3


    截取字符串中的后几个字符,等同于substr('123456',-2)
    hive (app)> select regexp_extract('123456','^(.*)([0-9]{2})$',2);
    56




    3. 正则替换字符串函数 regexp_replace

    regexp_replace(string initial_string, string pattern, string replacement)


    功能:

    将字符串initial_string中的符合正则表达式pattern的部分替换为replacement。注意,在有些情况下要使用转义字符。


    • 第一参数 initial_string:  要处理的字段

    • 第二参数 pattern:   正则表达式匹配字段中想要替换的部分

    • 第三个参数 replacement:替换成的字符串

    举例:

      将字符串中的baidu替换为.google.
      hive (app)> select regexp_replace('wwwbaiducom','baidu','.google.');
      www.google.com

      4. 不同运行环境下的转义处理

      有时正则匹配需要对一些特殊字符进行转义处理,比如 . 在正则表达式中表示一个任意字符,如果想在正则表达式中表示.这个字符串,就需要使用\进行转义,但是在Hive的CLI环境下,\也需要进行转义,所以表示一个.的正确写法是 \\.


      举例:

        需求:把www.baidu.com中的.替换成-


        如果直接使用.,会将所有字符替换成-,因为.代表任意一个字符
        hive (app)> select regexp_replace('www.baidu.com','.','-');
        -------------


        如果转义使用\.,仍然会将所有字符替换成-,因为\也需要转义
        hive (app)> select regexp_replace('www.baidu.com','\.','-');
        -------------


        转义使用\\.,成功替换
        hive (app)> select regexp_replace('www.baidu.com','\\.','-');
        OK
        _c0
        www-baidu-com


        在hive -e和hive -f的执行环境下,转义需要四个\,即\\\\.代表字符串.


        举例:

          使用\\.会转义失败,仍然将所有字符替换为-
          hive -e "select regexp_replace('www.baidu.com','\\.','-');"
          -------------


          使用\\\\.转义成功,将.正常替换为-
          hive -e "select regexp_replace('www.baidu.com','\\\\.','-');"
          www-baidu-com


          hive -f同理,需要使用\\\\.才能转义成功
          [root@hadoop ~]# vim regexp.hql
          vim 
          select regexp_replace('www.baidu.com','\\\\.','-');
          [root@hadoop ~]# hive -f regexp.hql


          www-baidu-com



          文章转载自数据分析师的FIRE人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论