2009-10-10 12 views
8

¿Hay alguna función Delphi D2010 como PosEx que encuentre una subcadena dentro de una cadena comenzando desde el final de la cadena?¿Alguna función Delphi incorporada como PosEx que encuentra una subcadena comenzando desde la parte posterior de la cadena?

estoy quitando todas las llamadas a la biblioteca FastStrings y una de las funciones que estaba usando era FastPosBack:

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

encontré LastDelimiter pero no es exactamente lo mismo, ya que sólo encuentra el último delimitador y no puedo especificar una posición de inicio.

Gracias!

actualización: DR siguiente comentario, he creado esta función:

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; 

¿Hay alguna forma más eficaz de hacer esto? En un ciclo de ciclo de 1000000, Pos tarda 47 ms, mientras que FastPosBack tarda 234 ms en completarse.

+0

Sólo por curiosidad: ¿cómo era tu prueba como exactamente? – jpfollenius

+0

Llamo a GetTickCount, seguido de un bucle 1000000 de la llamada a la función y luego obtengo la diferencia, GetTickCount - TickCount. – smartins

+0

Estaba más interesado en qué cadenas pasa a las funciones para probar ... – jpfollenius

Respuesta

8

probar este/estos:

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 sobrecarga proporciona una forma de llamar a las RPO utilizando los pos_comienzo más eficientes de búsqueda desde el final de la cadena, sin tener que calcular que usted mismo. Para la eficacia, no se realiza ninguna comprobación en los startpos cuando se especifica explícitamente.

En mi conjunto de pruebas de rendimiento SmokeTest esto sale aproximadamente un 20% más rápido que su FastPosBack (que por cierto contiene un error "uno por uno" y requiere algunos parámetros que no usa).

+0

Hola. Gracias por su función, de hecho es mucho más rápido que el que puedo con (344ms vs 109ms). Los parámetros adicionales en la función significan que no necesito cambiar ninguna de las llamadas a funciones FastStrings y simplemente reemplazarlas con mis propias funciones. – smartins

+0

Deltics, ¿su función es compatible con Unicode? Noté que los parámetros de las funciones de Marco son AnsiStrings y usaré este código en D2010. – smartins

+0

Fue desarrollado y probado usando Delphi 2009, entonces sí, creo que es Unicode "seguro". Sin embargo, * sí * supone que String y SubStr contienen los mismos tipos de 'carga útil' (es decir, tanto la cadena como el substr son UTF16 o ANSI, etc.). – Deltics

8

Puede utilizar Pos en combinación con ReverseString (de StrUtils)

+0

Gracias por su respuesta, usando sus sugerencias He creado una función que es sorprendentemente rápida en comparación con las otras alternativas hasta ahora. – smartins

1

no en el patrón RTL pero en INDY (idGlobalProtocols unidad de acuerdo a la ayuda en línea), que forma parte de las últimas instalaciones de Delphi:

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

Gracias por la respuesta. Esta función es mucho más lenta que la que se me ocurrió, tomó 5913ms (RPos) contra 234ms para completar. – smartins

3

Delphi viene con una función que puede buscar hacia atrás, SearchBuf en la unidad StrUtils. Sin embargo, está especializado en la búsqueda de palabras, por lo que podría no comportarse del modo que usted desea. A continuación, lo he envuelto en una función que coincide con la interfaz deseada.

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

Gracias por la respuesta Rob. Desafortunadamente esta función es incluso más lenta que la que yo puedo hacer, tomando 1310ms para completar. He cambiado AnsiString y PAnsiChar a String y PChar respectivamente porque eso es lo que intento lograr, un reemplazo decente para estos ultr funciones rápidas de AnsiString. – smartins

2

Primero, considere si es necesaria una solución de velocidad optimizada. Si no es probable que se llame 100000 veces en uso real, invertir las cadenas y usar la búsqueda de subcadenas existente está bien.

Si la velocidad es un problema, existen muchos recursos buenos para escribir. Busque en wikipedia "algoritmos de búsqueda de cadenas" para obtener ideas. Voy a publicar un enlace y un algoritmo de ejemplo cuando estoy en una computadora. Estoy escribiendo esto desde mi teléfono en este momento.

Actualización:

Aquí está el ejemplo que prometí:

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; 

Es básicamente una ingenua (fuerza bruta) algoritmo de búsqueda cadena invertida. Comienza al final tanto del patrón como del texto y avanza hacia el principio. Puedo garantizar que es menos eficiente que la función Pos() de Delphi aunque no puedo decir si es más rápida o más lenta que la combinación Pos() - ReverseString() ya que no la he probado. Hay un error en el que no he encontrado la causa. Si las dos cadenas son idénticas, devuelve -1 (no encontrado).

+0

Probablemente tengas razón, estas diferencias no se notarán a menos que se haga una gran cantidad de iteraciones. – smartins

+0

Las pequeñas diferencias pronto se suman, y en este caso la rutina requerida es tan pequeña que tiene sentido optimizar ya que el esfuerzo es insignificante y significa que no tiene que preocuparse por usarlo en el código de rendimiento crítico si la necesidad surge en el futuro. La optimización prematura es un error cuando el esfuerzo de optimización es desproporcionado con respecto a las ganancias. En este caso, el esfuerzo es mínimo y, por lo tanto, está justificado para * cualquier * ganancia si se puede lograr sin sacrificar la facilidad de uso, imho. – Deltics

+0

No estoy de acuerdo. Creo que la simplicidad gana la velocidad incluso en casos triviales. Cada vez que optimiza, aumenta la complejidad del código. La única vez que optimizo es cuando la solución existente ha demostrado ser inadecuada en un entorno de prueba o producción. –

2

utilizo las variantes RPOS de la función strutils de Free Pascal:

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

la cadena, cadena de versión es casi la misma que Deltics', pero hay 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;

Se licencian bajo la licencia de excepción LGPL + de FPC, pero desde que los escribí, los libero bajo licencia BSD.

+0

Marco, noté que todas estas funciones tienen AnsiStrings, por lo que considero que no son compatibles con Unicode en D2009/D2010. ¿Sabes si hay planes para hacer compatibles estos Unicode? – smartins

+0

FPC está trabajando en modos de compilador unicode. Probablemente seguirá el mismo modelo que con Turbo Pascal-> Delphi, a saber, que el tipo de cadena por defecto se puede seleccionar por unidad. Pero la compatibilidad con el idioma base llevará un tiempo, y para que las bibliotecas se pongan al día tomará más tiempo. Los sustitutos hacen que el caso Unicode sea considerablemente más difícil, ya que la mayoría de las técnicas de escaneo de documentos sustituidos funcionan hacia adelante, no hacia atrás. –

+0

No esperaría nada listo para la producción en el próximo año, año y medio. –

1

Tal vez la adición de mayúsculas o minúsculas a los parámetros Substr y aString antes de realizar la búsqueda puede hacer que el propósito de Deltics sea insensible a las mayúsculas y minúsculas. Creo que te dejó para hacer esto antes de llamar a RPos. pero tal vez un parámetro opcional puede hacer el trabajo.

esto es cómo el propósito de Deltic debe buscar:

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; 
Cuestiones relacionadas