正则表达式从小白到入门 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
wsgzao
V2EX    程序员

正则表达式从小白到入门

  •  
  •   wsgzao
    wsgzao 2019-09-13 15:28:04 +08:00 4839 次点击
    这是一个创建于 2268 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    正则表达式( Regular Expression,RegExp,regex )使用单个字符串来描述和匹配一系列符合某种句法规则的字符串。此概念来自形式化语言理论,最初由贝尔实验室实现。正则表达式最初在 Perl 中实现,它的推广得益于 UNIX 软件的流行,尤其是 SED,GREP 等。 现在许多编程语言都内置了正则表达式引擎,如 PERL、Python、Javascript、Java、C++ 等。很多文本编辑器也支持正则表达式来进行检索和替换,如 Vim、Sublime Text、Visual Studio Code 等。正则表达式相关的学习文章网上也是一大推,本文主要记录正则表达式的入门教程和常用公式工具,方便大家活学活用。

    正则表达式从小白到入门

    更新历史

    2019 年 09 月 12 日 - 初稿

    阅读原文 - https://wsgzao.github.io/post/regex/

    扩展阅读

    正则表达式 30 分钟入门教程 - https://deerchao.net/tutorials/regex/regex.htm


    为什么要正则表达式?

    为什么需要正则表达式 - 王垠

    学习 Unix 最开头,大家都学过正则表达式 (regexp)。可是有没有人考虑过我们为什么需要正则表达式?

    正则表达式本来的初衷是用来从无结构的字符串中提取信息,殊不知这正好是 Unix 的缺陷所在。Unix 用无结构的字符串来表示数据,导致了诸多复杂的基于 regexp 的软件的诞生。sed, AWK, Perl, … 都是为了同样的目的来到这个世界上的。如果不是因为 Unix 用字符串来表示数据,我们就会拥有按数据结构类型的直接存储,而不需要折腾 regexp。正则表达式有它自己的价值(针对自然语言),但是我们其实不需要把它应用到程序语言和操作系统里面。

    正则表达式本身用一个字符串来表示,这带来另外一些问题。因为正则表达式的本质不是字符串,而是一个数据结构。学过计算理论的人可能知道这个数据结构叫做 NFA ( nondeterministic finite automaton,非确定性有限自动机)。所有的数据结构应该由程序语言本身来表示,就像用 Java 构造一个对象用new ClassA("a")一样。但是正则表达式强迫你把这个简单的构造函数调用写成一个字符串。所以在这个比方之下,你得写成 new ClassA(\"a\")。这样当你想要组合这些表达式的时候就发现,正则表达式几乎都是不可组合 (compose) 的。你几乎不可能不能把两个 regexp 的变量 A 和 B 安全拼接成一个,比如用 Java 的字符串拼接 A+B。因为你不知道这两个字符串拼在一起之后,那些稀奇古怪的符号会出现什么交叉反应,使得最后的识别的东西根本不是你想要的。

    在正则表达式中,由于正则表达式本身的构造函数与数据本身合并到一起,我们不得不对某些 “特殊字符” 进行 escape。这些特殊字符,其实是用来描述 NFA 的记号,它们属于更高一层的语言。可是在正则表达式里,它们与 NFA 节点里的字符混为一谈。比如很简单的一个 block comment 的正则表达式,却要写成这个样子:

    /\\\\\*(\[^\\\\\*\]|\[^/\])\*\\\\\*/ 

    显然这样的表达式很容易出错。 如果我们用程序语言的表达式来构造这个表达式,它应该是这样:

    (@... "/\*" (@\*(@!"\*/")) "\*/") 

    在这个我自己设计的 Scheme 表达式里,以 @开头的标识符都是构造函数。其中 @... 是构造 sequence,@*是构造一个 zero-or-more 的匹配,@! 构造一个否定匹配。这个表达式是说:“以 / * 开头,接着零个或者多个不是 * / 的字符,最后接着一个 * /。这样一来清晰明了,什么表达式在什么 “层次” 都很清楚,不需要什么反斜杠 escape,而且这样的表达式可以 compose。比如:

    (define reg1 (@... "/\*" (@\*(@!"\*/")) "\*/")) (define reg2 (@+ "foo")) (define reg3 (@= "b")) 

    定义这三个表达式之后,我们之后可以用像 (@... reg1 (@or reg2 reg3))这样的表达式来连接 3 个不同的表达式,构造出更大的表达式。这样的构造可以无限的扩展。从这里以及以往的经验,我总结出一个普遍适用的程序设计的教训:尽量不要把多个层次的语言 “压缩” 到一层。我们也看到正则表达式与 “Unix 哲学” 有很大关系。我没有考古,所以不知道孰先孰后,但是它们肯定有直接的因果关系。两者都是 Unix 复杂性的来源。

    再来看取自 12306 网站的一段代码

    // http://www.12306.cn/mormhweb/js/adKyfw.min.js d = d.replace("'", ""); d = d.replace("%", ""); d = d.replace("#", ""); d = d.replace("&", ""); d = d.replace("*", ""); d = d.replace("(", ""); d = d.replace(")", ""); d = d.replace("@", ""); d = d.replace("`", ""); d = d.replace("/", ""); d = d.replace("\\", ""); d = d.replace(",", ""); d = d.replace(".", ""); d = d.replace("=", ""); d = d.replace("<", ""); d = d.replace(">", ""); 

    上述代码是在过滤掉不合法的搜索字符(姑且不论客户端过滤是否安全), 我们可以用一行正则替换来实现相同的功能:

    d = d.replace(/'%#\&\*\(\)@`\/\\,\.=<>/g, ''); 

    正则表达式入门教程推荐

    感谢作者 deerchao 从 2006 年开始更新至今,谢谢

    正则表达式 30 分钟入门教程 - DeerChao

    正则表达式 - 教程

    Python RegEx

    正则表达式在线工具

    regexr

    regex101

    正则表达式测试工具(在线)

    正则表达式在线测试

    正则表达式基本语法

    定义正则表达式的方式在不同的工具中可能有所差别,但正则表达式内容的语法是一致的。 正则表达式有三类语法结构:

    1. 串接(与操作)。相邻的字符默认为串接关系。例如 harttle 只能匹配 harttle, 不可匹配 hart
    2. 选择(或操作,|)。例如:harttle|serene 可以匹配 harttle 或者 serene。 选择的优先级级低于串接,因此很多情况下都可以省略括号。
    3. 数量(限定符)。最常见的数量限定符包括 +, ?, *,分别表示左侧的字符出现一次或更多,不出现或出现一次,不出现或出现任意次。例如 harttle? 可以匹配 harttlharttle
    4. 组合(括号,())。组合用来定义操作符的作用范围和优先级。例如 har(ttle)? 可以匹配 harttleharh(a|u)rttle 可以匹配 harttlehurttle

    常用正则表达式

    V2EX 不支持表格,跳过

    参考文章

    Wikipedia 正则表达式

    MDN RegExp

    正则表达式 30 分钟入门教程 - DeerChao

    4 条回复    2019-09-14 12:40:16 +08:00
    giveupAK47
        1
    giveupAK47  
       2019-09-13 15:32:34 +08:00 via iPhone
    用着搜着
    pink123
        2
    pink123  
      &nbs;2019-09-13 15:36:07 +08:00
    Read it the doc and then you will know how to use it, and eventually you will master it.
    It's not tough to master
    xupefei
        3
    xupefei  
       2019-09-13 19:37:35 +08:00 via iPhone
    11206 那个例子我觉得是为了保证可读性,也为了以后扩展方便。相比之下,它对应的正则表达式一眼还看不懂,需要肉眼 parse ;想加一个符号的话还要先想想要不要 escape。
    jellybool
        4
    jellybool  
       2019-09-14 12:40:16 +08:00 via Android
    正则表达式就是:从左往右
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2698 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 05:47 PVG 13:47 LAX 21:47 JFK 00:47
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86