Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   RegEx: fehlerhafter Zeilenumbruch (PostgreSQL) (https://www.delphipraxis.net/189708-regex-fehlerhafter-zeilenumbruch-postgresql.html)

himitsu 12. Jul 2016 10:20

RegEx: fehlerhafter Zeilenumbruch (PostgreSQL)
 
Moin,

bis gestern funktionierte ein RegEx und dann urplötzlich nicht mehr und ich weiß nicht warum und keine hat (angeblich) an den Settings gespielt.
Nur ein Kollege am Sprach-/Datumsformat des Servers, um was zu Testen, aber auch ein Zurücksetzen half natürlich nichts.
Und das Neustarten des Server/DBServer brachte auch nichts.

Was ich endlich rausfand ist, daß Zeilenumbrüche und \s sich im Verhalten verändert haben.
Modifier sind nur "mi" (MultiLine und CaseInsensitive)

SQL-Code:
CREATE OR REPLACE FUNCTION INI_GetValue(ini TEXT, section VARCHAR, name VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS VARCHAR AS $$
  -- SELECT+regexp_matches liefert "keinen" Datensatz, wenn nichts gefunden wurde, daher das SUBSELECT mit COALESCE
  SELECT nullif(coalesce((SELECT (regexp_matches(ini, '^\\[' || section || '\\]\\s*$[^\\[]*^' || name || '=(.*)$', 'mi'))[1]), defvalue), '')
$$ LANGUAGE SQL IMMUTABLE;
Also, das erste ^ findet noch einen Zeilenumbruch, \s trifft plözlich auch Zeilenumgrüche, obwohl z.B. der Modifier "s" (SingleLine) nicht angegeben ist.
und weitere ^ oder $ treffen keinen Zeilenumbruche im Text mehr.

Die Doppelten
Delphi-Quellcode:
\\
sind korrect, da es ja einmal das Escape von Postgres und dann nochmal vom RegEx ist.
Allerdings ging es bis gestern noch, daß ich in Postgres die \ nicht brauchte, wenn es keinen "Steuerbefehl" ergab, also z.B. kein Buchstabe folgte, womit auch
Delphi-Quellcode:
'\[xxx\]'
den Abschnitt traf und heute nur noch
Delphi-Quellcode:
'\\[xxx\\]'
funktioniert.

Code:
[zzz]
bbb=nee
[xxx]
aaa=nee
bbb=ok
ccc=nee
mit "^\[xxx\]\s*$[^\[]*^bbb=(.*)$" und "mi" sollte "ok" im ersten Match liefern.
https://regex101.com/r/uQ4aP9/1



Mein aktueller "Lösungsansatz" gefällt mir so garnicht, voralem da man im RegEx danach garnicht mehr erkennt, was der macht.
  • an den Text vorne und hinten ein
    Delphi-Quellcode:
    '\n'
    anhängen
  • Delphi-Quellcode:
    \s
    durch
    Delphi-Quellcode:
    [ \t]
    ersetzen
  • Delphi-Quellcode:
    .*
    durch
    Delphi-Quellcode:
    [^\r\n]*
  • mühevoll die Zeilenumbrüche ^ und $ mit
    Delphi-Quellcode:
    \r?\n([^\r\n]*\r?\n)
  • usw.



Ich hatte einfach nur versucht vorhandene "Funktionen" endlich mal in Funktionen zu packen
und als ich fertig war und es testen wollte, da ging es anfangs und dann mittendrin plötzlich nicht mehr.
SQL-Code:
--value = INI_GetValue(ini, section, name, [defvalue])
--ini  = INI_SetValue(ini, section, name, value)
--value = SL_GetValue(list, name, [defvalue])
--list = SL_SetValue(list, name, value)

CREATE OR REPLACE FUNCTION INI_GetValue(ini TEXT, section VARCHAR, name VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS VARCHAR AS $$
  -- SELECT+regexp_matches liefert "keinen" Datensatz, wenn nichts gefunden wurde, daher das SUBSELECT mit COALESCE
  SELECT nullif(coalesce((SELECT (regexp_matches(ini, '^\\[' || section || '\\]\\s*$[^\\[]*^' || name || '=(.*)$', 'mi'))[1]), defvalue), '')
$$ LANGUAGE SQL IMMUTABLE;

CREATE OR REPLACE FUNCTION INI_SetValue(ini TEXT, section VARCHAR, name VARCHAR, value VARCHAR) RETURNS TEXT AS $$
DECLARE seA TEXT;
        seN TEXT;
BEGIN
  -- altes weg ... der Lookbehind will nicht, wie er soll, als erst Section extrahieren und dann darin den Value entferen
  --ini := regexp_replace(ini, '(?<!^\\[' || section || '\\]\\s*$[^\\[]*)^' || name || '=(.*)$', '', 'mi');
  seA := (regexp_matches(ini, '^\\[' || section || '\\]\\s*$[^\\[]*^' || name || '=(.*)$', 'mi'))[0];
  seN := regexp_replace(seA, '^' || name || '=(.*)$', '', 'mi');
  ini := replace(ini, seA, seN);
  -- neues rein
  IF regexp_matches(ini, '^\\[' || section || '\\]\\s*$', 'mi') IS NULL THEN
    ini := concat(ini, '\r\n[', section, ']');
  END IF;
  IF (value <> '') and (value IS DISTINCT FROM defvalue) THEN
    ini := regexp_replace(ini, '^\\[' || section || '\\]\\s*$', concat('[', section, ']\r\n', name, '=', value), 'mi');
  END IF;
  -- fertig
  return ini;
END $$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION SL_GetValue(list TEXT, name VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS VARCHAR AS $$
  -- SELECT+regexp_matches liefert "keinen" Datensatz, wenn nichts gefunden wurde, daher das SUBSELECT mit COALESCE
  SELECT nullif((SELECT (regexp_matches(list, '^' || name || '=(.*)$', 'mi'))[1]), '')
$$ LANGUAGE SQL IMMUTABLE;

CREATE OR REPLACE FUNCTION SL_SetValue(list TEXT, name VARCHAR, value VARCHAR) RETURNS TEXT AS $$
BEGIN
  -- altes weg
  list := regexp_replace(list, '=(.*)$', '', 'mi');
  -- neues rein
  IF (value <> '') and (value IS DISTINCT FROM defvalue) THEN
    list := concat(list, '\r\n', name, '=', value);
  END IF;
  -- sortieren
  return trim(array_to_string(ARRAY(SELECT regexp_split_to_table(list, '\r?\n' , 'mi') ORDER BY 1), '\r\n')' \r\n');
END $$ LANGUAGE plpgsql;

-- Test = [zzz]\nbbb=nee\n[xxx]\naaa=nee\nbbb=ok\nccc=nee
-- Test2 = aaa=nee\nbbb=ok\nccc=nee

SELECT INI_GetValue(GetSetting('Test'), 'xxx', 'bbb')
SELECT SL_GetValue(GetSetting('Test2'), 'bbb')
Ein paar kleine "Bugs" gibt es zusätzlich noch, denn
Delphi-Quellcode:
$[^\[]*^
ist falsch, da es nicht "Alles außer [" sondern "Alles außer wenn [ am Zeilenanfang" heißen müsste,
aber k.A. wie das gehn soll, da Lookarounds keine
Delphi-Quellcode:
.*
drin haben wollen. :cry:

himitsu 14. Jul 2016 16:18

AW: RegEx: fehlerhafter Zeilenumbruch (PostgreSQL)
 
Hab zwar immernoch keine Ahnung, was das Problem ist :?, aber zumindestens hab ich erstmal wider eine funktionierende Version.

SQL-Code:
-- INI-TEXT und StringList-TEXT (Name=Value) in der DB behandeln

  -- line = SL_GetLine (list,         index)
  -- value = SL_GetValue(list,         name,       [defvalue])
  -- list = SL_SetValue(list,         name, value, [defvalue])
  -- value = INI_GetValue(ini, section, name,       [defvalue])
  -- ini  = INI_SetValue(ini, section, name, value, [defvalue])

  -- Initial : SELECT SetSetting('Test', 'aaa=nee\r\nbbb=ok\r\nccc=nee'), SetSetting('Test2', '[zzz]\r\nbbb=nee\r\n[xxx]\r\naaa=nee\r\nbbb=ok\r\nccc=nee')
  -- SL-Test : SELECT GetTextSetting('Test'), SL_GetValue(GetSetting('Test'), 'bbb'), SL_SetValue(GetSetting('Test'), 'bbb', 'NEU'), SL_SetValue(GetSetting('Test'), 'bbc', 'NEU')
  -- SL-Test2 : SELECT GetTextSetting('Test'), SL_GetLine(GetSetting('Test'), 1), SL_GetLine(GetSetting('Test'), 3), SL_GetLine(GetSetting('Test'), 4)
  -- INI-Test : SELECT GetTextSetting('Test2'), INI_GetValue(GetSetting('Test2'), 'xxx', 'bbb'), INI_SetValue(GetSetting('Test2'), 'xxx', 'bbb', 'NEU'), INI_SetValue(GetSetting('Test2'), 'xxx', 'bbc', 'NEU')

  -- Zeile aus einem StringList-TEXT auslesen
  CREATE OR REPLACE FUNCTION SL_GetLine(list TEXT, line INTEGER) RETURNS VARCHAR AS $$
    --SELECT (regexp_matches(list, '^(.*)$', 'm'))[line]
    SELECT (regexp_split_to_array(list, '\\r?\\n'))[line]
  $$ LANGUAGE SQL IMMUTABLE;
  --

  -- Value aus einem StringList-TEXT auslesen -> Name=Value
  CREATE OR REPLACE FUNCTION SL_GetValue(list TEXT, name VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS VARCHAR AS $$
    -- SELECT+regexp_matches liefert "keinen" Datensatz, wenn nichts gefunden wurde, daher das SUBSELECT mit COALESCE
    SELECT coalesce((SELECT (regexp_matches(list, '^' || name || '=(.*)$', 'mi'))[1]), defvalue)
  $$ LANGUAGE SQL IMMUTABLE;
  --

  -- Value in einem StringList-TEXT ändern -> Name=Value
  CREATE OR REPLACE FUNCTION SL_SetValue(list TEXT, name VARCHAR, value VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS TEXT AS $$
  BEGIN
    -- altes weg
    list := regexp_replace(list, '^' || name ||  '=(.*)$', '', 'mi');
    -- neues rein
    IF (value <> '') and (value IS DISTINCT FROM defvalue) THEN
      list := concat(list, '\r\n', name, '=', value);
    END IF;
    -- sortieren
    RETURN trim(array_to_string(ARRAY(SELECT regexp_split_to_table(list, '\r?\n' , 'mi') ORDER BY 1), '\r\n'), ' \r\n');
  END $$ LANGUAGE plpgsql;
  --

  -- Value aus einem INI-TEXT auslesen -> [Section] Name=Value
  CREATE OR REPLACE FUNCTION INI_GetValue(ini TEXT, section VARCHAR, name VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS VARCHAR AS $$
    -- SELECT+regexp_matches liefert "keinen" Datensatz, wenn nichts gefunden wurde, daher das SUBSELECT mit COALESCE
    --SELECT coalesce((SELECT (regexp_matches(ini, '^\\[' || section || '\\]\\s*$[^\\[]*^' || name || '=(.*)$', 'mi'))[1]), defvalue)

    -- Aus unerfindlichen Gründen funktioniert das Zeilenendematching nicht mehr, daher manuell die Zeilenumbrüche suchen http://www.delphipraxis.net/189708-regex-fehlerhafter-zeilenumbruch-postgresql.html
    SELECT coalesce((SELECT (regexp_matches('\\n' || ini || '\\n', '\\r?\\n\\[' || section || '\\][ \\t]*\\r?\\n?[^\\[]*\\r?\\n' || name || '=([^\\r\\n]*)\\r?\\n', 'i'))[1]), defvalue)
  $$ LANGUAGE SQL IMMUTABLE;
  --
 
  -- Value in einem INI-TEXT ändern -> [Section] Name=Value
  CREATE OR REPLACE FUNCTION INI_SetValue(ini TEXT, section VARCHAR, name VARCHAR, value VARCHAR, defvalue VARCHAR DEFAULT '') RETURNS TEXT AS $$
  DECLARE seA TEXT;
          seN TEXT;
  BEGIN
    /*
    -- altes weg ... der Lookbehind will nicht, wie er soll, also zuerst Section extrahieren und dann darin den Value entferen
    --ini := regexp_replace(ini, '(?<!^\\[' || section || '\\]\\s*$[^\\[]*)^' || name || '=(.*)$', '', 'mi');
    seA := (regexp_matches(ini, '^(\\[' || section || '\\]\\s*$[^\\[]*^' || name || '=.*)$', 'mi'))[0];
    seN := regexp_replace(seA, '^' || name || '=.*$', '', 'mi');
    IF seA <> '' THEN
      ini := replace(ini, seA, seN);
    END IF;
    -- neues rein
    IF regexp_matches(ini, '^\\[' || section || '\\]\\s*$', 'mi') IS NOT NULL THEN
    ELSE
      ini := concat(ini, '\r\n[', section, ']');
    END IF;
    IF (value <> '') and (value IS DISTINCT FROM defvalue) THEN
      ini := regexp_replace(ini, '^\\[' || section || '\\]\\s*$', concat('[', section, ']\r\n', name, '=', value), 'mi');
    END IF;
    -- fertig
    RETURN trim(regexp_replace(ini, '\\r?\\n([ \\t]*\\r?\\n)+', '\r\n', ''), ' \r\n');
    */

    -- Aus unerfindlichen Gründen funktioniert das Zeilenendematching nicht mehr, daher manuell die Zeilenumbrüche suchen http://www.delphipraxis.net/189708-regex-fehlerhafter-zeilenumbruch-postgresql.html
    ini := '\n' || ini || '\n';
    -- altes weg
    seA := (regexp_matches(ini, '(\\r?\\n\\[' || section || '\\][ \\t]*\\r?\\n?[^\\[]*\\r?\\n' || name || '=([^\\r\\n]*)\\r?\\n)', 'i'))[1];
    seN := regexp_replace(seA, '^' || name || '=.*$', '', 'mi');
    IF seA <> '' THEN
      ini := replace(ini, seA, seN);
    END IF;
    -- neues rein
    IF regexp_matches(ini, '\\r?\\n\\[' || section || '\\][ \\t]*\\r?\\n', 'i') IS NOT NULL THEN
    ELSE
      ini := concat(ini, '\r\n[', section, ']\r\n');
    END IF;
    IF (value <> '') and (value IS DISTINCT FROM defvalue) THEN
      ini := regexp_replace(ini, '\\r?\\n\\[' || section || '\\][ \\t]*\\r?\\n', concat('\r\n[', section, ']\r\n', name, '=', value, '\r\n'), 'i');
    END IF;
    -- fertig
    RETURN trim(regexp_replace(ini, '\\r?\\n([ \\t]*\\r?\\n)+', '\r\n', ''), ' \r\n');
  END $$ LANGUAGE plpgsql;
  --

--


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:36 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz