86 votes

Divise une chaîne de caractères en un tableau de chaînes de caractères basé sur un délimiteur.

J'essaie de trouver une fonction Delphi qui divise une chaîne d'entrée en un tableau de chaînes de caractères en fonction d'un délimiteur. J'en ai trouvé beaucoup en cherchant sur le web, mais toutes semblent avoir leurs propres problèmes et je n'ai pas réussi à en faire fonctionner une seule.

J'ai juste besoin de diviser une chaîne de caractères comme : "word:doc,txt,docx" dans un tableau basé sur ':'. Le résultat serait ['word', 'doc,txt,docx'] . Comment puis-je le faire ?

93voto

RRUZ Points 98685

Vous pouvez utiliser la propriété TStrings.DelimitedText pour diviser une chaîne de caractères.

consultez cet échantillon

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.

UPDATE

Voir ceci lien pour une explication de StrictDelimiter .

22 votes

Malheureusement, il existe un bogue dans de nombreuses "anciennes" versions de Delphi (je ne sais pas avec quelle version il a été corrigé) qui a pour effet que le caractère d'espace est siempre utilisé comme délimiteur. Donc, manipulez ceci avec précaution !

16 votes

Ouais. Vous devez définir StrictDelimiter sur true, et si la propriété StrictDelimiter n'est pas disponible dans votre version de Delphi, n'utilisez pas cette technique ! Mais si elle l'est, elle est très utile.

3 votes

Ce n'était pas un bug, c'était une décision de conception (ennuyeuse) datant de D1 ou D2. CommaText était censé entourer de guillemets tous les champs contenant des espaces. Si les champs contenant des espaces sont entourés de guillemets doubles, le résultat est correct.

72voto

NGLN Points 25671

Il n'est pas nécessaire de concevoir un Split fonction. Elle existe déjà, voyez : Classes.ExtractStrings .

Utilisez-le de la manière suivante :

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.

Et pour répondre pleinement à la question ; List représente le tableau souhaité avec les éléments :

List[0] = 'word'
List[1] = 'doc,txt,docx'

14 votes

ExtractStrings est très peu flexible : "Les retours chariot, les caractères de nouvelle ligne et les caractères de citation (simples ou doubles) sont toujours traités comme des séparateurs" ; et "Note : ExtractStrings n'ajoute pas les chaînes vides à la liste".

0 votes

Le problème n'est pas de concevoir un split mais la nécessité d'une TStrings objet. Et à cause de l'inflexibilité (@awmross) mentionnée, je préférerais La solution de Frank

55voto

alex Points 549

Vous pouvez utiliser StrUtils.SplitString .

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

Sa description à partir du documentation :

Scinde une chaîne de caractères en différentes parties délimitées par les caractères de délimitation spécifiés.

Chaînes de fractionnement divise une chaîne de caractères en différentes parties délimitées par les caractères de délimitation spécifiés. S est la chaîne de caractères à diviser. Délimiteurs est une chaîne de caractères contenant les caractères définis comme délimiteurs.

Chaînes de fractionnement renvoie un tableau de chaînes de caractères de type System.Types.TStringDynArray qui contient les parties divisées de la chaîne originale.

3 votes

Hmmm, pas dans ma version de Delphi 2010 (il y a une routine SplitString dans XMLDoc et dans (l'unité Indy) IdStrings, mais aucune d'entre elles ne fait ce que l'affiche veut et la routine XMLDoc n'est pas exposée par l'interface de l'unité de toute façon).

3 votes

Fonction SplitString(const S, Delimiters : string) : TStringDynArray ; définie dans StrUtils.pas

0 votes

Je ne parviens pas à inclure le fichier StrUtils.pas (même s'il est présent).

20voto

Frank Points 88

J'utilise toujours quelque chose de similaire à ceci :

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 votes

Solution idéale pour les utilisateurs d'anciennes versions de Delphi.

0 votes

Utilisateurs de C++ Builder 6 : la fonction correspondante est Strutils::AnsiReplaceText

0 votes

Étonnamment simple. Je travaille dans Delphi 7 avec : list.Text := AnsiReplaceStr(source, delimiter, #13#10); .

16voto

Deltics Points 9213

Semblable à la Explode() La fonction proposée par Mef, mais avec quelques différences (dont une que je considère comme une correction de bug) :

  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;

Différences :

  1. un paramètreMax limite le nombre de chaînes de caractères à renvoyer
  2. Si la chaîne d'entrée est terminée par un séparateur, on considère qu'il existe une chaîne finale nominale "vide".

Exemples :

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

C'est le séparateur de fin de ligne et l'"élément final vide" théorique que je considère comme la correction du bug.

J'ai également incorporé le changement d'allocation de mémoire que j'ai suggéré, avec un raffinement (j'ai suggéré par erreur que la chaîne d'entrée pouvait contenir au maximum 50% de séparateurs, mais il est concevable qu'elle puisse bien sûr consister en 100% de chaînes de séparateurs, ce qui donne un tableau d'éléments vides !)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X