2016-03-07 21 views
9

Używam StrUtils w celu podzielenia łańcucha na TStringDynArray, ale wynik nie był zgodny z oczekiwaniami. Postaram się wyjaśnić kwestię:StrUtils.SplitString nie działa zgodnie z oczekiwaniami

Mam ciąg str: 'a'; 'b'; 'c'
Teraz nazywa StrUtils.SplitString(str, '; '); podzielić ciąg i spodziewałem tablicę z trzech elementów: 'a', 'b', 'c'

ale co ja jest tablicą z pięcioma elementami: 'a', '', 'b', '', 'c'.
Kiedy dzielę tylko na ';' zamiast '; ', otrzymuję trzy elementy z wiodącym wykresem.

Dlaczego więc otrzymuję puste ciągi w moim pierwszym rozwiązaniu?

+3

Przeczytaj dokumentację. Być może nie zgodnie z oczekiwaniami, ale działa tak jak udokumentowano. –

+0

To pytanie zawiera kilka sugestii dotyczących podziału ciągu znaków na ciąg wieloznaczny (oczekiwany), ale większość z nich działa na listach ciągów, a nie na tablicach: http://stackoverflow.com/questions/15424293/how-to-split-string-by-a-multi-character-delimiter – quasoft

Odpowiedz

15

Ta funkcja nie służy do łączenia kolejnych separatorów. Na przykład, należy rozważyć podział następujący ciąg na przecinkami:

foo,,bar 

czego można oczekiwać SplitString('foo,,bar', ',') wrócić? Czy chcesz uzyskać numer ('foo', 'bar'), czy też odpowiedź brzmi: ('foo', '', 'bar')? Nie jest jasne, czy a priori ma rację, a różne przypadki użycia mogą wymagać różnych wyników.

Jeśli Twoja sprawa, określono dwa ograniczniki, ';' i ' '. Oznacza to, że

'a'; 'b' 

rozłamy w ';' i ponownie w ' '. Pomiędzy tymi dwoma ogranicznikami nie ma nic, a zatem pusty ciąg jest zwracany między 'a' i 'b'.

Metoda Split z string helper wprowadzona w XE3 ma parametr TStringSplitOptions. Jeśli dla tego parametru zostanie przekazany ExcludeEmpty, kolejne separatory będą traktowane jako pojedynczy separator. Ten program:

{$APPTYPE CONSOLE} 

uses 
    System.SysUtils; 

var 
    S: string; 

begin 
    for S in '''a''; ''b''; ''c'''.Split([';', ' '], ExcludeEmpty) do begin 
    Writeln(S); 
    end; 
end. 

wyjścia:

 
'a' 
'b' 
'c' 

Ale nie masz to dostępne w XE2 więc myślę, że będziesz musiał toczyć własną funkcję Split. Które może wyglądać tak:

function IsSeparator(const C: Char; const Separators: string): Boolean; 
var 
    sep: Char; 
begin 
    for sep in Separators do begin 
    if sep=C then begin 
     Result := True; 
     exit; 
    end; 
    end; 
    Result := False; 
end; 

function Split(const Str, Separators: string): TArray<string>; 
var 
    CharIndex, ItemIndex: Integer; 
    len: Integer; 
    SeparatorCount: Integer; 
    Start: Integer; 
begin 
    len := Length(Str); 
    if len=0 then begin 
    Result := nil; 
    exit; 
    end; 

    SeparatorCount := 0; 
    for CharIndex := 1 to len do begin 
    if IsSeparator(Str[CharIndex], Separators) then begin 
     inc(SeparatorCount); 
    end; 
    end; 

    SetLength(Result, SeparatorCount+1); // potentially an over-allocation 
    ItemIndex := 0; 
    Start := 1; 
    CharIndex := 1; 
    for CharIndex := 1 to len do begin 
    if IsSeparator(Str[CharIndex], Separators) then begin 
     if CharIndex>Start then begin 
     Result[ItemIndex] := Copy(Str, Start, CharIndex-Start); 
     inc(ItemIndex); 
     end; 
     Start := CharIndex+1; 
    end; 
    end; 

    if len>Start then begin 
    Result[ItemIndex] := Copy(Str, Start, len-Start+1); 
    inc(ItemIndex); 
    end; 

    SetLength(Result, ItemIndex); 
end; 

Oczywiście, wszystko to zakłada, że ​​spacja ma działać jako separator. Poprosiłeś o to w kodzie, ale być może chcesz tylko ; działać jako separator. W takim przypadku prawdopodobnie chcesz przekazać ';' jako separator i przyciąć ciągi, które są zwracane.

+0

dziękuję za szczegółowe wyjaśnienie! –

14

SplitString jest zdefiniowany jako

function SplitString(const S, Delimiters: string): TStringDynArray; 

Można by pomyślał, że Delimiters oznaczać pojedynczy ciąg separatora używany do rozłupywania ciąg, ale faktycznie oznacza zestaw pojedynczych znaków używanych do podziału łańcucha. Każdy znak w łańcuchu Delimiters będzie używany jako jeden z możliwych ograniczników.

SplitString

Dzieli ciąg znaków w różnych częściach oddzielonych od podanych ogranicznika znaków. SplitString dzieli ciąg na różne części ograniczone przez określony separator znaków. S jest ciągiem do podziału na . Ograniczniki to ciąg znaków zawierający znaki ograniczające zdefiniowane jako.

+1

Zakładam, że nazwaliby to "ogranicznikiem" (w liczbie pojedynczej), a nie "ogranicznikami". FWIW, W późniejszych wersjach 'TStringHelper' ma wersję' Split', która również bierze ciąg znaków jako ogranicznik, nie tylko znaki, Ale niestety nie w XE2. –

+0

@RudyVelthuis Zgoda. Ale cienka linia między znacznikami Delimiter i Delimiters może zostać utracona, jeśli nie jesteś native speakerem języka angielskiego. Poza tym, operacje dzielenia w innych językach zwykle przyjmują kompletny, dokładny ogranicznik, więc ta implementacja Delphi jest również myląca z tego aspektu. –

+0

@RudyVelthuis, Ale Split ma również swój własny zestaw dziwactw: http://stackoverflow.com/questions/28410901/string-split-works-strange-when-last-value-is-empty –

5

Dzieje się tak, ponieważ drugi parametr SplitString to lista pojedynczych znaków ograniczających, więc "; "oznacza split na a"; LUB podzielić na "". Więc łańcuch jest dzielony na każdym ";" i w każdej przestrzeni i między ";" i "nie ma nic, stąd puste struny.

Powiązane problemy