在 MaxCompute 中,当需要表示或输出具有特殊意义或无法直接输入的字符时,必须对这些字符进行转义,以确保字符串数据的准确表示和处理。本文为您介绍 MaxCompute 中转义字符的使用场景和使用示例。
转义字符使用场景
在编程领域几乎所有的字符串表示都会遇到转义字符的问题,而解决思路也都是类似的:
-
首先制定一个规则,规定一些有特殊含义的字符,例如
'、"。 -
然后对这些特殊含义的字符以及不可见字符做特殊处理,例如
\'、\"。 -
最后针对第 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 |
结束符。 |
反斜线后面跟了一个不需要转义的字符,这种情况和没有转义字符相同,例如
\a
和
a
是同一个字符。
正则表达式
正则表达式除了对一般的不可见字符使用
\
,为了做一些文本匹配,声明了模式,而这些模式中大量使用了
()^%
等符号,例如
MaxCompute
的底层正则引擎使用了
RE2
,一些使用
\
的转义写法如下:
|
转义字符写法 |
说明 |
|
\d |
匹配任何数字,等价于
|
|
\D |
匹配任何非数字,等价于
|
|
\s |
匹配任何空白符,等价于
|
|
\S |
匹配任何非空白符,等价于
|
|
\w |
匹配任何数字字母字符,等价于
|
|
\W |
匹配任何非数字字母字符,等价于
|
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
步骤如下:
-
写出正则表达式
(\d+)-。 -
根据 SQL 的转义规则,对其中的
\进行转义,同时在两边添加单引号,可以得到字符串'(\\d+)-'。
在 MaxCompute 中执行如下命令进行验证:
select REGEXP_EXTRACT('010-12345678', '(\\d+)-');
--返回结果
010
JSON+正则表达式+SQL 转义
如果字符串本身就含有转义字符,例如
{"key":"this is very \"important\"."}
这段
JSON,使用正则表达式匹配出其中的
important
步骤如下:
-
写出正则表达式:
\"(.*)\"。 -
对其中的反斜线进行正则表达式的转义:
\\"(.*)\\"。 -
再对表达式进行 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 以及正则字符串的转义处理。