Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - "Gegenstück" zu IndexOf?


Christian213 - Mi 17.07.13 09:25
Titel: "Gegenstück" zu IndexOf?
Hallo,

ich brauche eine Art schnelles Gegenstück zu IndexOf bei TStringList: Meine StringList enthält eine Anzahl von Substrings, ich möchte nun einen von Außen übergebenen String mit diesen Substrings vergleichen. Leider funktioniert IndexOf ja genau anders herum.
Hat jemand eine Idee wie ich das Ganze OHNE iterieren mittels for...Schleife lösen kann? Gibt es eine Methode die man dafür missbrauchen könnte?
Danke!

Gruß,
Christian


Hidden - Mi 17.07.13 09:54

Hallo Christian,

Ich verstehe leider nicht, was genau du zu erreichen versuchst: IndexOf(S) liefert den Index des ersten Strings in der Stringlist, der mit S übereinstimmt (d.h. AnsiCompareText = 0).
So oft ich deinen Beitrag auch lese, bis auf das Wörtchen substring ist das genau was du beschreibst? :nixweiss:

Grüße,
Daniel


Christian213 - Mi 17.07.13 10:13

Ok, ich versuche mal die Ausgangslage etwas besser zu beschreiben:

- Ich habe eine StringList mit Dateipfad-Fragmenten (Teilstrings)
- Ich möchte nun prüfen, ob in einem übergebenen String mit einem absoluten Pfad einer dieser Teilstrings matcht
- Und das möglichst ohne eine Schleife bauen zu müssen


Hidden - Mi 17.07.13 10:33

ich weiß zwar nicht, in wiefern IndexOf anders herum funktioniert, aber ob ein String in einem anderen als Substring enthalten ist kannst du mit Pos(Substring, String) herausfinden:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function FirstSubstring(const S: String, Str: TStrings): Integer;
begin
  for Result := 0 to Str.Count - 1 do
    if System.Pos(S, Str[Result]) > 0 then Exit;
  Result := -1;
end;


Grüße,
Daniel


Christian213 - Mi 17.07.13 11:28

Mit "anders herum" meine ich, dass ich die Teilstrings in der Stringliste habe, deswegen habe ich es nun so gelöst (ähnelt aber Deinem Vorschlag):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function reverseIndexOf(StringList: TStringList; SearchString: AnsiString): Integer;
var
  i : Integer;
begin
  for i := StringList.Count-1 downto 0 do
  begin
    Result := (pos(StringList[i], SearchString)) - 1// um kompatibel zu indexOf zu bleiben
    if Result > -1 then
      break;
  end;
end;


Christian213 - Mi 17.07.13 11:29

PS: Die Idee "Result" als Schleifenvariable zu benutzen gefällt mir - das klau' ich mal :-)


jaenicke - Mi 17.07.13 11:30

Falls du relative Pfade meinst, würde ich die in absolute Pfade umwandeln und dann vergleichen.

Oder soll das eine Suche in allen Ordnern oder so sein? Dann ist das so wie du es hast natürlich passend.


Christian213 - Mi 17.07.13 11:39

@jaenicke: Zweiteres. Ich habe meine Methode mittels des Tricks von Hidden noch weiter optimiert. Ich denke mit dem Resultat kann ich leben :-)

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function reverseIndexOf(StringList: TStringList; SearchString: AnsiString): Integer;
begin
  for Result := StringList.Count-1 downto 0 do
    if pos(StringList[Result], SearchString) > 0 then
      break;
  dec(Result); // um kompatibel zu indexOf zu bleiben
end;


Der Name "reverseIndexOf" ist sicherlich unglücklich gewählt, aber wie soll man eine Funktion nennen die eine Liste von Substrings gegen einen übergebenen String matcht?


Hidden - Mi 17.07.13 12:07

user profile iconChristian213 hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function reverseIndexOf(StringList: TStringList; SearchString: AnsiString): Integer;
begin
  for Result := StringList.Count-1 downto 0 do
    if pos(StringList[Result], SearchString) > 0 then
      break;
  dec(Result); // um kompatibel zu indexOf zu bleiben
end;
Achtung, diese Version unterscheidet nicht länger zwischen den Fällen "StringList[0] ist Substring von SearchString" und "keiner der Strings in Stringlist ist Substring von SearchString". Ich nehme an, dec(Result) sollte im letzten Fall sicherstellen, dass ebenfalls -1 zurückgegeben wird?

Was ich oben gepostet hatte, war die Implementierung von IndexOf aus der TStringlist übergeordneten Klasse TStrings in Classes.pas, Delphi 2010; nur, dass ich den Ausdruck AnsiCompareText = 0 durch Pos > 0 ersetzt habe. Für den Fall, dass die for-Schleife komplett durchläuft, steht dort noch ein Result := -1;.


jaenicke - Mi 17.07.13 13:07

Der Wert einer Schleifenvariablen nach dem Durchlauf ist undefiniert (das ist dokumentiert, dass das so ist). Es ist daher Glückssache, ob das richtige herauskommt. Und es kann passieren, dass die Umstellung von Projekteinstellungen (Optimierung, ...) oder der Wechsel auf eine neuere Delphiversion dazu führen, dass es nicht mehr funktioniert.

Deshalb sollte man so etwas lieber richtig machen...

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
function reverseIndexOf(const StringList: TStringList; const SearchString: AnsiString): Integer;
var
  i: Integer;
begin
  for i := StringList.Count - 1 downto 0 do
    if Pos(StringList[i], SearchString) > 0 then
      Exit(i);
  Result := -1;
end;
Nebenbei führt bei neueren Delphiversionen (>= 2009) die Verwendung von AnsiString als Typ dazu, dass bei der Übergabe an Pos jedesmal eine Umwandlung in einen UnicodeString durchgeführt wird. Deshalb ist das auch keine gute Idee.


Hidden - Mi 17.07.13 13:29

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
Exit(i);                    
Da hast du allerdings recht. Ich wollte das beim kopieren schon auf eine mehrzeilige Lösung ändern (result := i; Exit;) oder die Schleife als while do .. Inc(result) schreiben, aber da Classes.pas es so macht sollte es in dem Kontext ja funktionieren*.

Daran dass Exit optional auch ein Argument kriegen kann, hatte ich nicht gedacht. Das ist keine Zeile länger und verhält sich mit Sicherheit wie erwartet.

Edit: Ok, wie es scheint ist der Wert der Schleifenvariablen durchaus definiert wenn die Schleife mit einem Exit; gebrochen wird. Exit(i) ist trotzdem an der Stelle schöner, auch wenn es wohl nur für C++-Programmierer und ähnliche existiert, die gerne ein return hätten 8)


jaenicke - Mi 17.07.13 14:50

user profile iconHidden hat folgendes geschrieben Zum zitierten Posting springen:
Edit: Ok, wie es scheint ist der Wert der Schleifenvariablen durchaus definiert wenn die Schleife mit einem Exit; gebrochen wird.
Das ist aber kein definiertes Verhalten und funktioniert nur, wenn in eax am Ende eben dieser Wert steht.

In XE4 zumindest wird ein entsprechender Assemblerbefehl durch den Compiler eingefügt, der den Wert am Ende in den Rückgabewert schreibt. Das liegt daran, dass die Schleifenvariable auf dem Stack geführt wird. Trotzdem heißt das nicht, dass das immer funktionieren muss, deshalb kommt ja auch die entsprechende Compilerwarnung.


Christian213 - Mi 17.07.13 14:55

user profile iconHidden hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconChristian213 hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function reverseIndexOf(StringList: TStringList; SearchString: AnsiString): Integer;
begin
  for Result := StringList.Count-1 downto 0 do
    if pos(StringList[Result], SearchString) > 0 then
      break;
  dec(Result); // um kompatibel zu indexOf zu bleiben
end;
Achtung, diese Version unterscheidet nicht länger zwischen den Fällen "StringList[0] ist Substring von SearchString" und "keiner der Strings in Stringlist ist Substring von SearchString". Ich nehme an, dec(Result) sollte im letzten Fall sicherstellen, dass ebenfalls -1 zurückgegeben wird?

Was ich oben gepostet hatte, war die Implementierung von IndexOf aus der TStringlist übergeordneten Klasse TStrings in Classes.pas, Delphi 2010; nur, dass ich den Ausdruck AnsiCompareText = 0 durch Pos > 0 ersetzt habe. Für den Fall, dass die for-Schleife komplett durchläuft, steht dort noch ein Result := -1;.


Ja, stimmt. Das ist mir auch noch aufgefallen. Habe es entsprechend angepasst.
Was das POS angeht: Habe es nun in AnsiPos geändert. Ich verwendet zwar nur AnsiStrings, aber Ihr habt natürlich Recht.