从Oracle Varchar2中查找和删除非ascii字符

时间:2010-02-10 11:42:11

标签: regex oracle ascii

我们目前正在将我们的一个oracle数据库迁移到UTF8,并且我们发现了一些接近4000字节varchar限制的记录。 当我们尝试迁移这些记录时,它们会失败,因为它们包含成为多字节UF8字符的字符。 我想在PL / SQL中做的是找到这些字符以查看它们是什么,然后更改它们或删除它们。

我想这样做:

SELECT REGEXP_REPLACE(COLUMN,'[^[:ascii:]],'')

但是Oracle没有实现[:ascii:]字符类。

有没有一种简单的方法可以做我想做的事情?

17 个答案:

答案 0 :(得分:22)

如果您使用ASCIISTR函数将Unicode转换为\nnnn格式的文字,则可以使用REGEXP_REPLACE删除这些文字,就像这样......

UPDATE table SET field = REGEXP_REPLACE(ASCIISTR(field), '\\[[:xdigit:]]{4}', '')

...其中field和table分别是你的字段和表名。

答案 1 :(得分:21)

我认为这样可以解决问题:

SELECT REGEXP_REPLACE(COLUMN, '[^[:print:]]', '')

答案 2 :(得分:9)

我不推荐它用于生产代码,但它有意义并且似乎有效:

SELECT REGEXP_REPLACE(COLUMN,'[^' || CHR(1) || '-' || CHR(127) || '],'')

答案 3 :(得分:7)

在单字节ASCII兼容编码(例如Latin-1)中,ASCII字符只是0到127范围内的字节。因此,您可以使用类似[\x80-\xFF]的内容来检测非ASCII字符。 / p>

答案 4 :(得分:4)

选择可能如下所示:

select nvalue from table
where length(asciistr(nvalue))!=length(nvalue)  
order by nvalue;

答案 5 :(得分:3)

使用正则表达式可能有更直接的方法。幸运的是,其他人会提供它。但这就是我要做的事情而不需要去手册。

创建一个PLSQL函数来接收输入字符串并返回一个varchar2。

在PLSQL函数中,执行输入的asciistr()。 PLSQL是因为它可能返回一个长于4000的字符串,并且在PLSQL中你有32K可用于varchar2。

该函数将非ASCII字符转换为\ xxxx表示法。因此,您可以使用正则表达式来查找和删除它们。然后返回结果。

答案 6 :(得分:3)

以下也有效:

select dump(a,1016), a from (
SELECT REGEXP_REPLACE (
          CONVERT (
             '3735844533120%$03  ',
             'US7ASCII',
             'WE8ISO8859P1'),
          '[^!@/\.,;:<>#$%&()_=[:alnum:][:blank:]]') a
  FROM DUAL);

答案 7 :(得分:2)

尝试以下方法:

-- To detect
select 1 from dual
where regexp_like(trim('xx test text æ¸¬è© ¦ “xmx” number²'),'['||chr(128)||'-'||chr(255)||']','in')

-- To strip out
select regexp_replace(trim('xx test text æ¸¬è© ¦ “xmxmx” number²'),'['||chr(128)||'-'||chr(255)||']','',1,0,'in')
from dual

答案 8 :(得分:2)

我在这里找到了答案:

http://www.squaredba.com/remove-non-ascii-characters-from-a-column-255.html

CREATE OR REPLACE FUNCTION O1DW.RECTIFY_NON_ASCII(INPUT_STR IN VARCHAR2)
RETURN VARCHAR2
IS
str VARCHAR2(2000);
act number :=0;
cnt number :=0;
askey number :=0;
OUTPUT_STR VARCHAR2(2000);
begin
str:=’^'||TO_CHAR(INPUT_STR)||’^';
cnt:=length(str);
for i in 1 .. cnt loop
askey :=0;
select ascii(substr(str,i,1)) into askey
from dual;
if askey < 32 or askey >=127 then
str :=’^'||REPLACE(str, CHR(askey),”);
end if;
end loop;
OUTPUT_STR := trim(ltrim(rtrim(trim(str),’^'),’^'));
RETURN (OUTPUT_STR);
end;
/

然后运行此更新数据

update o1dw.rate_ipselect_p_20110505
set NCANI = RECTIFY_NON_ASCII(NCANI);

答案 9 :(得分:2)

我有一个类似的问题,并在博客上发表了here。 我开始使用alpha数字的正则表达式,然后在我喜欢的几个基本标点字符中添加:

select dump(a,1016), a, b
from
 (select regexp_replace(COLUMN,'[[:alnum:]/''%()> -.:=;[]','') a,
         COLUMN b
  from TABLE)
where a is not null
order by a;

我使用了1016变体的dump来给出我想要替换的十六进制字符,然后我可以在utl_raw.cast_to_varchar2中使用。

答案 10 :(得分:0)

Francisco Hayoz给出的答案是最好的。如果sql可以为你做的话,不要使用pl / sql函数。

以下是Oracle 11.2.03中的简单测试

select s
     , regexp_replace(s,'[^'||chr(1)||'-'||chr(127)||']','') "rep ^1-127"
     , dump(regexp_replace(s,'['||chr(127)||'-'||chr(225)||']','')) "rep 127-255"
from (
select listagg(c, '') within group (order by c) s
  from (select 127+level l,chr(127+level) c from dual connect by level < 129))

&#34; rep 127-255&#34;是

Typ = 1 Len = 30:226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255

即由于某种原因,此版本的Oracle不会替换char(226)及更高版本。 使用&#39; [&#39; || chr(127)||&#39; - &#39; || chr(225)||&#39;]&#39;给出了期望的结果。 如果您需要替换其他字符,只需将它们添加到上面的正则表达式中,或者如果替换不同,则使用嵌套的替换| regexp_replace,然后使用&#39;&#39; (空字符串)。

答案 11 :(得分:0)

谢谢,这符合我的目的。顺便说一句,上面的示例中缺少单引号。

REGEXP_REPLACE(COLUMN,&#39; [^&#39; || CHR(32)||&#39; - &#39; || CHR(127)||&#39;]&#39 ;,&#39;&#39;))

我在自动换行功能中使用它。偶尔在传入的文本中有一个嵌入的NewLine / NL / CHR(10)/ 0A,这些都搞砸了。

答案 12 :(得分:0)

请注意,无论何时使用

regexp_like(column, '[A-Z]')

Oracle的regexp引擎也将匹配Latin-1范围内的某些字符:这适用于所有与ASCII字符类似的字符,如Ä-&gt; A,Ö-&gt; O,Ü-&gt; U等。,所以[AZ]不是你所知道的其他环境,比如Perl。

尝试在字符集升级之前更改NVARCHAR2数据类型,而不是摆弄正则表达式。

另一种方法:如果您的数据库仅包含欧洲字符(即Latin-1)字符,您可以尝试使用SOUNDEX函数,而不是删除部分字段的内容。或者您只需编写一个函数,将Latin-1范围内的字符转换为类似的ASCII字符,如

  • å=&gt;一个
  • ä=&gt;一个
  • ö=&gt; Ø

当然仅适用于转换为UTF-8时超过4000字节的文本块。

答案 13 :(得分:0)

您可以尝试以下内容来搜索包含非ascii字符的列:

select * from your_table where your_col <> asciistr(your_col);

答案 14 :(得分:0)

我也有类似的要求(为避免这种丑陋的ORA-31061: XDB error: special char to escaped char conversion failed.),但必须保持换行符。

我从出色的评论中尝试了这一点

setlocal enableDelayedExpansion
:: parse the ProgId
FOR /F "usebackq tokens=1,2,* delims==" %%A IN (`REG QUERY "HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice" /v ProgId 2^>NUL ^| more +2`) DO (
    SET browserstub=%%C
    :: parse for Class information
    FOR /F "usebackq tokens=1,2,* delims==" %%R IN (`REG QUERY "HKCR\!browserstub!" /v AppUserModelId 2^>NUL`) DO SET "browser=%%T"
    FOR /F "usebackq tokens=1,2,* delims==" %%R IN (`REG QUERY "HKCR\!browserstub!\shell" /ve 2^>NUL`) DO SET "browserverb=%%T"
    FOR /F "usebackq tokens=1,2,* delims==" %%R IN (`REG QUERY "HKCR\!browserstub!\shell\!browserverb!\command" /ve 2^>NUL`) DO SET "browsercmd=%%T"
)
:: display results
SET browser

但是得到了这个ORA-12728: invalid range in regular expression

但是它引导我找到解决方案:

'[^ -~|[:space:]]'

(在我的TOAD工具中)显示为

in my toad tool

  • 替换所有select t.*, regexp_replace(deta, '[^[:print:]|[:space:]]', '#') from (select '- <- strangest thing here, and I want to keep line break after -' deta from dual ) t =>不在集合中的字符(打印^或空格[:print:]字符)

答案 15 :(得分:-2)

这样做,它会起作用。

trim(replace(ntwk_slctor_key_txt, chr(0), ''))

答案 16 :(得分:-3)

我回答这个问题有点晚了,但最近遇到了同样的问题(人们把各种各样的东西剪成并粘贴成字符串而我们不知道它是什么)。 以下是一种简单的字符白名单方法:

SELECT est.clients_ref
  ,TRANSLATE (
              est.clients_ref
             ,   'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890#$%^&*()_+-={}|[]:";<>?,./'
              || REPLACE (
                          TRANSLATE (
                                     est.clients_ref
                                    ,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890#$%^&*()_+-={}|[]:";<>?,./'
                                    ,'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
                                    )
                         ,'~'
                         )
             ,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890#$%^&*()_+-={}|[]:";<>?,./'
             )
      clean_ref

FROM edms_staging_table est