ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

软件构造——17.常规表达式和语法(翻译版)

2021-05-18 00:00:44  阅读:148  来源: 互联网

标签:http 17 操作员 常规 语法 终端 字符串 表达式


软件在 6.031

免于错误: 准备更改
今天纠正,在未知的未来更正。
易于理解 :与未来的程序员,包括未来的你进行清晰的沟通。
准备更改:旨在适应变化而不重写。

目标

今天的课后,你应该:

1.了解语法制作和常规表达操作员的想法
3.能够读取语法或常规表达,并确定它是否匹配一系列字符
3.能够编写语法或常规表达式以匹配一组字符序列并将它们解析为数据结构

介绍

今天的阅读介绍了几个想法:

  • 语法,与生产,非终端,终端和运营商
  • 常规表达式

某些程序模块以字节序列或字符序列的形式接收输入或生成输出,当字符只是存储在内存中时称为字符串,或在流入或流出模块时称为流。在今天的阅读中,我们讨论如何为这样一个序列编写一个规范。具体来说,字节或字符的序列可能是:

  • 磁盘上的文件,在这种情况下,规范称为文件格式 通过网络发送的消息,在这种情况下,规范是一个有线协议
  • 用户在控制台上键入的命令,在这种情况下,规范是命令行接口
  • 存储在内存中的字符串

对于这类序列,我们引入了语法的概念,它不仅使我们能够区分法律序列和非法序列,还可以将序列解析为程序可以使用的数据结构。从语法生成的数据结构通常是一种递归式数据类型,就像我们在递归数据类型读取中讨论的一样。

我们还谈到了一种称为常规表达的语法的专门形式。除了用于规范和分析外,常规表达式是许多字符串处理任务的广泛应用工具,需要拆卸字符串、从字符串中提取信息或将其转换。
下一读将讨论解析器生成器,这是一种自动将语法转换为语法解析器的工具。

语法

为了描述一串符号,无论是字节、字符,还是从固定集中提取的其他类型的符号,我们使用称为语法的紧凑表示。
语法定义一组字符串。例如,我们的网址语法将指定 HTTP 协议中合法网址的字符串集。

语法中的字面字符串称为终端。它们被称为终端,因为它们是代表字符串结构的解析树的叶子。他们没有任何孩子,不能再扩展了。我们通常用引号(如或。‘http’’:’

语法由一组作品描述,其中每个生产定义一个非中期。您可以认为一个非最终变量,如代表一组字符串的变量,以及该变量的生成在其他变量(非终端)、操作员和常数(终端)方面的定义。非细分是代表一根绳子的树的内部节点。

语法中的制作具有形式

终端、非终端和运营商的非终端表达

语法的非最不计量数之一被指定为根。语法识别的一组字符串是与根非消灭匹配的字符串。这个非中期通常被称为或,但在下面的语法,我们通常会选择更难忘的名字,如,和。

root start url html markdown

语法操作员

生产表达中最重要的三个操作员是:

  • 对联,不是由符号表示,而只是一个空间:

x ::= y z an x is a y followed by a z

  • 重复,表示:*

x ::= y* an x is zero or more y

  • 工会,也称为交替,代表:|

x ::= y | z an x is a y or a z

您还可以使用其他操作员,这些操作员只是句法糖(即,它们相当于三大运营商的组合):

  • 可选(0 或 1 发生),表示:?

x ::= y? an x is a y or is the empty string

  • 1 或更多事件:表示:+

x ::= y+ an x is one or more y
(equivalent to x ::= y y* )

  • 字符类,表示包含方括号中列出的任何字符的长度-1字符串:[…]

x ::= [abc] is equivalent to x ::= ‘a’ | ‘b’ | ‘c’

  • 倒置字符类,表示包含括号中未列出的任何字符的长度-1字符:[^…]

x ::= [^abc] is equivalent to x ::= ‘d’ | ‘e’ | ‘f’
| … (all other characters in Unicode)

按照惯例,后缀操作员,并具有最高的优先级,这意味着他们首先应用。接下来应用连结。替代具有最低优先级,这意味着它最后应用。括号可用于凌驾于优先级:*?+|

  • 使用括号分组:

x ::= (y z | a b)* an x is zero or more y-z or a-b pairs m ::= a
(b|c) d an m is a, followed by either b or c, followed by d

示例:网址

假设我们要编写一个代表网址的语法。让我们从简单的例子开始,并扩展语法,逐步建立语法。

下面是一个简单的网址:

http://mit.edu/

表示仅包含此 URL的字符串集的语法看起来是:

url ::= ‘http://mit.edu/’

但是,让我们将其概括为捕获其他域名:

http://stanford.edu/
http://google.com/

我们可以这样写成一行:

url ::= ‘http://’ [a-z]+ ‘.’ [a-z]+ ‘/’

此语法表示由两部分主机名组成的所有 URL 集,主机名的每个部分由 1 个或多个字母组成。所以,并会匹配,但不是。由于它只有一个非中期的,这个URL语法的解析树看起来像右边的图片。

http://mit.edu/http://yahoo.com/http://ou812.com/

在这种单行形式中,单个非终端的生产仅使用操作员和终端,语法称为常规表达式(稍后将对此进行更多)。但是,如果我们使用新的非中期名称,将更容易理解:

url ::= ‘http://’ hostname ‘/’ hostname ::= word ‘.’ word word ::=[a-z]+

此语法的解析树现在显示在右侧。这棵树现在有更多的结构。树叶是已解析的绳子的一部分。如果我们把叶子连在一起,我们就会恢复原来的绳子。和非极品是标记树的节点,其子树符合这些规则的语法。请注意,非星节点的直系亲属喜欢遵循规则的模式。

hostnamewordhostnamehostnameword ‘.’ word

在这里插入图片描述

我们还需要如何概括?主机名可以具有两个以上的组件,并且可以有一个可选的端口编号:

http://didit.csail.mit.edu:4949/

要处理这种字符串,语法现在是:

url ::= ‘http://’ hostname (’:’ port)? ‘/’
hostname ::= word ‘.’ hostname | word ‘.’ word port ::= [0-9]+
word::= [a-z]+

请注意主机名现在是如何从自身角度递归定义的。主机名定义的哪一部分是基壳,哪个部分是递归步骤?允许使用何种主机名?

使用重复操作员,我们还可以编写这样的主机名:

hostname ::= (word ‘.’)+ word

另一个需要观察的是,此语法允许在技术上不合法的端口号码,因为端口编号的范围只能从 0 到 65535。我们可以编写一个更复杂的端口定义,只允许这些整数,但这通常不是语法中完成的。相反,使用语法的程序中将指定限制 0 < = 端口< = 65535。

我们还有很多事情要做才能走得更远:

概括以支持网址可以具有的其他协议http
概括在年底的斜线分离路径/
允许主机具有全套法律字符,而不仅仅是a-z

示例:标记和 HTML

现在,让我们来看看一些文件格式的语法。我们将使用两种不同的标记语言,在文本中表示排版风格。它们如下:

标记

This is italic.

html

Here is an italic word.

简单来说,我们的例子HTML和记分语法只会指定语法,但其他文本样式当然是可能的。此外,为了简单起见,我们将假设格式划分器之间的纯文本不允许使用任何格式标点符号,如或。_<

以下是我们简化版的记分的语法:

markdown ::= ( normal | italic ) * italic ::= ‘’ normal '’ normal
::= text text ::= [^_]*

在这里插入图片描述

以下是我们简化版 HTML 的语法:

html ::= ( normal | italic ) * italic ::= ‘’ html ‘
normal::= text text ::= [^<>]*
在这里插入图片描述

常规表达式

常规语法具有特殊属性:通过用右手侧替换每个非终端(根部除外),您可以将其简化为根部的单一生产,右侧只有终端和操作员。
我们的网址语法是常规的。通过用非特产替换非特产,可以简化为单个表达式:

url ::= ‘http://’ ([a-z]+ ‘.’)+ [a-z]+ (’:’ [0-9]+)? ‘/’

记分语法也是常规的:

markdown ::= ([^]* | '’ [^]* '’ )*

但我们的HTML语法不能完全减少。通过用右手边代替非端,您最终可以将其简化为类似的东西:

html ::= ( [^<>]* | ‘’ html ‘’ )*

…但右手边的递归性使用不能消除,也不能简单地由重复操作员替换。因此HTML语法不规则。html

终端和操作员的减少表达可以写在一个更紧凑的形式,称为常规表达。常规表达方式会消除终端周围的报价以及终端和操作员之间的空格,因此它只包含终端字符、分组括号括号和操作员字符。例如,我们格式的常规表达式只是 markdown

([_]*|_[]*)*

常规表达式也简称为雷雷克斯。regex 远不如原始语法可读,因为它缺少记录每个子表达含义的非中期名称。但是,注册表执行速度很快,并且许多编程语言中的库都支持常规表达式。

除了我们在语法中使用的语法外,通常在编程语言库中实现的 regex 语法还有几个特殊的操作员。以下是一些常见的有用之处:

. any single character

\d any digit, same as [0-9] \s any whitespace character,
including space, tab, newline \w any word character, including
letters and digits

., (, ), *, +, …
escapes an operator or special character so that it matches literally

每当有终端字符与特殊字符混淆时,使用反斜线都很重要。因为我们的常规表达有它作为一个终端, 我们需要使用反斜线来逃避它:url.

http://([a-z]+.)+[a-z]+(:[0-9]+)?/

在Java中使用常规表达式
常规表达式(“regexes”)广泛用于编程,并且您应该将它们放在工具箱中。

在 Java 中,您可以使用 regex 来操纵字符串(参见字符串.分裂、字符串匹配、java.util.regex.模式)。它们是现代脚本语言(如 Python、Ruby 和 Javascript)的一流功能内置的,您可以在许多文本编辑器中使用它们进行查找和替换。经常表达是你的朋友!大多数时候。下面是一些示例。

用单个空间替换所有运行空间:

String singleSpacedString = string.replaceAll(" +", " ");

匹配网址:

Pattern regex =Pattern.compile(“http://([a-z]+\.)+[a-z]+(:[0-9]+)?/”);
Matcher m =regex.matcher(string); if(m.matches()) {
// then string is a url
}

提取 HTML 标签的一部分:

Pattern regex = Pattern.compile("<a href="([^"]*)">");
Matcher m =regex.matcher(string);
if (m.matches()) {
String url = m.group(1);
// Matcher.group(n) returns the nth parenthesized part of the regex }

请注意网址和HTML标签示例中的反斜杠。在 URL 示例中,我们希望匹配字面句点,因此我们必须首先将其作为保护它免受被解释为 regex 匹配任何字符的操作员的程度,然后我们必须进一步逃离它,以保护反斜杠不被解释为 Java 字符串逃生字符。在 HTML 示例中,我们必须避开引号以防止其结束字符串。反冲逃逸的频率使雷雷克斯的可读性更低。.\.""

无上下文语法

一般来说,一种可以用我们的语法系统表达的语言称为无上下文语言。并非所有无上下文语言都是常规语言:即,某些语法不能简化为单一的非递归生成。我们的HTML语法没有上下文,但不定期。

大多数编程语言的语法也无上下文。一般来说,任何具有嵌套结构的语言(如嵌套括号或括号)都是无上下文的,但不具有规律性。此描述适用于 Java 语法,部分表现如下:

statement ::=
‘{’ statement* ‘}’
| ‘if’ ‘(’ expression ‘)’
statement (‘else’ statement)? | ‘for’ ‘(’ forinit? ‘;’ expression? ‘;’
forupdate? ‘)’ statement | ‘while’ ‘(’ expression ‘)’ statement | ‘do’
statement ‘while’ ‘(’ expression ‘)’ ‘;’ | ‘try’ ‘{’ statement* ‘}’ (
catches | catches? ‘finally’ ‘{’ statement* ‘}’ ) | ‘switch’ ‘(’
expression ‘)’ ‘{’ switchgroups ‘}’ | ‘synchronized’ ‘(’ expression
‘)’ ‘{’ statement* ‘}’ | ‘return’ expression? ‘;’ | ‘throw’ expression
‘;’ | ‘break’ identifier? ‘;’ | ‘continue’ identifier? ‘;’ |
expression ‘;’ | identifier ‘:’ statement | ‘;’

总结

机器处理的文本语言在计算机科学中随处可见。语法是描述此类语言最流行的形式主义,而常规表达是语法的重要子类,无需重复即可表达。

今天的阅读主题连接到我们良好的软件的三个属性如下:

**免于错误。**语法和常规表达式是字符串和流的声明性规范,图书馆和工具可以直接使用。这些规范通常比手工编写的解析代码更简单、更直接、更不可能是越野车。

易于理解。语法以比手写解析代码更容易理解的形式捕获序列的形状。唉,常规表达式通常不容易理解,因为它们是一种单行减少的形式,可能是一种更容易理解的常规语法。

准备好改变。语法可以很容易地编辑,但不幸的是,常规表达式更难改变,因为复杂的常规表达是神秘的,很难理解。

标签:http,17,操作员,常规,语法,终端,字符串,表达式
来源: https://blog.csdn.net/qq_43505820/article/details/116953807

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有