web-dev-qa-db-fra.com

Coupez les espaces de fin avec PostgreSQL

J'ai une colonne eventDate qui contient des espaces de fin. J'essaie de les supprimer avec la fonction PostgreSQL TRIM(). Plus précisément, je lance:

SELECT TRIM(both ' ' from eventDate) 
FROM EventDates;

Cependant, les espaces de fuite ne disparaissent pas. De plus, lorsque j'essaie de couper un autre caractère à partir de la date (comme un nombre), il ne coupe pas non plus. Si je lis le manuel correctement cela devrait fonctionner. Des pensées?

32
zgall1

Il existe de nombreux personnages invisibles différents. Beaucoup d'entre eux ont la propriété WSpace=Y ("Espace blanc") en Unicode. Mais certains caractères spéciaux ne sont pas considérés comme des "espaces blancs" et n'ont toujours pas de représentation visible. Les excellents articles de Wikipédia sur espace (ponctuation) et caractères blancs devraient vous donner une idée.

<rant> Unicode est nul à cet égard: introduire beaucoup de personnages exotiques qui servent principalement à embrouiller les gens. </rant>

La fonction SQL trim() standard par défaut ne coupe que le caractère d'espace latin de base (Unicode: U + 0020/ASCII 32). Idem avec le rtrim() et ltrim() variantes. Votre appel cible également uniquement ce caractère particulier.

Utilisez plutôt des expressions régulières avec regexp_replace() .

Trailing

Pour supprimer tous les tirets espace blanc (mais pas les espaces blancs à l'intérieur la chaîne ):

SELECT regexp_replace(eventdate, '\s+$', '') FROM eventdates;

L'expression régulière a expliqué:
\s .. raccourci de classe d'expression régulière pour [[:space:]]
qui est l'ensemble des caractères d'espace blanc - voir les limitations ci-dessous
+ .. 1 ou plusieurs correspondances consécutives
$ .. fin de chaîne

Démo:

SELECT regexp_replace('inner white   ', '\s+$', '') || '|'

Résultats:

inner white|

Oui, c'est une barre oblique inversée simple (\). Détails dans cette réponse connexe.

De premier plan

Pour supprimer tous les espaces blancs en tête (mais pas les espaces blancs à l'intérieur de la chaîne):

regexp_replace(eventdate, '^\s+', '')

^ .. début de la chaîne

Tous les deux

Pour supprimer les deux, vous pouvez enchaîner les appels de fonction ci-dessus:

regexp_replace(regexp_replace(eventdate, '^\s+', ''), '\s+$', '')

Ou vous pouvez combiner les deux en un seul appel avec deux branches .
Ajoutez 'g' Comme quatrième paramètre pour remplacer toutes les correspondances, pas seulement la première:

regexp_replace(eventdate, '^\s+|\s+$', '', 'g')

Mais cela devrait généralement être plus rapide avec substring() :

substring(eventdate, '\S(?:.*\S)*')

\S .. tout mais espace blanc
(?:re)jeu de parenthèses non capturant
.* .. toute chaîne de 0 à n caractères

Ou l'un d'eux:

substring(eventdate, '^\s*(.*\S)')
substring(eventdate, '(\S.*\S)')

(re) .. Capture d'un ensemble de parenthèses

Prend effectivement le premier caractère non blanc et tout jusqu'au dernier caractère non blanc si disponible.

Espace blanc?

Il y en a quelques autres caractères apparentés qui ne sont pas classés comme "espaces blancs" dans Unicode - donc non contenus dans la classe de caractères [[:space:]].

Ceux-ci s'affichent sous forme de glyphes invisibles dans pgAdmin pour moi: "voyelle mongole", "espace de largeur nulle", "non-jointeur de largeur zéro", "jointeur de largeur zéro":

SELECT E'\u180e', E'\u200B', E'\u200C', E'\u200D';

'᠎' | '​' | '‌' | '‍'

Deux de plus, imprimant en tant que visible glyphes dans pgAdmin, mais invisibles dans mon navigateur: "Word joiner", "espace nul insécable de largeur nulle":

SELECT E'\u2060', E'\uFEFF';
'⁠' | ''

En fin de compte, le fait que les caractères soient rendus invisibles ou non dépend également de la police utilisée pour l'affichage.

Pour supprimer tout cela également, remplacez '\s' Par '[\s\u180e\u200B\u200C\u200D\u2060\uFEFF]' Ou '[\s᠎​‌‍⁠]' (Notez les caractères invisibles de fin!).
Exemple, au lieu de:

regexp_replace(eventdate, '\s+$', '')

utilisation:

regexp_replace(eventdate, '[\s\u180e\u200B\u200C\u200D\u2060\uFEFF]+$', '')

ou:

regexp_replace(eventdate, '[\s᠎​‌‍⁠]+$', '')  -- note invisible characters

Limites

Il y a aussi la classe de caractères Posix [[:graph:]] censée représenter des "caractères visibles". Exemple:

substring(eventdate, '([[:graph:]].*[[:graph:]])')

Cela fonctionne de manière fiable pour ASCII caractères dans chaque configuration (où cela se résume à [\x21-\x7E])), Mais au-delà de cela vous actuellement (y compris pg 10) dépendez des informations fournies par le sous-jacent OS (pour définir ctype) et éventuellement les paramètres régionaux.

Strictement parlant, c'est le cas pour tous les référence à une classe de caractères, mais il semble y avoir plus de désaccord avec les moins utilisés comme graphe. Mais vous devrez peut-être ajouter plus de caractères à la classe de caractères [[:space:]] (Raccourci \s) Pour attraper tous les caractères d'espacement. comme: \u2007, \u202f Et \u00a0 Semblent également manquer pour @XiCoN JFS .

Le manuel:

Dans une expression entre crochets, le nom d'une classe de caractères entre [: Et :] Représente la liste de tous les caractères appartenant à cette classe. Les noms de classe de caractères standard sont: alnum, alpha, blank, cntrl, digit, graph, lower, print, punct, space, upper, xdigit. Ceux-ci représentent les classes de caractères définies dans ctype. Une locale peut en fournir d'autres.

Accentuation sur moi.

Notez également cette limitation qui était corrigée avec Postgres 1 :

Correction de la gestion des classes de caractères des expressions régulières pour les grands codes de caractères, en particulier les caractères Unicode au-dessus de U+7FF (Tom Lane)

Auparavant, ces caractères n'étaient jamais reconnus comme appartenant à des classes de caractères dépendantes de l'environnement local telles que [[:alpha:]].

61
Erwin Brandstetter

Si votre espace est supérieur à la simple méta space, vous devrez utiliser regexp_replace:

 SELECT '(' || REGEXP_REPLACE(eventDate, E'[[:space:]]', '', 'g') || ')' 
 FROM EventDates;

Dans l'exemple ci-dessus, je limite la valeur de retour dans ( et ) juste pour que vous puissiez facilement voir que le regex replace fonctionne dans une invite psql. Vous voudrez donc supprimer ceux de votre code.

2
Cody Caughlan

Cela devrait fonctionner de la façon dont vous le manipulez, mais c'est difficile à dire sans connaître la chaîne spécifique.

Si vous ne supprimez que les espaces de début, vous pouvez utiliser le formulaire le plus concis:

SELECT RTRIM(eventDate) 
FROM EventDates;

Ceci est un petit test pour vous montrer que cela fonctionne. Dites-nous si cela fonctionne!

2
ArthurChamz
SELECT  replace(('       devo    system      ') ,' ','');

Il donne: devosystem

0
devosystem sarl