Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Teilstring extrahieren


D. Annies - Sa 05.01.13 09:48
Titel: Teilstring extrahieren
Hi, Delpher,

ich möchte aus einem langen SQL-String den Teil als Dateinamen vergeben, der hinter der
WHERE-Phrase steht, gefolgt von der Endung '.xls'.

Beispiel: aus "Select * from Db.dbf where Klasse = '7a' soll als Dateiname werden: Klasse = 7a.xls


Wo ist denn in meiner Idee der Fehler? Die Anführungszeichen müssen ja auch noch raus.


Delphi-Quelltext
1:
2:
3:
if pos('where', inputstring) > 0 then
          fname := copy(inputstring, pos+1, length(inputstring)) + '.xls';
showmessage(fname);


Der Fehler liegt im Moment bei pos+1

Vielen Dank für Ideen,
D. Annies


Mathematiker - Sa 05.01.13 10:05

Hallo,
pos ist eine Funktion, die Parameter benötigt, und keine einfache Variable, d.h.

Delphi-Quelltext
1:
2:
3:
stelle:=pos('where', inputstring);
if stelle > 0 then
          fname := copy(inputstring, stelle+6, length(inputstring)) + '.xls';

funktioniert dann. stelle+6 ist wichtig, da 'where'+Leerzeichen sechs Zeichen lang ist.
Anschließend kannst Du noch mit

Delphi-Quelltext
1:
2:
3:
4:
5:
stelle:=pos(chr(39), inputstring);
while stelle > 0 do begin
  delete(inputstring,stelle,1);
  stelle:=pos(chr(39), inputstring);
end;

die '=chr(39) entfernen.

Beste Grüße
Mathematiker


Horst_H - Sa 05.01.13 10:07

Hallo,

Du brauchst eine Variable, die die Position speichert oder Du fragst es nochmals ab.
HIer frage ich Anfang und Ende ab.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
testString := uppercase(inputstring);
posWHERE := pos('WHERE', testString);
posXLS := pos('XLS', testString);
if (posWHERE > 0AND (posXLS > posWHERE) then
  begin
  inc(posWHERE,length('WHERE')+1);// Ort hinter 'WHERE'
  inc(posXLS,length('XLS')+1);   // Ort hinter '.XLS' 
  fName:= copy(inputstring, posWhere, posXLS-posWhere+1);
 ...
  end;


Gruß Horst


haentschman - Sa 05.01.13 10:26

Moin...

Ich bin dafür, das Problem an der Wurzel zu packen. 8) Wo / wie wird denn der SQL String zusammengesetzt ? Ist der hardcodiert ? Sollte man sich nicht schon zum Zeitpunkt des Zusammensetzens den "where Wert" für den Dateinamen merken als den später wieder rauszufrickeln ?


D. Annies - Sa 05.01.13 10:26

Vielen Dank für eure schnellen (und richtigen) Antworten!
Jetzt komme ich wieder weiter.
LG, Detlef


haentschman - Sa 05.01.13 10:28

...hast du meinen Einwand gelesen ?


D. Annies - Sa 05.01.13 11:35

@ haentschman:
So, jetzt habe ich deinen Einwand gelesen - sehr interessant,
aber der SQL-String ist nicht hardcodiert, sondern ich nehme ihn
aus einer Textdatei und kann ihn zur Laufzeit noch ändern/anpassen.
Ausprobieren werde ich deine Idee aber auch mal, denn die Idee habe
ich schon verstanden.
Danke, Detlef


haentschman - Sa 05.01.13 13:03

...gern geschehen. Alle lernen ständig dazu... 8)


Horst_H - Sa 05.01.13 16:30

Hallo,

user profile iconhaentschman hat schon recht, wenn man ohnehin den Abfragestring selbst aufbaut, kann man den Dateinamen frühest möglich extrahieren.
Wenn man schon pos benutzen will, wäre PosEx sicher auch angebracht, da es ab einer vorherigen Fund/Suchstelle suchen könnte.
http://www.delphi-treff.de/tutorials/objectpascal/stringverarbeitung/die-wichtigsten-funktionen/

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
const
  SuchStr1 = 'WHERE';
  SuchStr2 = '.XLS';

begin
fName := '';
testString := uppercase(inputstring);
posWHERE := pos(SuchStr1, testString);
if (posWHERE > 0then
  begin
  inc(posWHERE,length(SuchStr1)+1);// Ort hinter 'WHERE'
  // Ab dort nach '.XLS' suchen
  posXLS := posEX(SuchStr2, testString,posWHERE);
  IF posXLS >0 then
    begin
    inc(posXLS,length(SuchStr2)+1);   // Ort hinter '.XLS' 
    fName:= copy(inputstring, posWhere, posXLS-posWhere+1);
    // Jetzt weiterverarbeiten;
    //....
    end;
  end;


Gruß Horst


D. Annies - Sa 05.01.13 16:46

Danke, ich bin begeistert!
Gruß, Detlef


D. Annies - Sa 05.01.13 20:04

Etwas ist mir noch aufgefallen:
Der Dateiname hat (leider) immer vor der Endung .xls eine Leertaste.
Habe es auch schon mit lenght(teilstring)-1 versucht, ohne Erfolg.
LG, Detlef


D. Annies - Sa 05.01.13 22:13

Wie kann man denn diesen SpaghettiCode noch etwas komprimieren?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
stelle := pos(chr(39), fname);     // '
        while stelle > 0 do
        begin
          delete(fname, stelle, 1);
          stelle := pos(chr(39), fname);
        end;

        stelle := pos(chr(61), fname);     // =
        while stelle > 0 do
        begin
          delete(fname, stelle, 1);
          stelle := pos(chr(61), fname);
        end;

        stelle := pos(chr(37), fname);     // %
        while stelle > 0 do
        begin
          delete(fname, stelle, 1);
          stelle := pos(chr(37), fname);
        end;

        stelle := pos(chr(40), fname);     // (
        while stelle > 0 do
        begin
          delete(fname, stelle, 1);
          stelle := pos(chr(40), fname);
        end;

        stelle := pos(chr(41), fname);     // )
        while stelle > 0 do
        begin
          delete(fname, stelle, 1);
          stelle := pos(chr(41), fname);
        end;
        fname := fname + '.xls';


LG, Detlef


Mathematiker - Sa 05.01.13 22:54

Hallo,
user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Wie kann man denn diesen SpaghettiCode noch etwas komprimieren?

Mein Vorschlag wäre, alle Zeichen zu einem String zusammenzufassen.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
loeschstring:=chr(39)+chr(61)+chr(37)+chr(40)+chr(41);  //oder mit den Zeichen
for i:=1 to length(loeschstring) do 
begin  
  stelle := pos(loeschstring[i], fname);     // '
  while stelle > 0 do
  begin
    delete(fname, stelle, 1);
    stelle := pos(loeschstring[i], fname);
  end;
end;
fname := fname + '.xls';


Beste Grüße
Mathematiker


D. Annies - Sa 05.01.13 23:58

Danke, sieht sehr gut aus!

Also:

var loeschstring : String;
oder
var loeschstring : String[10];

ich bin ja immer vorsichtig ...
LG, Detlef


Mathematiker - So 06.01.13 00:30

Hallo,
user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Also:
var loeschstring : String;
oder
var loeschstring : String[10];

Beides ist möglich. Ich bevorzuge string, ohne Begrenzung.
Zu Pascal-Zeiten hätte ich den String noch eingeschränkt, aber bei den heutigen Computern ...

Beste Grüße
Mathematiker


Delphi-Laie - So 06.01.13 01:24

user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Der Dateiname hat (leider) immer vor der Endung .xls eine Leertaste.
Habe es auch schon mit lenght(teilstring)-1 versucht, ohne Erfolg.


Na, wohl nicht Leertaste, sondern Leerzeichen.

Mit Delete bekommt man Teile aus einem String gelöscht, ich glaube, die Syntax lautet: Delete(Stringvariable,Startposition,Anzahl_Löschzeichen).


Delete - So 06.01.13 08:19

user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Der Dateiname hat (leider) immer vor der Endung .xls eine Leertaste.


Leerzeichen an Anfang und Ende eines Strings entfernt man mit Trim.


D. Annies - So 06.01.13 10:47

Gibt es noch einen begabten, der auch diesen Code zusammenfassen kann?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
stelle := pos('WHERE', teststring);
if stelle > 0 then
  fname := copy(teststring, stelle+6, length(teststring)-1);

stelle := pos('ORDER', teststring);
if stelle > 0 then
  fname := copy(teststring, stelle+6, length(teststring)-1);

stelle := pos('GROUP', teststring);
if stelle > 0 then
  fname := copy(teststring, stelle+6, length(teststring)-1);


Danke, Detlef


Mathematiker - So 06.01.13 11:17

Hallo,
mein Vorschlag:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
const suchstring:array[1..3of string[6] = ('WHERE','ORDER','GROUP');
...
for i:=1 to 3 do begin
  stelle := pos(suchstring[i], teststring);
  if stelle > 0 then
    fname := copy(teststring, stelle+length(suchstring[i])+1, length(teststring)-1);
end;


Beste Grüße
Mathematiker


D. Annies - So 06.01.13 13:13

Kurze Nachfrage: Warum nicht VAR suchstring: ...
LG Det


Mathematiker - So 06.01.13 13:33

Hallo,
user profile iconD. Annies hat folgendes geschrieben Zum zitierten Posting springen:
Kurze Nachfrage: Warum nicht VAR suchstring:

Wählst Du var, so musst Du im Text die Wörter zuweisen, z.B.

Delphi-Quelltext
1:
2:
3:
4:
5:
var suchstring:array[1..3of string[6];
...
suchstring[1]:='WHERE';
suchstring[2]:='ORDER';
suchstring[3]:='GROUP';

Mit const können die entsprechenden Werte automatisch zugewiesen werden.

Beste Grüße
Mathematiker


D. Annies - So 06.01.13 14:18

Toll! und Danke!


WasWeißDennIch - So 06.01.13 19:02

Ich möchte noch einmal auf haentschmans berechtigten Einwand zurückkommen. Wenn die Dateinamen in einer Datei stehen, wieso wird dann nicht gleich beim Auslesen der neue Dateiname gebildet? Diese ganze Pos-Frickelei erscheint mir eher kontraproduktiv, die Verwendung von Shortstrings macht das Ganze dann noch schlimmer und ist auch unnötig, da eine Stringkonstante automatisch die richtige Länge aufweist, ohne dass man sie extra einschränkt. Ich habe gerade kein Delphi zur Hand, aber wenn man Pos unter Delphi >= 2009 mit einem Shortstring und einem String-Parameter anwendet, sollte es zumindest eine (vermeidbare) Compilerwarnung geben, da dort Shortstrings 1 Byte und Strings 2 Byte je Zeichen besitzen. Ich denke, man kann das auch einfacher gestalten (Pseudo-Code):

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  DateinameXLS, DateinameSQL: string;
begin
  DateinameSQL := gelesener_Wert_aus_Datei;
  DateinameXLS := ChangeFileExt(DateinameSQL, '.xls');

Weiterhin kann ich nur dringend empfehlen, SQL-Parameter zu verwenden. Neben der automatisch korrekten SQL-Syntax werden damit auch SQL-Injections vermieden (und schneller sind Prepared Statements nebenher auch noch).

Delphi-Quelltext
1:
2:
3:
Query.SQL.Text := 'SELECT Feldliste FROM Tabelle WHERE Feldwert = :wert';
...
Query.{Parameters.}ParamByName('wert').Value := DateinameSQL;


Mathematiker - So 06.01.13 19:11

user profile iconWasWeißDennIch hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe gerade kein Delphi zur Hand, aber wenn man Pos unter Delphi >= 2009 mit einem Shortstring und einem String-Parameter anwendet, sollte es zumindest eine (vermeidbare) Compilerwarnung geben, da dort Shortstrings 1 Byte und Strings 2 Byte je Zeichen besitzen.

Toll, was Du alles weißt. Wir sind alle beeindruckt. :?
Ich würde aber erst einmal lesen, welche Compiler D.Annies nutzt. Dein Hinweis auf Delphi 2009-Strings bringt ihm absolut nichts!

Beste Grüße
Mathematiker


WasWeißDennIch - So 06.01.13 19:16

Entschuldigung, ich wusste nicht, dass man hier nur spezifische Antworten für spezifische User mit spezifischen Delphi-Versionen geben darf. Naja, nach einem evtl. Delphi-Update hätte er es ja dann selbst gemerkt, ich werde in Zukunft auf allgemeingültige Antworten verzichten. Dann kann man ja gleich alles per PM lösen und braucht kein Forum mehr.