正規表現エンジンを行う原則は - それほど明確ではありませんでした!

より多くのWebサイトは、編集者は、「正規表現」の文字列と呼ばれるものによってサポートされているプログラミング言語は、「式」を見つけることは、(正規表現略し正規表現)正規表現を理解しておく必要があり経験の学生のプログラミングを持っていましたそれは、文字列(パターン)のセットです何か、のような論理式です。

使用正则表达式去匹配字符串Hello World 中的 Hello
伪代码:/Hello/, "Hello World"
输出:Hello

正規表現についての記事を書くためにどのように、私は記事が紳士はとても混乱させたことのない、週にそれについて考えました。

私は正規表現だと思うので:覚えにくい、説明するのは難しい、広くて深いと真剣に取られていないが、いくつかは、書き込みに難しいだけで良い書き込み正規表現を言います!

  1. グッド執筆:より多くのいくつかの一般的な、実用的な例よりも書き込み何も、あなたのそれぞれがこれを書くことができます正直に言うと:インターネットBaiduの上で、その後、私の実務経験のいくつかを組み合わせて、記事が出てきました。
  2. 書き込みするのは難しい:多くの人々が定期的に簡単なこと、百度にそれを使用して、覚えていないと思います。しかし、ほとんどの人は普通の小さな顔、ほとんど注目の本当の本質を知っています!

私は特に、通常のエンジン最適化の原則の実施と定期的に、非常に、非常に実際にあなたが知識の定期的なポイントを理解することを願っ紳士、また、インタビューの中で求められることがあり、正規表現の高度な知識とみなすことができます。 ここに画像を挿入説明

まず、起源と発展

私たちは、技術自体はいくつかの助けを持って理解する必要が技術を学ぶために時間に由来し、開発プロセスを理解する必要があります!

1940:2つの神経科医からの正規表現のオリジナルのアイデア:マカロックとウォルター・ピッツは、彼らが道ニューラルネットワークを記述するために数学的モデルを開発しました。

1956:題しスティーブン・クリーネの発表された論文という名前の数学的科学者「の表現のニューラルネットワークのイベントは、」数学的表記の使用は、このモデルを記述するための定期的なセットと呼ばれ、正規表現の概念を導入。正規表現は、「定期的な代数的集合」と呼ばれるその表現の一つとして記述するために使用されるので、「正規表現」という用語を採用しています。

1968:C言語、UNIXケン・トンプソンの父、「正規表現」いくつかの研究を行うための検索アルゴリズムのための理論的結果の父は、彼は、正規表現コンパイラを説明し、これがなければなりません最初の正規表現コンパイラは(ものgrepの編集者になった)QED。

Unix使用正则之后,正则表达式不断的发展壮大,然后大规模应用于各种领域,根据这些领域各自的条件需要,又发展出了许多版本的正则表达式,出现了许多的分支。我们把这些分支叫做“流派”。

1987年:Perl语言诞生了,它综合了其他的语言,用正则表达式作为基础,开创了一个新的流派,Perl流派。之后很多编程语言如:Python、Java、Ruby、.Net、PHP等等在设计正则式支持的时候都参考Perl正则表达式。 ここに画像を挿入説明

到这里我们也就知道为什么众多编程语言的正则表达式基本一样,因为他们都师从Perl。

注:Perl语言是一种擅长处理文本的语言,但因晦涩语法和古怪符号不利于理解和记忆导致很多开发者并不喜欢。

二、语法

完整的正则表达式由两种字符构成:特殊字符(元字符)和普通字符。

ps:元字符表示正则表达式功能的最小单位,如 * ^ $ \d 等等

关于语法部分猪哥并不想过多的讲解,给大家做一个详细的归纳整理,供大家日后快速查找吧! ここに画像を挿入説明

如果想系统学习正则表达式的语法部分,猪哥推荐 菜鸟教程: https://www.runoob.com/regexp/regexp-tutorial.html ここに画像を挿入説明

三、匹配原理

匹配原理是猪哥想要重点讲解的部分,也希望同学们可以认真了解这部分的内容。

很多人觉得开车没必要了解车的构造原理,但是我们学编程的还真的需要了解原理。

因为了解原理,你才能调优,这往往也是初级工程师与中高级工程师之间的差别点之一!

1.执行过程

正则表达是的执行,是由正则表达引擎编译执行的,大致的执行流程猪哥也花了一个流程图给大家看看。 ここに画像を挿入説明

这里给大家提一点就是:预编译(pre-use compile)

猪哥建议大家在生产环境中使用预编译功能,为什么呢?

以Python语言内置re模块举例:

  1. 通过re.compile(pattern)预编译返回Pattern对象,在后面代码中可以直接引用。
  2. 通过re.match(pattern, text)即用编译,虽然也会有缓存Pattern对象,但是每次使用都需要去缓存中取出,比预编译多一步取操作。

猪哥也通过实际测试来 验证预编译 确实比 即用编译 要快!

pattern = r'http:\/\/(?:.?\w+)+'
text = '<a href="http://www.xxx.com">xxx.com</a>'

ここに画像を挿入説明

2.引擎

既然正则表达式由执行引擎执行,那我们就来讲讲正则表达式的引擎吧,这一块是重点,希望大家仔细看看,弄懂了理解了才行!

正则引擎主要可以分为基本不同的两大类:

  1. DFA (Deterministic finite automaton) 确定型有穷自动机
  2. NFA (Non-deterministic finite automaton) 非确定型有穷自动机

ps:当然还有一种引擎为:POSIX NFA,这是根据NFA引擎出的规范版本,但因为使用较少所以我们这里也就不重点讲解。

这里需要和大家解释下何为确定型有穷自动机这几个名词:

  1. 确定型与非确定型:假设有一个字符串(text=abc)需要匹配,在没有编写正则表达式的前提下,就直接可以确定字符匹配顺序的就是确定型,不能确定字符匹配顺序的则为非确定型。
  2. 有穷:有穷即表示有限的意思,这里表示有限次数内能得到结果。
  3. 自动机:自动机便是自动完成,在我们设置好匹配规则后由引擎自动完成,不需要人为干预!

根据上面的解释我们可得知DFA引擎 和 NFA引擎 的区别就在于:在没有编写正则表达式的前提下,是否能确定字符执行顺序!

DFA引擎执行原理: 为了大家能很清楚的理解DFA引擎执行原理,猪哥制作了一个简易的动态执行过程图给大家看看 ここに画像を挿入説明 根据上面的动图我们可以得出DFA引擎的一些特点:

  1. 文本主导:按照文本的顺序执行,这也就能说明为什么DFA引擎是确定型(deterministic)了,稳定!
  2. 记录当前有效的所有可能:我们看到当执行到(d|b)时,同时比较表达式中的db,所以会需要更多的内存。
  3. 每个字符只检查一次:这提高了执行效率,而且速度与正则表达式无关。
  4. 不能使用反向引用等功能:因为每个字符只检查一次,文本零宽度(位置)只记录当前比较值,所以不能使用反向引用、环视等一些功能!

NFA引擎执行原理: 猪哥同样画了一个简易的NFA引擎执行过程图方便大家理解 ここに画像を挿入説明 根据上面的动图我们可以得出NFA引擎的一些特点:

  1. 文表达式主导:按照表达式的一部分执行,如果不匹配换其他部分继续匹配,直到表达式匹配完成。
  2. 会记录某个位置:我们看到当执行到(d|b)时,NFA引擎会记录字符的位置(零宽度),然后选择其中一个先匹配。
  3. 单个字符可能检查多次:我们看到当执行到(d|b)时,比较d后发现不匹配,于是NFA引擎换表达式的另一个分支b,同时文本位置回退,重新匹配字符'b'。这也是NFA引擎是非确定型的原因,同时带来另一个问题效率可能没有DFA引擎高。
  4. 可实现反向引用等功能:因为具有回退这一步,所以可以很容易的实现反向引用、环视等一些功能!

针对两种引擎的区别,猪哥进行了比较 ここに画像を挿入説明 关于这两种引擎的总结,猪哥引用《精通正则表达式》书本中的一句话来概括:

> DFA(是电动机) 和NFA(汽油机) 都有很长的历史,不过,正如汽油机一样,NFA 的历史更长一些。也有些系统采用了混合引擎,它们会根据任务的不同选择合适的引擎(甚至对同一表达式中的不同部分采用不同的引擎,以求得功能与速度之间的最佳平衡)。 ——《精通正则表达式》

3.回溯

作为绝大多数编程语言都选择的引擎——NFA (非确定型有穷自动机) 引擎,我们当然要再详细了解一下它的精髓——回溯。 ここに画像を挿入説明 动图中,我们可以看到当某个正则分支匹配不成功之后,文本的位置需要回退,然后换另一个分支匹配,而回退这步专业术语就叫:回溯。

回溯的原理类似我们走迷宫时走过的路设置一个标志物,如果不对则原路返回,换另一条路。 ここに画像を挿入説明

回溯机制不但需要重新计算正则表达式和文本的对应位置,也需要维护括号内的子表达式所匹配文本的状态(b匹配成功),保存到内存中以数字编号的组中,这就叫捕获组。

保存括号内的匹配结果之后,我们在后面的正则表达式中就可以使用,这就是我们所说的反向引用,在上面的案例中只有一个捕获,所以$1=b

回溯陷阱:讲到回溯必须提到回溯陷阱,它导致的结果就是机器CPU使用率爆满(超100%),机器就卡死了。

举个例子:text=aaaaa,pattern=/^(a*)b$/,匹配过程大致是

  1. (a*):匹配到了文本中的aaaaa
  2. 匹配正则中的b,但是失败,因为(a*)已经把text都吃了
  3. 这时候引擎会要求(a*)吐出最后一个字符(a),但是无法匹配b
  4. 第二次是吐出倒数第二个字符(还是a),依然无法匹配
  5. 就这样引擎会要求(a*)逐个将吃进去的字符都吐出来
  6. 但是到最后都无法匹配b

这里的重点就在于 引擎会要求*匹配的东西一点一点吐回,我们假设如果文本长度为几万,那引擎就要回溯几万次,这对机器的CPU来说简直是灾难。

有些复杂的正则表达式可能有多个部分都要回溯,那回溯次数就是指数型。如果文本长度为500,一个表达式有两部分都要回溯,那次数可能是500^2=25万次,这谁受得了!

关于更多更详细的回溯介绍,推荐大家可以阅读《精通正则表达式》这本书!

四、优化

编写巧妙的正则表达式不仅仅是一种技能,而且还是一种艺术。

上面我们了解到,绝大多数的编程语言都采用的是NFA引擎,而NFA引擎的特点是:功能强大、但有回溯机制所以效率慢。所以我们需要学习一些NFA引擎的一些优化技巧,以减少引擎回溯次数以及更直接的匹配到结果!

针对NFA引擎的可优化的点其实挺多的,为了方便大家记忆,猪哥也画幅结构图归纳一下,方便大家收藏细看。 ここに画像を挿入説明 在面试过程中也许会被问到关于正则的优化,大家记住几点就可以。

五、推荐

上面我们讲解了关于正则表达式的诞生和发展、引擎、优化等知识,但是关于正则表达式的知识点远远不止这些,所以最后猪哥推荐一些好的学习资料,大家有空可以了解学习下。

1.书

推荐正则表达式的书,那必然是《精通正则表达式》 ,目前这本书已经出了第三版,豆瓣评分8.9。

内容虽然稍有啰嗦,但是对于正则新手很友好,唯一不足是Python案例少。 ここに画像を挿入説明

2.博客

 注释:public void ConfigureServices(IServiceCollection services)
  
  //添加HttpReports
  
  services.AddHttpReports(www.wangffzc.cn).UseSQLServerStorage( www.xinchenkg.com shentuylzc.cn);
  
  services.AddControllers(www.honglanggjpt.cn );
  
  // This method gets called by the runtime. www.wangffzc.cn Use this method to configure the HTTP request pipeline.
  
  public void Configure(www.baishenjzc.cn IApplicationBuilder app, IWebHostEnvironment env)
  
  //使用HttpReports
  
  app.UseHttpReports(www.jiniuyLzc.cn );
  
  if (env.IsDevelopment(www.tianjiptzc.cn))
  
  app.UseDeveloperExceptionPage(www.jinniugpt.cn);
  
  app.UseRouting(www.huanhua2zhuc.cn);
  
  app.UseAuthorization(www.yunshengpt.com);
  
  app.UseEndpoints(endpoints www.jiniuyLzc.cn =>

3.在线测试工具

https://regex101.com/、このサイトは定期的に、意味解析、マッチングテスト、参考文献リストなど、非常に実用的なのための異なるプログラミング言語のサポートを選択することができます。 ここに画像を挿入説明

4.一般的なケース

例いくつかの簡単な一般的な小型の要約、ルーキーのチュートリアル:のhttp://c.runoob.com/front-end/854 ここに画像を挿入説明

最後に、私は正規表現を取得するには、誰もが望む、テキストを処理することは便利です!

おすすめ

転載: www.cnblogs.com/laobeipai/p/12333615.html