2015-11-03 7 views
5

Wie kann ich ein Array von Strings mit Move concat. Ich habe das versucht, aber ich kann mir einfach nicht vorstellen, wie der Move-Vorgang richtig funktioniert.Wie fasst man mehrere Zeichenketten mit Move zusammen?

program Project2; 

{$POINTERMATH ON} 

procedure Concat(var S: String; const A: Array of String); 
var 
    I, J: Integer; 
    Len: Integer; 
begin 
    Len := 0; 
    for I := 0 to High(A) do 
    Len := Len + Length(A[I]); 

    SetLength(S, Length(S) + Len); 

    for I := 0 to High(A) do 
    Move(PWideChar(A[I])[0], S[High(S)], Length(A[I]) * SizeOf(WideChar)); 
end; 

var 
    S: String; 
begin 
    S := 'test'; 
    Concat(S, ['test', 'test2', 'test3']); 
end. 
+0

Das RTL hat eine solche Funktion. Siehe '_LStrCatN' und sein Unicode-Pendant in * System.pas *. Der Compiler generiert automatisch Aufrufe an diese Funktion, wenn Sie mehr als drei Strings in einer einzigen Anweisung verketten, wie in 's1 + s2 + s3 + s4 + s5'. Die RTL-Funktion hat nicht dieselbe Schnittstelle; Es akzeptiert seine Liste von Zeichenfolgen auf dem Stapel statt als ein Array. Sie können die RTL-Funktion nicht direkt aufrufen, wenn Sie keine Baugruppe schreiben. –

Antwort

7

würde ich diese Funktion wie so schreiben:

procedure Concat(var Dest: string; const Source: array of string); 
var 
    i: Integer; 
    OriginalDestLen: Integer; 
    SourceLen: Integer; 
    TotalSourceLen: Integer; 
    DestPtr: PChar; 
begin 
    TotalSourceLen := 0; 
    OriginalDestLen := Length(Dest); 
    for i := low(Source) to high(Source) do begin 
    inc(TotalSourceLen, Length(Source[i])); 
    end; 
    SetLength(Dest, OriginalDestLen + TotalSourceLen); 

    DestPtr := PChar(Pointer(Dest)) + OriginalDestLen; 
    for i := low(Source) to high(Source) do begin 
    SourceLen := Length(Source[i]); 
    Move(Pointer(Source[i])^, DestPtr^, SourceLen*SizeOf(Char)); 
    inc(DestPtr, SourceLen); 
    end; 
end; 

Es ist ziemlich selbsterklärend. Die Komplikationen werden durch leere Strings verursacht. Jeder Versuch, Zeichen einer leeren Zeichenfolge zu indizieren, führt zu Ausnahmen, wenn die Bereichsüberprüfung aktiviert ist.

Um diese Komplikation zu behandeln, können Sie if Tests für den Fall hinzufügen, in dem eine der Zeichenfolgen, die an dem -Aufruf beteiligt sind, leer ist. Ich bevorzuge einen anderen Ansatz. Ich würde die String-Variable lieber als Zeiger ausgeben. Dies umgeht die Bereichsüberprüfung, erlaubt aber auch das Auslassen der if-Anweisung.

Move(Pointer(Source[i])^, DestPtr^, SourceLen*SizeOf(Char)); 

Man könnte sich fragen, was passiert, wenn Source[i] leer ist. In diesem Fall ist Pointer(Source[i])nil und Sie könnten eine Zugriffsverletzung erwarten. In der Tat gibt es keinen Fehler, da die Länge der Verschiebung, wie durch das dritte Argument spezifiziert, Null ist, und der Zeiger nil niemals tatsächlich de-referenziert wird.

Die andere Linie Bemerkenswert ist hier:

DestPtr := PChar(Pointer(Dest)) + OriginalDestLen; 

Wir verwenden PChar(Pointer(Dest)) statt PChar(Dest). Letzterer ruft Code auf, um zu prüfen, ob Dest leer ist oder nicht, und wenn dies der Fall ist, liefert er einen Zeiger auf einen einzelnen Null-Terminator. Wir möchten vermeiden, dass dieser Code ausgeführt wird, und die Adresse erhalten, die in Dest direkt gehalten wird, selbst wenn es nil ist.

+0

Assert könnte verwendet werden, um nach Null-Zeigern zu suchen. – user15124

+4

Es besteht keine Notwendigkeit etwas zu behaupten. Der ganze Sinn des Schreibens ist, dass es mit leeren Strings arbeitet. Was könntest du überhaupt behaupten? Der Code sollte keine Zeiger zulassen, sie sind gültige Eingaben. –

+0

Auch die erste Antwort hat 'call @ UniqueStringU' und' call @ UStrToPWChar' in der Schleife, was die Leistung weiter verschlechtern würde! – user15124

3

In der zweiten Schleife Sie vergessen, dass S bereits die richtige Größe hat mit allen Elementen zu erhalten gefüllt, so dass Sie eine andere Variable zu verwenden, haben die Ziel Parameter von Move

procedure Concat(var S: String; const A: Array of String); 
var 
    I, Len, Sum: Integer; 
begin 
    Len := 0; 
    for I := 0 to High(A) do 
    Inc(Len, Length(A[I])); 
    Sum := Length(S); 
    SetLength(S, Sum + Len); 
    for I := 0 to High(A) do 
    begin 
    if Length(A[I]) > 0 then 
     Move(A[I][1], S[Sum+1], Length(A[I]) * SizeOf(Char)); 
    Inc(Sum, Length(A[I])); 
    end; 
end; 

wissen Casting der Quelle Parameter zu PWideChar ist völlig überflüssig, da die Funktion verwenden eine Art von alten generischen Syntax tha t erlaubt, alles zu übergeben, was Sie wollen (const Parameter ohne Typ).

+0

Der Zugriff auf das erste Zeichen eines 'AnsiString' mit der' [] '-Notation löst eine Ausnahme aus, wenn die Zeichenfolge leer ist. Aktivieren Sie entweder "A [I] <> nil" oder "Länge (A [I])> 0", bevor Sie die Zeichenfolge indizieren, oder verwenden Sie stattdessen eine 'PAnsiChar'-Typumwandlung (was sicher ist, wenn die Zeichenfolge leer ist):' PAnsiChar (A [I])^'. Auch das Initialisieren einer lokalen Variablen zur Deklaration ('var Sum: Integer = 1;') ist in Delphi nicht zulässig. Sie ordnen auch "S" neu zu, um die ursprünglichen Daten von "S" einzuschließen, aber nicht "Sum" am Ende der ursprünglichen Daten zu beginnen, also überschreiben Sie es, anstatt es anzuhängen. –

+0

@ user15124: Es sei denn, behauptet wird deaktiviert. –

+0

@ user15124 Wenn Sie den Code verwenden, um die Antwort zu erhalten, dann würde es gut funktionieren. –

Verwandte Themen