web-dev-qa-db-fra.com

Recherche et suppression de caractères non ascii dans un Oracle Varchar2

Nous migrons actuellement une de nos bases de données Oracle vers UTF8 et nous avons trouvé quelques enregistrements qui sont proches de la limite de varchar de 4000 octets. Lorsque nous essayons de migrer ces enregistrements, ils échouent car ils contiennent des caractères qui deviennent des caractères UF8 multioctets. Ce que je veux faire dans PL/SQL, c'est localiser ces caractères pour voir ce qu'ils sont, puis les modifier ou les supprimer.

Je voudrais faire :

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

mais Oracle n'implémente pas la classe de caractères [: ascii:].

Existe-t-il un moyen simple de faire ce que je veux faire?

24
Paul Gilfedder

Dans un codage compatible ASCII à un octet (par exemple Latin-1), ASCII sont simplement des octets compris entre 0 et 127. Vous pouvez donc utiliser quelque chose comme [\x80-\xFF] pour détecter les caractères non ASCII.

7
Max Shawabkeh

Si vous utilisez la fonction ASCIISTR pour convertir l'Unicode en littéraux de la forme \nnnn, vous pouvez ensuite utiliser REGEXP_REPLACE pour éliminer ces littéraux, comme ça ...

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

... où champ et table sont respectivement vos noms de champ et de table.

23
Robb Smith

Je pense que cela fera l'affaire:

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

Je ne le recommanderais pas pour le code de production, mais cela a du sens et semble fonctionner:

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

La sélection peut ressembler à l'exemple suivant:

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

Ce qui suit fonctionne également:

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

Il existe probablement un moyen plus direct d'utiliser les expressions régulières. Avec de la chance, quelqu'un d'autre le fournira. Mais voici ce que je ferais sans avoir à consulter les manuels.

Créez une fonction PLSQL pour recevoir votre chaîne d'entrée et renvoyer un varchar2.

Dans la fonction PLSQL, faites un asciistr () de votre entrée. Le PLSQL est parce que cela peut retourner une chaîne de plus de 4000 et vous avez 32 Ko disponibles pour varchar2 dans PLSQL.

Cette fonction convertit les caractères non ASCII en notation\xxxx. Vous pouvez donc utiliser des expressions régulières pour les trouver et les supprimer. Retournez ensuite le résultat.

3
Jim Hudson

Essayez ce qui suit:

-- 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
2
Kok-Yan Lo

J'ai eu un problème similaire et j'ai blogué à ce sujet ici . J'ai commencé avec l'expression régulière pour les chiffres alpha, puis j'ai ajouté les quelques caractères de ponctuation de base que j'aimais:

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;

J'ai utilisé le vidage avec la variante 1016 pour donner les caractères hexadécimaux que je voulais remplacer que je pourrais ensuite utiliser dans un utl_raw.cast_to_varchar2.

2
Gary Myers

J'ai trouvé la réponse ici:

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;
/

Ensuite, exécutez ceci pour mettre à jour vos données

update o1dw.rate_ipselect_p_20110505
set NCANI = RECTIFY_NON_ASCII(NCANI);
2
Matt McGurie

Vous pouvez essayer quelque chose comme ce qui suit pour rechercher la colonne contenant des caractères non ascii:

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

Merci, cela a fonctionné pour mes besoins. BTW il y a un guillemet simple manquant dans l'exemple ci-dessus.

REGEXP_REPLACE (COLONNE, '[^' || CHR (32) || '-' || CHR (127) || ']', ''))

Je l'ai utilisé dans une fonction Word-wrap. Parfois, il y avait une NewLine/NL/CHR (10)/0A intégrée dans le texte entrant qui gâchait les choses.

0
allen

Veuillez noter que chaque fois que vous utilisez

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

Le moteur d'expression régulière d'Oracle correspondra également à certains caractères de la plage Latin-1: cela s'applique à tous les caractères qui ressemblent à ASCII caractères comme Ä-> A, Ö-> O, Ü-> U, etc., de sorte que [AZ] n'est pas ce que vous savez dans d'autres environnements comme, par exemple, Perl.

Au lieu de jouer avec les expressions régulières, essayez de changer pour le type de données NVARCHAR2 avant la mise à niveau du jeu de caractères.

Une autre approche: au lieu de couper une partie du contenu des champs, vous pouvez essayer la fonction SOUNDEX, à condition que votre base de données ne contienne que des caractères européens (c'est-à-dire latin-1). Ou vous écrivez simplement une fonction qui traduit les caractères de la plage Latin-1 en caractères ASCII, comme

  • å => a
  • ä => a
  • ö => o

bien sûr uniquement pour les blocs de texte dépassant 4000 octets lorsqu'ils sont transformés en UTF-8.

0
elwood

La réponse donnée par Francisco Hayoz est la meilleure. N'utilisez pas les fonctions pl/sql si sql peut le faire pour vous.

Voici le test simple dans 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))

Et "rep 127-255" est

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

c'est-à-dire que pour une raison quelconque, cette version d'Oracle ne remplace pas char (226) et plus. L'utilisation de '[' || chr (127) || '-' || chr (225) || ']' donne le résultat souhaité. Si vous avez besoin de remplacer d'autres caractères, ajoutez-les simplement à l'expression régulière ci-dessus ou utilisez le remplacement imbriqué | regexp_replace si le remplacement est différent alors '' (chaîne nulle).

0
Alex S