Разбить строку на массив строк на основе разделителя

71

Я пытаюсь найти функцию Delphi, которая разбивает входную строку на массив строк на основе разделителя. Я нашел много на Google, но у всех, похоже, есть свои проблемы, и я не смог заставить их работать.

Мне просто нужно разбить строку как: "word:doc,txt,docx" в массив на основе ':'. Результатом будет ['word', 'doc,txt,docx'].

Есть ли у кого-нибудь функция, которую они знают?

Спасибо

Теги:
string
split
delimiter

17 ответов

75
Лучший ответ

вы можете использовать свойство TStrings.DelimitedText для разделения строки

проверьте этот образец

program Project28;

{$APPTYPE CONSOLE}

uses
  Classes,
  SysUtils;

procedure Split(Delimiter: Char; Str: string; ListOfStrings: TStrings) ;
begin
   ListOfStrings.Clear;
   ListOfStrings.Delimiter       := Delimiter;
   ListOfStrings.StrictDelimiter := True; // Requires D2006 or newer.
   ListOfStrings.DelimitedText   := Str;
end;


var
   OutPutList: TStringList;
begin
   OutPutList := TStringList.Create;
   try
     Split(':', 'word:doc,txt,docx', OutPutList) ;
     Writeln(OutPutList.Text);
     Readln;
   finally
     OutPutList.Free;
   end;
end.

ОБНОВЛЕНИЕ

См. ссылку для объяснения StrictDelimiter.

  • 20
    К сожалению, во многих «старых» версиях Delphi есть ошибка (не уверен, с какой версией это исправлено), из-за которой символ пробела всегда используется в качестве разделителя. Так что обращайтесь с этим с осторожностью!
  • 14
    Да уж. Вы захотите установить для StrictDelimiter значение true, и если свойство StrictDelimiter недоступно в вашей версии Delphi, не используйте эту технику! Но если это так, то это очень полезно.
Показать ещё 6 комментариев
59

Нет необходимости в разработке функции Split. Он уже существует, см. Classes.ExtractStrings.

Используйте его следующим образом:

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.

И ответить на вопрос полностью; List представляет желаемый массив с элементами:

List[0] = 'word'
List[1] = 'doc,txt,docx'
  • 12
    ExtractStrings очень негибки: «Символы возврата каретки, символов новой строки и кавычек (одинарные или двойные) всегда рассматриваются как разделители.»; и «Примечание: ExtractStrings не добавляет пустые строки в список».
  • 0
    Проблема заключается не в разработке функции split , а в необходимости объекта TStrings . И из-за негибкости (@awmross) упоминает, я бы предпочел решение Фрэнка
33

Вы можете использовать StrUtils.SplitString.

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

Его описание из документации:

Разделяет строку на разные части, ограниченные указанными символами разделителя.

SplitString разделяет строку на разные части, разделенные указанными символами разделителя. S - это строка, которую нужно разбить. Разделители - это строка, содержащая символы, определенные как разделители.

SplitString возвращает массив строк типа System.Types.TStringDynArray, который содержит разделенные части исходной строки.

  • 3
    Хммм, не в моей версии Delphi 2010 (есть подпрограмма SplitString в XMLDoc и в (Indy unit) IdStrings, но ни один из них не делает то, что хочет плакат, и подпрограмма XMLDoc в любом случае не отображается через интерфейс модуля).
  • 3
    function SplitString (const S, Delimiters: string): TStringDynArray; определено в StrUtils.pas
Показать ещё 3 комментария
30

Используя функцию SysUtils.TStringHelper.Split, введенную в Delphi XE3:

var
  MyString: String;
  Splitted: TArray<String>;
begin
  MyString := 'word:doc,txt,docx';
  Splitted := MyString.Split([':']);
end.

Это разделит строку с заданным разделителем на массив строк.

  • 0
    Только это сработало для UTF-8 предложений.
12

Я всегда использую нечто похожее на это:

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;
  • 2
    Отличное решение для пользователей старых версий Delphi.
  • 0
    Пользователи C ++ Builder 6: соответствующая функция - Strutils::AnsiReplaceText
Показать ещё 1 комментарий
12

Подобно функции Explode(), предлагаемой Mef, но с несколькими отличиями (один из которых я считаю исправлением ошибки):

  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;

Отличия:

  • Параметр aMax ограничивает количество возвращаемых строк
  • Если входная строка заканчивается разделителем, то считается, что существует номинальная "пустая" конечная строка.

Примеры:

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

Это конечный разделитель и условный "пустой конечный элемент", который я считаю исправлением ошибки.

Я также включил изменение распределения памяти, которое я предложил, с уточнением (я ошибочно предположил, что входная строка может содержать не более 50% разделителей, но вполне возможно, что она состоит из 100% разделительных строк, что дает массив пустых элементов! )

6
var  
    su  : string;        // What we want split
    si  : TStringList;   // Result of splitting
    Delimiter : string;
    ...
    Delimiter := ';';
    si.Text := ReplaceStr(su, Delimiter, #13#10);

Строки в списке si будут содержать разделенные строки.

6

Взрыв - очень высокая скорость, исходный алгоритм get из компонента TStrings. Я использую следующий тест для взрыва: Взорвать 134217733 байтов данных, получить 19173962 элементов, время работы: 2984 мс.

Implode - очень низкая скорость, но я просто пишу.

{ ****************************************************************************** }
{  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;
  • 3
    Это не компилируется: Strings не является типом.
4

Я написал эту функцию, которая возвращает связанный список разделенных строк определенным разделителем. Чистый свободный паскаль без модулей.

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.
4

Вы можете создать свою собственную функцию, которая возвращает TArray строки:

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;
4

Здесь - реализация функции разрыва, которая доступна во многих других языках программирования в качестве стандартной функции:

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; 

Использование образца:

var
  res: TStringDynArray;
begin
  res := Explode(':', yourString);
  • 2
    В этом коде есть несколько странных и потенциально чрезвычайно неэффективных вариантов управления / прогнозирования длины результата. При постепенном увеличении результирующего массива увеличивается вероятность перераспределения памяти и фрагментации. Более эффективным было бы установить начальную длину настолько большой, насколько это возможно, т. Е. Предположить, что входная строка состоит из 50% разделительных строк = длина (S) div (2 * длина (разделитель). Затем установите для нее фактическое число элементы, когда выполнено. 1 выделение может последовать за одним усечением.
  • 0
    Также вы не объясняете назначение параметра Limit. Я интуитивно ожидал, что он установит максимальное количество возвращаемых подстрок, хотя на самом деле он ограничивает обнаружение подстрок первым «пределом» # символов во входной строке. Это кажется бессмысленным, поскольку, если вам нужно сделать это, вы можете просто использовать Explode () над Copy () требуемой подстроки. Использование Limit для установки максимального количества подстрок было бы гораздо более полезным.
Показать ещё 3 комментария
2

Это решит вашу проблему.

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;
  • 0
    Не могли бы вы дать объяснение, что делает код? Спасибо
  • 0
    он проходит через переданную строку в поисках разделителя const, если не найден, конкатенируется с текущей позицией в массиве, когда находит, переходит на следующую позицию в динамическом массиве
2

Библиотека кода Jedi предоставляет расширенный StringList со встроенной функцией Split, которая способна как добавлять, так и заменять существующий текст. Он также обеспечивает интерфейс с подсчетом ссылок. Таким образом, это можно использовать даже со старыми версиями Delphi, у которых нет SplitStrings и без осторожных и немного утомительных настроек запаса TStringList, чтобы использовать только указанные разделители.

Например, данный текстовый файл строк типа Dog 5 4 7 можно проанализировать, используя:

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

0

Моя любимая функция для резки:

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];
        end;
    end;
end;
  • 0
    Последний элемент был пропущен в вашей функции
0

Для Delphi 2010 вам необходимо создать свою собственную функцию split.

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;

Вы можете ссылаться на нее как таковую

type
  TStringArray = array of string;
var Temp2:TStringArray;
Temp1="hello:world";
Temp2=Split(Temp1,':')
0

В базе ответа NGLG https://stackoverflow.com/questions/2625707/split-a-string-into-an-array-of-strings-based-on-a-delimiter вы можете использовать следующую функцию:

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;

Он работает во всех версиях Delphi.

0

*

//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;

*

  • 3
    Как это лучше, чем принятый ответ?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню