Estoy tratando de encontrar una función Delphi que divida una cadena de entrada en una matriz de cadenas basada en un delimitador. He encontrado mucho en Google, pero todos parecen tener sus propios problemas y no he podido hacer que ninguno funcione.
Solo necesito dividir una cadena como:
"word:doc,txt,docx"
en una matriz basada en ':'. El resultado sería
['word', 'doc,txt,docx']
.
¿Alguien tiene una función que sabe que funciona?
Gracias
No es necesario diseñar una
Split
función. Ya existe, véase:Classes.ExtractStrings
.Úselo de la siguiente manera:
program Project1; {$APPTYPE CONSOLE} uses Classes; var List: TStrings; begin List := TStringList.Create; try ExtractStrings([':'], [], PChar('word:doc,txt,docx'), List); WriteLn(List.Text); ReadLn; finally List.Free; end; end.
Y para responder la pregunta completamente;
List
representa la matriz deseada con los elementos:List[0] = 'word' List[1] = 'doc,txt,docx'
fuente
split
función, sino la necesidad de unTStrings
objeto. Y debido a la inflexibilidad que menciona (@awmross), preferiría la solución de FrankPuede utilizar
StrUtils.SplitString
.function SplitString(const S, Delimiters: string): TStringDynArray;
Su descripción de la documentación :
fuente
Usando la función SysUtils.TStringHelper.Split , introducida en Delphi XE3:
var MyString: String; Splitted: TArray<String>; begin MyString := 'word:doc,txt,docx'; Splitted := MyString.Split([':']); end.
Esto dividirá una cadena con un delimitador dado en una matriz de cadenas.
fuente
Siempre uso algo similar a esto:
Uses StrUtils, Classes; Var Str, Delimiter : String; begin // Str is the input string, Delimiter is the delimiter With TStringList.Create Do try Text := ReplaceText(S,Delim,#13#10); // From here on and until "finally", your desired result strings are // in strings[0].. strings[Count-1) finally Free; //Clean everything up, and liberate your memory ;-) end; end;
fuente
Strutils::AnsiReplaceText
list.Text := AnsiReplaceStr(source, delimiter, #13#10);
.Similar a la función Explode () ofrecida por Mef, pero con un par de diferencias (una de las cuales considero una corrección de errores):
type TArrayOfString = array of String; function SplitString(const aSeparator, aString: String; aMax: Integer = 0): TArrayOfString; var i, strt, cnt: Integer; sepLen: Integer; procedure AddString(aEnd: Integer = -1); var endPos: Integer; begin if (aEnd = -1) then endPos := i else endPos := aEnd + 1; if (strt < endPos) then result[cnt] := Copy(aString, strt, endPos - strt) else result[cnt] := ''; Inc(cnt); end; begin if (aString = '') or (aMax < 0) then begin SetLength(result, 0); EXIT; end; if (aSeparator = '') then begin SetLength(result, 1); result[0] := aString; EXIT; end; sepLen := Length(aSeparator); SetLength(result, (Length(aString) div sepLen) + 1); i := 1; strt := i; cnt := 0; while (i <= (Length(aString)- sepLen + 1)) do begin if (aString[i] = aSeparator[1]) then if (Copy(aString, i, sepLen) = aSeparator) then begin AddString; if (cnt = aMax) then begin SetLength(result, cnt); EXIT; end; Inc(i, sepLen - 1); strt := i + 1; end; Inc(i); end; AddString(Length(aString)); SetLength(result, cnt); end;
Diferencias:
Ejemplos:
SplitString(':', 'abc') returns : result[0] = abc SplitString(':', 'a:b:c:') returns : result[0] = a result[1] = b result[2] = c result[3] = <empty string> SplitString(':', 'a:b:c:', 2) returns: result[0] = a result[1] = b
Es el separador final y el "elemento final vacío" teórico lo que considero la corrección del error.
También incorporé el cambio de asignación de memoria que sugerí, con refinamiento (sugerí erróneamente que la cadena de entrada podría contener como máximo un 50% de separadores, pero posiblemente podría consistir, por supuesto, en cadenas de separación al 100%, ¡produciendo una matriz de elementos vacíos!)
fuente
Explode es una función de muy alta velocidad, la fuente se obtiene del componente TStrings. Utilizo la siguiente prueba para explotar: explotar 134217733 bytes de datos, obtengo 19173962 elementos, tiempo de trabajo: 2984 ms.
Implode es una función de muy baja velocidad, pero la escribo fácilmente.
{ ****************************************************************************** } { Explode/Implode (String <> String array) } { ****************************************************************************** } function Explode(S: String; Delimiter: Char): Strings; overload; var I, C: Integer; P, P1: PChar; begin SetLength(Result, 0); if Length(S) = 0 then Exit; P:=PChar(S+Delimiter); C:=0; while P^ <> #0 do begin P1:=P; while (P^ <> Delimiter) do P:=CharNext(P); Inc(C); while P^ in [#1..' '] do P:=CharNext(P); if P^ = Delimiter then begin repeat P:=CharNext(P); until not (P^ in [#1..' ']); end; end; SetLength(Result, C); P:=PChar(S+Delimiter); I:=-1; while P^ <> #0 do begin P1:=P; while (P^ <> Delimiter) do P:=CharNext(P); Inc(I); SetString(Result[I], P1, P-P1); while P^ in [#1..' '] do P:=CharNext(P); if P^ = Delimiter then begin repeat P:=CharNext(P); until not (P^ in [#1..' ']); end; end; end; function Explode(S: String; Delimiter: Char; Index: Integer): String; overload; var I: Integer; P, P1: PChar; begin if Length(S) = 0 then Exit; P:=PChar(S+Delimiter); I:=1; while P^ <> #0 do begin P1:=P; while (P^ <> Delimiter) do P:=CharNext(P); SetString(Result, P1, P-P1); if (I <> Index) then Inc(I) else begin SetString(Result, P1, P-P1); Exit; end; while P^ in [#1..' '] do P:=CharNext(P); if P^ = Delimiter then begin repeat P:=CharNext(P); until not (P^ in [#1..' ']); end; end; end; function Implode(S: Strings; Delimiter: Char): String; var iCount: Integer; begin Result:=''; if (Length(S) = 0) then Exit; for iCount:=0 to Length(S)-1 do Result:=Result+S[iCount]+Delimiter; System.Delete(Result, Length(Result), 1); end;
fuente
Strings
no es un tipo.var su : string; // What we want split si : TStringList; // Result of splitting Delimiter : string; ... Delimiter := ';'; si.Text := ReplaceStr(su, Delimiter, #13#10);
Las líneas de la lista si contendrán cadenas divididas.
fuente
Puede crear su propia función que devuelve TArray de cadena:
function mySplit(input: string): TArray<string>; var delimiterSet: array [0 .. 0] of char; // split works with char array, not a single char begin delimiterSet[0] := '&'; // some character result := input.Split(delimiterSet); end;
fuente
Aquí hay una implementación de una función de explosión que está disponible en muchos otros lenguajes de programación como función estándar:
type TStringDynArray = array of String; function Explode(const Separator, S: string; Limit: Integer = 0): TStringDynArray; var SepLen: Integer; F, P: PChar; ALen, Index: Integer; begin SetLength(Result, 0); if (S = '') or (Limit < 0) then Exit; if Separator = '' then begin SetLength(Result, 1); Result[0] := S; Exit; end; SepLen := Length(Separator); ALen := Limit; SetLength(Result, ALen); Index := 0; P := PChar(S); while P^ <> #0 do begin F := P; P := AnsiStrPos(P, PChar(Separator)); if (P = nil) or ((Limit > 0) and (Index = Limit - 1)) then P := StrEnd(F); if Index >= ALen then begin Inc(ALen, 5); SetLength(Result, ALen); end; SetString(Result[Index], F, P - F); Inc(Index); if P^ <> #0 then Inc(P, SepLen); end; if Index < ALen then SetLength(Result, Index); end;
Uso de muestra:
var res: TStringDynArray; begin res := Explode(':', yourString);
fuente
Escribí esta función que devuelve una lista vinculada de cadenas separadas por un delimitador específico. Pascal libre puro sin módulos.
Program split_f; type PTItem = ^TItem; TItem = record str : string; next : PTItem; end; var s : string; strs : PTItem; procedure split(str : string;delim : char;var list : PTItem); var i : integer; buff : PTItem; begin new(list); buff:= list; buff^.str:=''; buff^.next:=nil; for i:=1 to length(str) do begin if (str[i] = delim) then begin new(buff^.next); buff:=buff^.next; buff^.str := ''; buff^.next := nil; end else buff^.str:= buff^.str+str[i]; end; end; procedure print(var list:PTItem); var buff : PTItem; begin buff := list; while buff<>nil do begin writeln(buff^.str); buff:= buff^.next; end; end; begin s := 'Hi;how;are;you?'; split(s, ';', strs); print(strs); end.
fuente
Jedi Code Library proporciona una StringList mejorada con la función Split incorporada, que es capaz de agregar y reemplazar el texto existente. También proporciona una interfaz contada por referencias. Por lo tanto, esto se puede usar incluso con versiones anteriores de Delphi que no tienen SplitStrings y sin personalizaciones cuidadosas y un poco tediosas de TStringList estándar para usar solo delimitadores especificados.
Por ejemplo, un archivo de texto de líneas dado como
Dog 5 4 7
uno puede analizarlas usando:var slF, slR: IJclStringList; ai: TList<integer>; s: string; i: integer; action: procedure(const Name: string; Const Data: array of integer); slF := TJclStringList.Create; slF.LoadFromFile('some.txt'); slR := TJclStringList.Create; for s in slF do begin slR.Split(s, ' ', true); ai := TList<Integer>.Create; try for i := 1 to slR.Count - 1 do ai.Add(StrToInt(slR[i])); action(slR[0], ai.ToArray); finally ai.Free; end; end;
http://wiki.delphi-jedi.org/wiki/JCL_Help:IJclStringList.Split@string@string@Boolean
fuente
Esto resolverá tu problema
interface TArrayStr = Array Of string; implementation function SplitString(Text: String): TArrayStr; var intIdx: Integer; intIdxOutput: Integer; const Delimiter = ';'; begin intIdxOutput := 0; SetLength(Result, 1); Result[0] := ''; for intIdx := 1 to Length(Text) do begin if Text[intIdx] = Delimiter then begin intIdxOutput := intIdxOutput + 1; SetLength(Result, Length(Result) + 1); end else Result[intIdxOutput] := Result[intIdxOutput] + Text[intIdx]; end; end;
fuente
Mi función favorita para dividir:
procedure splitString(delim: char; s: string; ListOfStrings: TStrings); var temp: string; i: integer; begin ListOfStrings.Clear; for i:=1 to length(s) do begin if s[i] = delim then begin ListOfStrings.add(temp); temp := ''; end else begin temp := temp + s[i]; if i=length(s) then ListOfStrings.add(temp); end; end; ListOfStrings.add(temp); end;
fuente
ListOfStrings.add(temp);
después del bucle para agregar el último elemento.*
//Basic functionality of a TStringList solves this: uses Classes //TStringList ,types //TStringDynArray ,SysUtils //StringReplace() ; .... //-------------------------------------------------------------------------- function _SplitString(const s:string; const delimiter:Char):TStringDynArray; var sl:TStringList; i:integer; begin sl:=TStringList.Create; //separete delimited items by sLineBreak;TStringlist will do the job: sl.Text:=StringReplace(s,delimiter,sLineBreak,[rfReplaceAll]); //return the splitted string as an array: setlength(Result,sl.count); for i:=0 to sl.Count-1 do Result[i]:=sl[i]; sl.Free; end; //To split a FileName (last item will be the pure filename itselfs): function _SplitPath(const fn:TFileName):TStringDynArray; begin result:=_SplitString(fn,'\'); end;
*
fuente
La base de NGLG responde https://stackoverflow.com/a/8811242/6619626 puede usar la siguiente función:
type OurArrayStr=array of string; function SplitString(DelimeterChars:char;Str:string):OurArrayStr; var seg: TStringList; i:integer; ret:OurArrayStr; begin seg := TStringList.Create; ExtractStrings([DelimeterChars],[], PChar(Str), seg); for i:=0 to seg.Count-1 do begin SetLength(ret,length(ret)+1); ret[length(ret)-1]:=seg.Strings[i]; end; SplitString:=ret; seg.Free; end;
Funciona en todas las versiones de Delphi.
fuente
Para delphi 2010, debe crear su propia función de división.
function Split(const Texto, Delimitador: string): TStringArray; var i: integer; Len: integer; PosStart: integer; PosDel: integer; TempText:string; begin i := 0; SetLength(Result, 1); Len := Length(Delimitador); PosStart := 1; PosDel := Pos(Delimitador, Texto); TempText:= Texto; while PosDel > 0 do begin Result[i] := Copy(TempText, PosStart, PosDel - PosStart); PosStart := PosDel + Len; TempText:=Copy(TempText, PosStart, Length(TempText)); PosDel := Pos(Delimitador, TempText); PosStart := 1; inc(i); SetLength(Result, i + 1); end; Result[i] := Copy(TempText, PosStart, Length(TempText)); end;
Puedes referirte a él como tal
type TStringArray = array of string; var Temp2:TStringArray; Temp1="hello:world"; Temp2=Split(Temp1,':')
fuente
procedure SplitCSV(S:STRING;out SL:TStringList); var c,commatext:string; a,b,up:integer; begin c:=s.Replace(' ','<SPACE>'); //curate spaces //first ocurrence of " a:=pos('"',c); b:=pos('"',c,a+1); if (a>0) and (b>0) then begin commatext:=commatext+copy(c,0,a-1); commatext:=commatext+copy(c,a,b-a+1).Replace(',','<COMMA>'); //curate commas up:=b+1; end else commatext:=c; //while continue discovering " while (a>0) and (b>0) do begin a:=Pos('"',c,b+1); b:=pos('"',c,a+1); if (a>0) and (b>0) then begin commatext:=commatext+copy(c,up,a-up); commatext:=commatext+copy(c,a,b-a+1).Replace(',','<COMMA>'); //curate commas up:=b+1; end; end; //last piece of text end if up<c.Length then commatext:=commatext+copy(c,up,c.Length-up+1); //split text using CommaText sl.CommaText:=commatext; sl.Text:=sl.Text.Replace('<COMMA>',','); //curate commas sl.Text:=sl.Text.Replace('<SPACE>',' '); //curate spaces end;
fuente
interface uses Classes; type TStringArray = array of string; TUtilStr = class class function Split(const AValue: string; const ADelimiter: Char = ';'; const AQuoteChar: Char = '"'): TStringArray; static; end; implementation { TUtilStr } class function TUtilStr.Split(const AValue: string; const ADelimiter: Char; const AQuoteChar: Char): TStringArray; var LSplited: TStringList; LText: string; LIndex: Integer; begin LSplited := TStringList.Create; try LSplited.StrictDelimiter := True; LSplited.Delimiter := ADelimiter; LSplited.QuoteChar := AQuoteChar; LSplited.DelimitedText := AValue; SetLength(Result, LSplited.Count); for LIndex := 0 to LSplited.Count - 1 do begin Result[LIndex] := LSplited[LIndex]; end; finally LSplited.Free; end; end; end.
fuente