2009-10-10 11 views
8

Y at-il une fonction Delphi D2010 comme PosEx qui trouve une sous-chaîne dans une chaîne à partir de la fin de la chaîne?Toute fonction Delphi intégrée comme PosEx qui trouve une sous-chaîne commençant à l'arrière de la chaîne?

Je supprimer tous les appels à la bibliothèque FastStrings et l'une des fonctions que j'utilisais était FastPosBack:

function FastPosBack(const aSourceString, aFindString : AnsiString; const aSourceLen, aFindLen, StartPos : Integer) : Integer; 

J'ai trouvé LastDelimiter mais ce n'est pas tout à fait la même chose, car il ne trouve que le dernier delimiter et je ne peux pas spécifier une position de départ.

Merci!

Mise à jour: Après un commentaire DR, je l'ai créé cette fonction:

function FastPosBack(const aSourceString, aFindString : String; const aSourceLen, aFindLen, StartPos : Integer) : Integer; 
var 
    RevSourceString, RevFindString: string; 
begin 
    RevSourceString := AnsiReverseString(aSourceString); 
    RevFindString := AnsiReverseString(aFindString); 

    Result := Length(aSourceString) - PosEx(RevFindString, RevSourceString, StartPos) + 1; 
end; 

est-il un moyen plus efficace de le faire? Sur un cycle de boucle de 1000000, Pos prend 47ms tandis que FastPosBack prend 234ms à compléter.

+0

Juste par curiosité: comment votre look test comme exactement? – jpfollenius

+0

J'appelle GetTickCount, suivi d'une boucle 1000000 de l'appel à la fonction, puis obtenir la différence, GetTickCount - TickCount. – smartins

+0

J'étais plus intéressé par ce que les chaînes que vous passez aux fonctions de test ... – jpfollenius

Répondre

8

Essayez cette/ces:

function RPos(const aSubStr, aString : String; const aStartPos: Integer): Integer; overload; 
var 
    i: Integer; 
    pStr: PChar; 
    pSub: PChar; 
begin 
    pSub := Pointer(aSubStr); 

    for i := aStartPos downto 1 do 
    begin 
    pStr := @(aString[i]); 
    if (pStr^ = pSub^) then 
    begin 
     if CompareMem(pSub, pStr, Length(aSubStr)) then 
     begin 
     result := i; 
     EXIT; 
     end; 
    end; 
    end; 

    result := 0; 
end; 


function RPos(const aSubStr, aString : String): Integer; overload; 
begin 
    result := RPos(aSubStr, aString, Length(aString) - Length(aSubStr) + 1); 
end; 

La surcharge est un moyen d'appeler à l'aide RPO pour la recherche de la fin de la chaîne startpos les plus efficaces sans avoir à calculer vous-même. Pour des raisons d'efficacité, aucune vérification n'est effectuée sur startpos lorsqu'il est explicitement spécifié.Dans ma suite de tests de performance SmokeTest, cette sortie est environ 20% plus rapide que votre FastPosBack (qui contient d'ailleurs une erreur «désactivée par un» et nécessite certains paramètres qu'elle n'utilise pas réellement).

+0

Bonjour. Merci pour votre fonction, il est en effet beaucoup plus rapide que celui que je peux avec (344ms vs 109ms). Les paramètres supplémentaires sur la fonction signifiaient que je n'ai pas besoin de changer les fonctions de FastStrings et de les remplacer par mes propres fonctions. – smartins

+0

Deltics, est-ce que votre fonction est compatible Unicode? J'ai remarqué que les paramètres des fonctions de Marco sont AnsiStrings et je vais utiliser ce code dans D2010. – smartins

+0

Il a été développé et testé en utilisant Delphi 2009, donc oui je crois que c'est Unicode "sûr". Cependant, il * suppose * que String et SubStr contiennent les mêmes types de 'données utiles' (c'est-à-dire à la fois chaîne et sous-chaîne sont UTF16 ou ANSI etc.). – Deltics

8

Vous pouvez utiliser Pos en combinaison avec ReverseString (de StrUtils)

+0

Merci pour votre réponse, en utilisant vos conseils, j'ai créé une fonction qui est étonnamment rapide par rapport aux autres alternatives à ce jour. Merci pour – smartins

1

pas dans la RTL standard, mais dans INDY (unité idGlobalProtocols selon l'aide en ligne), qui fait partie des récentes installations Delphi:

function RPos(
    const ASub: String, 
    const AIn: String, 
    AStart: Integer = -1 
): Integer; 
+0

la réponse. cette fonction est beaucoup, beaucoup plus lente que celle que je suis venu avec, il a fallu 5913ms (RPO) contre 234ms pour terminer. – smartins

3

Delphi est livré avec une fonction qui peut rechercher en arrière, SearchBuf dans l'unité StrUtils. Cependant, il est spécialisé dans la recherche de mots, de sorte qu'il ne se comporte pas comme vous le souhaitez. Ci-dessous, je l'ai enveloppé dans une fonction correspondant à votre interface souhaitée.

function FastPosBack(const aSourceString, aFindString: AnsiString; 
        const aSourceLen, aFindLen, StartPos: Integer): Integer; 
var 
    Source, Match: PAnsiChar; 
begin 
    Source := PAnsiChar(ASourceString); 
    Match := SearchBuf(Source, ASourceLen, ASourceLen, 0, 
        AFindString, [soMatchCase]); 
    if Assigned(Match) then 
    Result := Match - Source + 1 
    else 
    Result := 0; 
end; 
+0

Merci pour la réponse Rob. Malheureusement, cette fonction est encore plus lente que celle que je peux me up, prendre J'ai changé l'AnsiString et PAnsiChar en String et en PChar respectivement parce que c'est ce que j'essaie d'accomplir, un remplacement décent de ces fonctions AnsiString rapides. – smartins

2

D'abord, déterminez si une solution optimisée en vitesse est nécessaire. Si ce n'est pas probable qu'il sera appelé 100000 fois en utilisation réelle en inversant les chaînes et en utilisant la recherche de sous-chaîne existante est très bien.

Si la vitesse est un problème, il y a beaucoup de bonnes ressources pour écrire. Regardez sur Wikipedia pour "algorithmes de recherche de chaîne" pour des idées. Je posterai un lien et un exemple d'algorithme quand je serai devant un ordinateur. Je tape ceci depuis mon téléphone en ce moment.

Mise à jour:

Voici l'exemple que je promis:

function RPOS(pattern: string; text:string): Integer; 
var patternPosition, 
    textPosition: Integer; 
begin 
    Result := -1; 
    for textPosition := Length(text) downto 0 do 
    begin 
    for patternPosition := Length(pattern) downto 0 do 
     if not (pattern[patternPosition] = (text[textPosition - (Length(pattern) - patternPosition)])) then 
     break; 
    if patternPosition = 0 then 
     Result := textPosition -Length(pattern) + 1; 
    end; 
end; 

Son essentiellement une recherche de chaîne naïve (force brute) inversée algorithme. Il commence à la fin du motif et du texte et se poursuit jusqu'au début. Je peux garantir qu'il est moins efficace que la fonction Pos() de Delphi bien que je ne puisse pas dire si c'est plus ou moins rapide que la combinaison Pos() - ReverseString() car je ne l'ai pas testé. Il y a une erreur dont je n'ai pas trouvé la cause. Si les deux chaînes sont identiques, elle renvoie -1 (non trouvé).

+0

Vous avez probablement raison, ces différences ne seront pas perceptibles à moins de faire un grand nombre d'itérations. De petites différences – smartins

+0

ajouter sous peu, et dans ce cas, la routine nécessaire est si petit qu'il est logique d'optimiser depuis l'effort de le faire est négligeable et signifie que vous alors n'avez pas à vous soucier de l'utiliser dans l'exécution de code critique si le besoin se présente à l'avenir. L'optimisation prématurée est une erreur lorsque l'effort d'optimisation est disproportionné par rapport aux gains. Dans ce cas, l'effort est minime et est donc justifié pour * tout * gain si cela peut être réalisé sans sacrifier la facilité d'utilisation, à mon humble avis. – Deltics

+0

Je ne suis pas d'accord. Je pense que la simplicité gagne la vitesse même dans les cas triviaux. Chaque fois que vous optimisez vous augmentez la complexité du code. La seule fois où j'optimise, c'est lorsque la solution existante s'avère inadéquate dans un environnement de test ou de production. –

2

J'utilise les variantes de RPOS de Gratuit Pascal fonction strutils:

http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/rtl/objpas/strutils.pp?view=markup

la chaîne, version chaîne est à peu près la même que Deltics', mais il y a des variantes:

Function RPosEX(C:char;const S : AnsiString;offs:cardinal):Integer; overload;
Function RPosex (Const Substr : AnsiString; Const Source : AnsiString;offs:cardinal) : Integer; overload;
Function RPos(c:char;const S : AnsiString):Integer; overload;
Function RPos (Const Substr : AnsiString; Const Source : AnsiString) : Integer; overload;

Ils sont licenciés sous la licence d'exception LGPL + de FPC, mais depuis que je les ai écrits, je les libère sous licence BSD.

+0

Marco, j'ai remarqué que ces fonctions prennent toutes AnsiStrings alors je suppose que ce ne sont pas compatibles Unicode dans D2009/D2010. Savez-vous s'il est prévu de rendre ces Unicode compatibles? – smartins

+0

FPC travaille sur les modes du compilateur Unicode. Il suivra probablement le même modèle qu'avec Turbo Pascal-> Delphi, à savoir que le type de la chaîne par défaut peut être sélectionné par unité. Mais le support du langage de base prendra un certain temps, et le rattrapage des bibliothèques prendra plus de temps. Les substituts rendent le cas d'unicode beaucoup plus difficile, puisque la plupart des techniques de balayage de substitution de document fonctionnent en avant, pas en arrière. –

+0

Je ne m'attendrais à rien de prêt à la production l'année prochaine, un an et demi. –

1

Peut-être ajouter Uppercasing ou paramètres de aSubstr en minuscules et AString avant de faire la recherche peut faire effet Deltics insensible à la casse. Je pense qu'il t'a laissé faire ça avant d'appeler RPos. mais peut-être qu'un paramètre facultatif peut faire le travail.

c'est ainsi que le but de Deltic devrait ressembler à:

function RPos(const aSubStr, aString : String; const aStartPos: Integer; 
       const aCaseInSensitive:boolean=true): Integer; overload; 
var 
    i, _startPos: Integer; 
    pStr: PChar; 
    pSub: PChar; 
    _subStr, _string: string; 
begin 

if aCaseInSensitive then 
begin 
    _subStr := lowercase(aSubstr); 
    _string := lowercase(aString); 
end 
else 
begin 
    _subStr := aSubstr: 
    _string := aString; 
end; 

pSub := Pointer(_subStr); 

if aStartPos = -1 then 
    _startPos := Length(_string) - Length(_subStr) + 1 
else 
    _startPos := aStartPos; 

for i := _startPos downto 1 do 
begin 
    pStr := @(_string[i]); 
    if (pStr^ = pSub^) then 
    begin 
    if CompareMem(pSub, pStr, Length(_subStr)) then 
    begin 
     result := i; 
     EXIT; 
    end; 
    end; 
end; 

result := 0; 
end; 


function RPos(const aSubStr, aString : String; 
       const aCaseInSensitive:boolean=true): Integer; overload; 
begin 
    result := RPos(aSubStr, aString, Length(aString) - Length(aSubStr) + 1, 
       aCaseInSensitive); 
end;