我正在尝试使用TOAD中的SQL从表中的DESCRIPTION字段获取所有不可打印的ASCII字符,但是以下查询无法正常工作。
select
regexp_instr(a.description,'[^[:ascii:]]') as description from
poline a where a.ponum='XXX' and a.siteid='YYY' and
regexp_instr(a.description,'[^[:ascii:]]') > 0
以上查询购买错误ORA-127729:正则表达式中的字符类无效。我试过:print:而不是:ascii:但它没有带来任何结果。以下是此记录的说明,其中包含不可打印的字符。
Sherlock 16 x 6.5” Wide Wheelbarrow wheel .M100P.10R – Effluent care bacteria and enzyme formulation
答案 0 :(得分:1)
:ascii:
不是有效的字符类,即使它是,它似乎也不是你想要到达的字符(ascii包含不可打印的字符)。可以找到有效的类here。
实际上,如果您在原始查询中将:ascii:
替换为:print:
,它确实会返回每个POLINE.DESCRIPTION
中不可打印字符的第一个位置。 (如果它没有为您返回任何内容,可能是因为您的DESCRIPTION
数据实际上都是可打印的。)
但正如您所说,您希望在DESCRIPTION
中的每个POLINE
中识别 每个 不可打印的字符,则需要进行一些更改。我将包含一个将每场比赛作为首发位置的示例。
在此示例中,每个DESCRIPTION
将被分解为其各个组成字符,并且将检查每个字符的可印刷性。将返回DESCRIPTION
字符串中的位置以及不可打印字符的ASCII number
。
此示例假设POLINE
中的每一行都有唯一标识符,此处称为POLINE_ID
。
首先,创建测试表:
CREATE TABLE POLINE(
POLINE_ID NUMBER PRIMARY KEY,
PONUM VARCHAR2(32),
SITEID VARCHAR2(32),
DESCRIPTION VARCHAR2(256)
);
并加载一些数据。我在您提供的示例Sherlock
字符串#23
和#17
中插入了几个非打印字符。还包括仅包含前64个ASCII字符(其中前31个不在:print:
中)的示例字符串,以及一些填充PONUM
和SITEID
谓词的填充符。
INSERT INTO POLINE VALUES (1,'XXX','YYY','Sherlock'||CHR(23)||' 16 x 6.5” Wide Wheelbarrow wheel .M100P.10R –'||CHR(17)||' Effluent care bacteria and enzyme formulation');
DECLARE
V_STRING VARCHAR2(64) := CHR(1);
BEGIN
FOR POINTER IN 2..64 LOOP
V_STRING := V_STRING||CHR(POINTER);
END LOOP;
INSERT INTO POLINE VALUES (2, 'XXX','YYY',V_STRING);
INSERT INTO POLINE VALUES (3, 'AAA','BBB',V_STRING);
END;
/
INSERT INTO POLINE VALUES(4,'XXX','YYY','VOLTRON');
现在我们共有4行。其中三个包含(多个)不可打印的字符,但只有两个符合所有限制。
然后运行查询。下面有两个示例查询 - 第一个使用REGEXP_INSTR
与初始示例查询中的一样(用:cntrl:
代替:print:
)。但是对于替代方案,还包括第二个变体,它只检查每个char是否在前31个ascii字符中。
两个示例查询都将索引每个DESCRIPTION
的每个字符,并检查它是否可打印,并收集每个候选DESCRIPTION
中每个不可打印字符的ascii编号和位置。这里的示例表具有256个字符长的DESCRIPTION
,因此这被用作笛卡尔连接中的最大索引。
请注意,这些 效率不高 ,旨在让每次匹配。如果你最终只需要第一场比赛,那么用:print:
替换的原始查询将会表现得更好。此外,这也可以通过放入PL / SOL或者可能是递归来调整(如果在你的用例中允许PL / SQL,或者你是11gR2 +,等等)。此处的一些谓词如REGEXP_LIKE
不影响最终结果,仅用于初步过滤。根据您的数据集,这些对您来说可能是多余的(或更糟)。
第一个例子,使用正则表达式和:print:
SELECT
POLINE_ID,
STRING_INDEX AS NON_PRINTABLE_LOCATION,
ASCII(REGEXP_SUBSTR(SUBSTR(DESCRIPTION, STRING_INDEX, 1), '[[:cntrl:]]', 1, 1)) AS NON_PRINTABLE_ASCII_NUMBER
FROM POLINE
CROSS JOIN (SELECT LEVEL AS STRING_INDEX
FROM DUAL
CONNECT BY LEVEL < 257) CANDIDATE_LOCATION
WHERE PONUM = 'XXX'
AND SITEID = 'YYY'
AND REGEXP_LIKE(DESCRIPTION, '[[:cntrl:]]')
AND REGEXP_INSTR(SUBSTR(DESCRIPTION, STRING_INDEX, 1), '[[:cntrl:]]', 1, 1, 0) > 0
AND STRING_INDEX <= LENGTH(DESCRIPTION)
ORDER BY 1 ASC, 2 ASC;
第二个例子,使用ASCII码:
SELECT
POLINE_ID,
STRING_INDEX AS NON_PRINTABLE_LOCATION,
ASCII(SUBSTR(DESCRIPTION, STRING_INDEX, 1)) AS NON_PRINTABLE_ASCII_NUMBER
FROM POLINE
CROSS JOIN (SELECT LEVEL AS STRING_INDEX
FROM DUAL
CONNECT BY LEVEL < 257) CANDIDATE_LOCATION
WHERE PONUM = 'XXX'
AND SITEID = 'YYY'
AND REGEXP_LIKE(DESCRIPTION, '[[:cntrl:]]')
AND ASCII(SUBSTR(DESCRIPTION, STRING_INDEX, 1)) BETWEEN 1 AND 31
AND STRING_INDEX <= LENGTH(DESCRIPTION)
ORDER BY 1 ASC, 2 ASC;
在我们的测试数据中,这些查询将产生等效的输出。我们应该期望Sherlock
DESCRIPTION
有两次点击(第17和23章),第一次64-ascii DESCRIPTION
有31次点击。
结果:
POLINE_ID NON_PRINTABLE_LOCATION NON_PRINTABLE_ASCII_NUMBER
1 9 23
1 56 17
2 1 1
2 2 2
2 3 3
2 4 4
2 5 5
2 6 6
2 7 7
2 8 8
2 9 9
2 10 10
2 11 11
2 12 12
2 13 13
2 14 14
2 15 15
2 16 16
2 17 17
2 18 18
2 19 19
2 20 20
2 21 21
2 22 22
2 23 23
2 24 24
2 25 25
2 26 26
2 27 27
2 28 28
2 29 29
2 30 30
2 31 31
33 rows selected.
编辑在回复评论时,以下是[[:cntrl:]]
和[^[:cntrl:]]
与regexp_instr
的期望。
[[:cntrl:]]
将匹配前31个ascii字符中的任何一个,而[^[:cntrl:]]
是[[:cntrl:]]
的逻辑否定,因此它将匹配除前31个ascii字符之外的任何内容。
为了比较这些,我们可以从最简单的情况开始,只有一个字符ascii #31
。由于只有一个字符,因此结果只能是匹配或未命中。我们希望以下内容返回1
来进行匹配:
SELECT REGEXP_INSTR(CHR(31),'[[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL;
MATCH_INDEX
1
但是0因为否定[^ [:cntrl:]]:
SELECT REGEXP_INSTR(CHR(31),'[^[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL;
MATCH_INDEX
0
现在,如果我们包含两个(或更多)混合了可打印和不可打印的字符,那么可能会有更多结果。 两者 [[:cntrl:]]
和[^[:cntrl:]]
都可以匹配,但它们只能匹配不同的内容。如果我们只从ascii #31
移动到ascii #64#31
,我们仍然希望[[:cntrl:]]
匹配(因为在第二个位置有一个不可打印的字符),但它现在应该返回2 ,因为不可打印位于第二位。
SELECT REGEXP_INSTR(CHR(64)||CHR(31),'[[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL;
MATCH_INDEX
2
现在[^[:cntrl:]]
也有机会匹配(在第一个位置):
SELECT REGEXP_INSTR(CHR(64)||CHR(31),'[^[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL;
MATCH_INDEX
1
当混合使用可打印和控制字符时,[[:cntrl:]]
和[^[:cntrl:]]
都可以匹配,但它们会匹配不同的索引。