:ascii:
不是有效的字符类,即使它是,它并不显得你正在尝试到这里(ASCII不包含非打印字符)。可以找到有效的课程here。
实际上,如果您在原始查询中将:ascii:
替换为:print:
,那么它确实会返回每个POLINE.DESCRIPTION
中的第一个不可打印字符的位置。 (如果你没有返回,这可能是因为您的DESCRIPTION
数据实际上是所有可打印的。)
但正如你所说,你要在POLINE
识别每非打印字符在每个DESCRIPTION
,一些变化将需要。我会列举一个以每场比赛为起点的例子。
在此示例中,每个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:
)。但是,对于另一种选择,还包括第二个变体,它只检查每个字符是否在前31个ASCII字符中。
这两个示例查询都将索引每个DESCRIPTION
的每个字符,并检查它是否可打印,并收集每个候选DESCRIPTION
中每个不可打印字符的ASCII码和位置。这里的示例表具有长度为256个字符的DESCRIPTION
s,所以这被用作笛卡尔连接中的最大索引。
请注意,这些都是效率不高,并设计让EVERY比赛。如果您最终只需要第一场比赛,您的原始查询替换为: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;
在我们的测试数据,这些查询会产生相同的输出。我们应该预计这将有两个命中(第17和23号)在Sherlock
DESCRIPTION
和31命中的前64-ascii DESCRIPTION
。
结果:
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:]]
可以匹配,但他们会匹配不同的指数。
谢谢max092012。你是否希望每行都出现一个不可打印的字符,或者只是第一次出现? – alexgibbs