web-dev-qa-db-de.com

Trimmen Sie abschließende Leerzeichen mit PostgreSQL

Ich habe eine Spalte eventDate, die nachgestellte Leerzeichen enthält. Ich versuche, sie mit der PostgreSQL-Funktion TRIM() zu entfernen. Genauer gesagt renne ich:

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

Die nachgestellten Leerzeichen verschwinden jedoch nicht. Wenn ich versuche, ein anderes Zeichen aus dem Datum (z. B. eine Zahl) zu entfernen, wird es auch nicht entfernt. Wenn ich das Handbuch richtig lese , sollte dies funktionieren. Irgendwelche Gedanken?

32
zgall1

Es gibt viele verschiedene unsichtbare Zeichen. Viele von ihnen haben die Eigenschaft WSpace=Y ("Whitespace") in Unicode. Einige Sonderzeichen werden jedoch nicht als "Leerzeichen" betrachtet und haben immer noch keine sichtbare Darstellung. Die hervorragenden Wikipedia-Artikel über Leerzeichen (Interpunktion) und Leerzeichen sollten Ihnen eine Idee geben.

<rant> Unicode ist ein Witz: Es werden viele exotische Zeichen eingeführt, die hauptsächlich dazu dienen, Menschen zu verwirren. </ rant>

Die Standard-SQL-Funktion trim() schneidet standardmäßig nur das grundlegende lateinische Leerzeichen ab (Unicode: U + 0020/ASCII 32) rtrim() und ltrim() Varianten. Ihr Aufruf zielt auch nur auf diesen bestimmten Charakter.

Verwenden Sie stattdessen reguläre Ausdrücke mit regexp_replace() .

Nachlauf

Um alle nachfolgenden Leerzeichen (aber keine Leerzeichen innerhalb der Zeichenfolge zu entfernen ):

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

Der reguläre Ausdruck erklärte:
\s .. Klassenkürzel für reguläre Ausdrücke für [[:space:]]
das ist die Menge der Leerzeichen - siehe Einschränkungen weiter unten
+ .. 1 oder mehr aufeinanderfolgende Übereinstimmungen
$ .. Ende der Zeichenfolge

Demo:

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

Kehrt zurück:

inner white|

Ja, das ist ein single Backslash (\). Details in dieser verwandten Antwort.

Führen

So entfernen Sie alle führenden Leerzeichen (jedoch keine Leerzeichen in der Zeichenfolge):

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

^ .. Stringanfang

Beide

Zum Entfernen von both können Sie die folgenden Funktionsaufrufe verketten:

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

Oder Sie können beide in einem einzigen Aufruf mit zwei Zweigen kombinieren.
Fügen Sie 'g' Als vierten Parameter hinzu, um alle Übereinstimmungen zu ersetzen, nicht nur die erste:

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

Aber das sollte normalerweise schneller gehen mit substring() :

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

\S .. alles aber Leerraum
(?:re)Satz nicht erfassender Klammern
.* .. eine beliebige Zeichenfolge von 0-n Zeichen

Oder eine davon:

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

(re) .. Klammern setzen

Nimmt effektiv das erste Nicht-Leerzeichen und alles bis zum letzten Nicht-Leerzeichen, falls verfügbar.

Leerzeichen?

Es gibt noch ein paar weitere verwandte Zeichen, die in Unicode nicht als "Leerzeichen" klassifiziert sind - also nicht in der Zeichenklasse [[:space:]] Enthalten.

Diese werden für mich in pgAdmin als unsichtbare Glyphen gedruckt: "Mongolischer Vokal", "Leerzeichen mit null Breite", "Nicht-Schreiner mit null Breite", "Schreiner mit null Breite":

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

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

Zwei weitere, die als sichtbar Glyphen in pgAdmin gedruckt werden, aber in meinem Browser nicht sichtbar sind: "Word Joiner", "nicht unterbrechendes Leerzeichen ohne Breite":

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

Ob Zeichen unsichtbar gemacht werden oder nicht, hängt letztendlich auch von der für die Anzeige verwendeten Schriftart ab.

Um auch all diese zu entfernen, ersetzen Sie '\s' Durch '[\s\u180e\u200B\u200C\u200D\u2060\uFEFF]' Oder '[\s᠎​‌‍⁠]' (Nicht sichtbare Zeichen am Ende beachten!).
Beispiel statt:

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

verwenden:

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

oder:

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

Einschränkungen

Es gibt auch die Posix-Zeichenklasse [[:graph:]] soll "sichtbare Zeichen" darstellen. Beispiel:

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

Es funktioniert zuverlässig für ASCII Zeichen in jedem Setup (wo es sich auf [\x21-\x7E] Zusammensetzt), aber darüber hinaus sind Sie derzeit (inkl. S. 10) auf Informationen des Basiswerts angewiesen OS (um ctype zu definieren) und möglicherweise Gebietsschemaeinstellungen.

Genau genommen ist das der Fall für every Verweise auf eine Zeichenklasse, aber es scheint mehr Uneinigkeit mit den weniger häufig verwendeten wie graph zu geben. Möglicherweise müssen Sie jedoch der Zeichenklasse [[:space:]] (Kurzform \s) Weitere Zeichen hinzufügen, um alle Whitespace-Zeichen abzufangen. Like: \u2007, \u202f Und \u00a0 Scheinen auch für @XiCoN JFS zu fehlen .

Das Handbuch:

Innerhalb eines Klammerausdrucks steht der Name einer Zeichenklasse, die in [: Und :] Eingeschlossen ist, für die Liste aller Zeichen, die zu dieser Klasse gehören. Standardzeichenklassennamen sind: alnum, alpha, blank, cntrl, digit, graph, lower, print, punct, space, upper, xdigit. Diese stehen für die in ctype definierten Zeichenklassen. Ein Gebietsschema kann andere bereitstellen.

Meine kühne Betonung.

Beachten Sie auch diese Einschränkung, die behoben mit Postgres 1 :

Korrektur der Zeichenklassenbehandlung für reguläre Ausdrücke bei großen Zeichencodes, insbesondere bei Unicode-Zeichen über U+7FF (Tom Lane)

Bisher wurden solche Zeichen nie als zu ländereinstellungsabhängigen Zeichenklassen gehörig erkannt, wie z. B. [[:alpha:]].

61

Wenn Ihr Whitespace mehr als nur den Metawert space enthält, müssen Sie regexp_replace:

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

Im obigen Beispiel beschränke ich den Rückgabewert in ( und ) nur damit du es leicht kannst siehe dass das Ersetzen von Regex in einer psql-Eingabeaufforderung funktioniert. Also werden Sie diese in Ihrem Code entfernen wollen.

2
Cody Caughlan

Es sollte so funktionieren, wie Sie es handhaben, aber es ist schwer zu sagen, ohne die spezifische Zeichenfolge zu kennen.

Wenn Sie nur führende Leerzeichen abschneiden, möchten Sie möglicherweise das präzisere Formular verwenden:

SELECT RTRIM(eventDate) 
FROM EventDates;

Dies ist ein kleiner Test um Ihnen zu zeigen, dass es funktioniert. Sagen Sie uns, ob es funktioniert!

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

Es gibt: devosystem

0
devosystem sarl