转义字符

当您需要表示或输出在MaxCompute中有特殊意义或者无法直接输入的字符时,此时需要对字符进行转义,以确保字符串数据的正确表示和处理。MaxCompute转义字符表达字符串中的特殊字符或将其后跟的字符解释为其本身,本文为您介绍MaxCompute中转义字符的使用场景和使用示例。

转义字符使用场景

在编程领域几乎所有的字符串表示都会遇到转义字符的问题,而解决思路也都是类似的:

  1. 首先制定一个规则,规定一些有特殊含义的字符,例如' "

  2. 然后对这些特殊含义的字符以及不可见字符做特殊处理,例如 \' \"

  3. 最后针对第2步中的方式再做个补充,做一些特殊处理,例如\\表示反斜线。

有很多规范使用\进行转义,常见使用场景如下:

说明

有些规范并没有使用\,例如URL中使用%进行转义,XML中使用&进行转义。

SQL(MaxCompute

在SQL语法中,字符串的内容需要写在半角单引号('')或者双引号("")中,例如"abc"'123'。对于一些字符串中本身就有单引号或者双引号的情况,如果只包括一种引号,则可以简单使用另一种引号,例如 '双引号(")'。但是如果一段文字中既包括半角单引号又包括半角双引号,例如如下语句:

双引号(")
单引号(')

不仅包含了''"",还包含了换行符,对于这种,规定使用平时较少用到的反斜线(\)作为特殊字符,表示和后面的字符共同表示个字符,例如\n表示换行,\"\'表示字符串中的引号,并不代表字符串结束,则上面的内容可以写成一个字符串:'双引号(")\n单引号(\')' 或者 "双引号(\")\n单引号(')"

但是这样反斜线又有了特殊的含义,对于字符串中的反斜线,就要写成\\,第一个表示转义,第二个表示真正的字符。SQL中常见的转义字符写法如下:

转义字符写法

说明

\b

退格(backspace),将当前位置移到前一列。

\t

水平制表(tab)。

\n

换行(newline),将当前位置移到下一行开头。

\r

回车(carriage-return),将当前位置移到本行开头。

\'

单引号。

\"

双引号。

\\

反斜线。

\;

分号。

\Z

control-Z。

\0或\00

结束符。

说明

反斜线后面跟了一个不需要转义的字符,这种情况和没有转义字符相同,例如 \aa是同一个字符。

正则表达式

正则表达式除了对一般的不可见字符使用\,为了做一些文本匹配,声明了模式,而这些模式中大量使用了()^%等符号,例如MaxCompute的底层正则引擎使用了RE2,RE2详情请参见RE2,一些使用\的转义写法如下:

转义字符写法

说明

\d

匹配任何数字,等价于[0-9]

\D

匹配任何非数字,等价于 [^0-9]

\s

匹配任何空白符,等价于 [\t\n\f\r ]

\S

匹配任何非空白符,等价于[^\t\n\f\r ]

\w

匹配任何数字字母字符,等价于 [0-9A-Za-z_]

\W

匹配任何非数字字母字符,等价于[^0-9A-Za-z_]

完整的正则语法非常复杂,您可以使用regexrregex101在线工具写出符合预期的正则表达式。

JSON

JSON是一种常用的传输数据的文本协议,标准比较简单,完整的标准请参见JSON,使用\的转义写法如下:

转义字符写法

说明

\"

半角双引号。

\\

反斜线。

\/

正斜线。

\b

退格符。

\f

换页符。

\n

换行符。

\r

回车符。

\t

水平制表符。

\u+4位16进制

unicode。

可以看到JSON许多转义字符的设计和SQL非常相似,您可以使用JSON在线工具JSONLint验证一段文本是否符合JSON规范。

转义字符使用示例

在转义字符使用的过程中,由于许多规范都使用了\做转义,在组合使用的时候,较难理解。这时候需要理解文本的嵌套结构,辅助使用上文提到的在线工具,逐层进行转义和反转义,便可写出正确语义的代码语句,示例如下:

转义字符后字符解释为其本身

  • MaxCompute SQL中的字符串常量可以用单引号或双引号表示。您可以在单引号括起的字符串中包含双引号,或在双引号括起的字符串中包含单引号,否则要用转义字符来表达。表达方式如下:

    "I'm a happy manong."
    'I\'m a happy manong.'
  • 在LIKE字符匹配中,要匹配%_本身,则要对其进行转义:

    select 'ab_cde' like 'ab\_c%';
    
    --返回结果
    true

特殊字符

  • 'a\tb'字符串里有三个字符,\t被视为一个字符。

    select length('a\tb');
    
    --返回结果
    3
  • 'a\ab'字符串里有三个字符,\a被解释为普通的a

    select 'a\ab',length('a\ab');
    
    --返回结果
    aab,3

JSON+SQL转义

有一段JSON:{"key":"this is very \"important\"."},想要使用get_json_object函数提取Value值,却得不到正确的结果:

-- 使用新版本的get_json_object,会检查json的完整性
set odps.sql.udf.getjsonobj.new=true;
select get_json_object('{"key":"this is very \"important\"."}', '$.key');

--返回结果
NULL

原因出现在这段JSON文本只是简单地在两侧加了半角单引号,里面的内容并没有经过SQL的转义,直接select文本验证此结论:

select '{"key":"this is very \"important\"."}'; 

--返回结果
{"key":"this is very "important"."}

可以看到反斜线消失了,原因是编译器解析的时候把JSON中的转义字符当做了SQL的转义字符,\" 被解释成了 ",导致得到的结果并不符合JSON语法,从而无法解析。对JSON文本加上正确的SQL转义字符:'{"key":"this is very \\"important\\"."}',再次使用get_json_object函数提取Value值,得到正确的结果:

set odps.sql.udf.getjsonobj.new=true;
select get_json_object('{"key":"this is very \\"important\\"."}', '$.key');

--返回结果
this is very "important".

简单文本+正则表达式+SQL转义

有一段文字:010-12345678,使用函数提取出最前面的010步骤如下:

  1. 写出正则表达式(\d+)-

  2. 根据SQL的转义规则,对其中的\进行转义,同时在两边添加单引号,可以得到字符串'(\\d+)-'

在MaxCompute中执行如下命令进行验证:

select REGEXP_EXTRACT('010-12345678', '(\\d+)-');

--返回结果
010

JSON+正则表达式+SQL转义

如果字符串本身就含有转义字符,例如{"key":"this is very \"important\"."}这段JSON,使用正则表达式匹配出其中的important步骤如下:

  1. 写出正则表达式:\"(.*)\"

  2. 对其中的反斜线进行正则表达式的转义:\\"(.*)\\"

  3. 再对表达式进行SQL的转义,将所有的\替换成\\,为了简便,两边再添加单引号,得到字符串:'\\\\"(.*)\\\\"'

在MaxCompute中执行如下命令进行验证:

select REGEXP_EXTRACT('{"key":"this is very \\"important\\"."}', '\\\\"(.*)\\\\"');

--返回结果
important

结果符合预期,类似此场景建议先对JSON进行解析,逻辑会更清晰,命令如下:

set odps.sql.udf.getjsonobj.new=true;
select REGEXP_EXTRACT(get_json_object('{"key":"this is very \\"important\\"."}', '$.key'), '"(.*)"');

--返回结果
important

通过以上示例,可以正向地写出符合业务需求的语句,但是这么多转义之后,看到的字符串往往非常难懂,'\\\\"(.*)\\\\"'究竟想要匹配什么,已经不太容易理解和维护了。可以通过select原始的字符串进行反向操作,再结合原始字符串和正则表达式工具,可以推断出想要匹配的模式:

select '\\\\"(.*)\\\\"';

--返回结果
\\"(.*)\\"

SQL转义增强

SQL、JSON和正则表达式都使用了\作为转义字符容易引起混乱,选择\作为转义字符是因为在正常的语句中出现的频率很低,但是在几种规范组合的场景反而大量出现,出现转义字符膨胀的问题。为了处理好此类场景,MaxCompute引入了一种新的转义方式: R"()",在括号中书写的字符不再需要使用\来进行转义。

例如:

  • R"(abc)"等价于 'abc'

  • R'(\\"(.*)\\")' 等价于'\\\\"(.*)\\\\"'

这里的R也可以使用小写字母r,代表原始字符串(Raw String),双引号也可以替换为单引号。在MaxCompute中此方式对于想要转义的字符串不需要每个都添加\,只需要在两边稍加修改即可。使用正则表达式匹配出JSON中important的语句也可以修改为如下命令:

select REGEXP_EXTRACT(R'({"key":"this is very \"important\"."})', R'(\\"(.*)\\")');

--返回结果
important

此方式极大地简化了对JSON以及正则字符串的转义处理。