Comment Générer des UUID en Delphi : Guide Complet avec Exemples

Comprendre les UUIDs dans le Développement Delphi

Les Identifiants Uniques Universels (UUIDs), également connus sous le nom de GUIDs (Globally Unique Identifiers) dans le développement Windows, sont des valeurs de 128 bits qui fournissent une identification unique à travers les systèmes distribués. Dans la programmation Delphi, les GUIDs sont essentiels pour les interfaces COM, les clés primaires de base de données, les applications distribuées et pour garantir l'unicité sans coordination centralisée. Que vous développiez des applications de bureau avec VCL/FMX ou des applications serveur, comprendre la génération d'UUID est crucial.

Ce guide complet explore plusieurs approches pour générer des UUIDs dans les applications Delphi, couvrant à la fois les fonctions RTL natives et les appels API Windows. Nous examinerons les stratégies d'implémentation pour différentes versions de Delphi, du classique Delphi 7 aux dernières versions de RAD Studio, ainsi que les considérations multiplateformes pour les applications FireMonkey. Les techniques présentées ici s'appliquent également au développement Free Pascal et Lazarus.

4 Méthodes Efficaces pour Générer des UUIDs en Delphi

Méthode 1 : Utiliser la Fonction CreateGUID

La façon la plus simple et la plus courante de générer un UUID en Delphi est d'utiliser la fonction intégrée CreateGUID de l'unité System.SysUtils. Cette méthode fonctionne sur toutes les versions et plateformes Delphi.

program GenererUUID;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

var
  MonGUID: TGUID;
  ChaineGUID: string;
begin
  try
    // Générer un nouveau GUID
    if CreateGUID(MonGUID) = S_OK then
    begin
      // Convertir le GUID en représentation chaîne
      ChaineGUID := GUIDToString(MonGUID);
      
      WriteLn('UUID Généré: ', ChaineGUID);
      // Exemple de sortie: UUID Généré: {8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75}
      
      // Alternative: Obtenir la chaîne sans accolades
      ChaineGUID := MonGUID.ToString;
      WriteLn('Sans accolades: ', ChaineGUID);
      // Exemple de sortie: Sans accolades: 8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75
    end
    else
      WriteLn('Échec de création du GUID');
      
    ReadLn;
  except
    on E: Exception do
      WriteLn('Erreur: ', E.Message);
  end;
end.

CreateGUID génère un UUID Version 4 (aléatoire) en utilisant le générateur de nombres aléatoires cryptographiquement sûr du système d'exploitation. Sur Windows, il appelle CoCreateGuid en interne, tandis que sur d'autres plateformes, il utilise les fonctions système appropriées. La fonction retourne S_OK (0) en cas de succès. Le type d'enregistrement TGUID représente la structure GUID, et GUIDToString la convertit au format de chaîne standard avec accolades.

Méthode 2 : Appel Direct à l'API Windows

Pour les applications spécifiques à Windows ou lorsque vous avez besoin de plus de contrôle, vous pouvez appeler directement la fonction CoCreateGuid de l'API Windows. Cette approche nécessite l'unité Winapi.ActiveX.

program GUIDAPIWindows;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Winapi.Windows,
  Winapi.ActiveX;

function GenererGUIDWindows: string;
var
  NouveauGUID: TGUID;
  hr: HResult;
begin
  // Initialiser COM (requis pour CoCreateGuid)
  CoInitialize(nil);
  try
    // Appeler directement l'API Windows
    hr := CoCreateGuid(NouveauGUID);
    
    if Succeeded(hr) then
    begin
      // Convertir au format chaîne
      Result := GUIDToString(NouveauGUID);
    end
    else
    begin
      raise Exception.CreateFmt('CoCreateGuid a échoué avec l''erreur: %d', [hr]);
    end;
  finally
    // Nettoyer COM
    CoUninitialize;
  end;
end;

begin
  try
    WriteLn('GUID API Windows: ', GenererGUIDWindows);
    
    // Générer plusieurs GUIDs
    WriteLn('Génération de 5 GUIDs:');
    for var i := 1 to 5 do
      WriteLn(Format('  GUID %d: %s', [i, GenererGUIDWindows]));
      
    ReadLn;
  except
    on E: Exception do
      WriteLn('Erreur: ', E.Message);
  end;
end.

Cette méthode interface directement avec le sous-système COM de Windows pour générer des GUIDs. CoCreateGuid est l'API Windows sous-jacente que CreateGUID appelle sur les plateformes Windows. Bien que cette approche nécessite l'initialisation COM, elle fournit un accès direct à la génération de GUID Windows et peut être utile lors du travail avec des interfaces COM ou lorsque vous devez vous assurer que vous utilisez l'implémentation spécifique à Windows.

Méthode 3 : GUIDs Formatés Personnalisés

Delphi permet un formatage flexible des GUIDs pour répondre à diverses exigences, comme supprimer les accolades, utiliser différents séparateurs ou créer des représentations personnalisées pour des systèmes spécifiques.

program GUIDsFormates;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TGUIDHelper = record helper for TGUID
    function VersChainCompacte: string;
    function VersChaineMajuscules: string;
    function VersFormatRegistre: string;
    function VersFormatURN: string;
  end;

function TGUIDHelper.VersChainCompacte: string;
begin
  // Supprimer tout le formatage (32 chiffres hex)
  Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x',
    [D1, D2, D3, D4[0], D4[1], D4[2], D4[3], D4[4], D4[5], D4[6], D4[7]]);
end;

function TGUIDHelper.VersChaineMajuscules: string;
begin
  Result := UpperCase(ToString);
end;

function TGUIDHelper.VersFormatRegistre: string;
begin
  // Le format registre utilise des accolades
  Result := GUIDToString(Self);
end;

function TGUIDHelper.VersFormatURN: string;
begin
  // Format URN: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  Result := 'urn:uuid:' + ToString;
end;

var
  MonGUID: TGUID;
begin
  CreateGUID(MonGUID);
  
  WriteLn('Format standard:      ', MonGUID.ToString);
  WriteLn('Avec accolades:       ', GUIDToString(MonGUID));
  WriteLn('Compact (sans tirets):', MonGUID.VersChainCompacte);
  WriteLn('Majuscules:           ', MonGUID.VersChaineMajuscules);
  WriteLn('Format registre:      ', MonGUID.VersFormatRegistre);
  WriteLn('Format URN:           ', MonGUID.VersFormatURN);
  
  // Formatage personnalisé avec séparateurs spécifiques
  WriteLn('Format personnalisé:  ',
    Format('%.8x_%.4x_%.4x_%.2x%.2x_%.2x%.2x%.2x%.2x%.2x%.2x',
    [MonGUID.D1, MonGUID.D2, MonGUID.D3, MonGUID.D4[0], MonGUID.D4[1],
     MonGUID.D4[2], MonGUID.D4[3], MonGUID.D4[4], MonGUID.D4[5],
     MonGUID.D4[6], MonGUID.D4[7]]));
  
  ReadLn;
end.

Cet exemple démontre diverses options de formatage GUID en utilisant un helper d'enregistrement. L'enregistrement TGUID contient les champs D1 (longword), D2 et D3 (word), et D4 (tableau de 8 octets) qui représentent la structure GUID. Vous pouvez accéder directement à ces champs pour un formatage personnalisé. Le format compact est utile pour les URLs ou lorsque l'espace de stockage est limité, tandis que le format URN est utilisé dans divers standards Internet.

Méthode 4 : Génération Déterministe d'UUID

Bien que les GUIDs aléatoires conviennent à la plupart des cas, vous avez parfois besoin d'UUIDs déterministes qui peuvent être régénérés à partir d'entrées spécifiques. Cette implémentation crée des UUIDs Version 5 en utilisant le hachage SHA-1.

program GUIDDeterministe;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Hash;

type
  // GUIDs d'espace de noms standard de RFC 4122
  TEspaceNomsGUID = record
    const DNS: TGUID = '{6ba7b810-9dad-11d1-80b4-00c04fd430c8}';
    const URL: TGUID = '{6ba7b811-9dad-11d1-80b4-00c04fd430c8}';
    const OID: TGUID = '{6ba7b812-9dad-11d1-80b4-00c04fd430c8}';
    const X500: TGUID = '{6ba7b814-9dad-11d1-80b4-00c04fd430c8}';
  end;

function CreerGUIDBaseNom(const EspaceNomsGUID: TGUID; const Nom: string): TGUID;
var
  Hash: THashSHA1;
  OctetsHash: TBytes;
  OctetsEspaceNoms: TBytes;
  OctetsNom: TBytes;
  OctetsCombines: TBytes;
  i: Integer;
begin
  // Convertir le GUID d'espace de noms en octets
  SetLength(OctetsEspaceNoms, 16);
  Move(EspaceNomsGUID.D1, OctetsEspaceNoms[0], 4);
  Move(EspaceNomsGUID.D2, OctetsEspaceNoms[4], 2);
  Move(EspaceNomsGUID.D3, OctetsEspaceNoms[6], 2);
  Move(EspaceNomsGUID.D4[0], OctetsEspaceNoms[8], 8);
  
  // Échanger l'ordre des octets pour l'ordre réseau (big-endian)
  // D1 est 4 octets
  for i := 0 to 1 do
  begin
    var temp := OctetsEspaceNoms[i];
    OctetsEspaceNoms[i] := OctetsEspaceNoms[3-i];
    OctetsEspaceNoms[3-i] := temp;
  end;
  // D2 est 2 octets
  var temp := OctetsEspaceNoms[4];
  OctetsEspaceNoms[4] := OctetsEspaceNoms[5];
  OctetsEspaceNoms[5] := temp;
  // D3 est 2 octets  
  temp := OctetsEspaceNoms[6];
  OctetsEspaceNoms[6] := OctetsEspaceNoms[7];
  OctetsEspaceNoms[7] := temp;
  
  // Convertir le nom en octets UTF-8
  OctetsNom := TEncoding.UTF8.GetBytes(Nom);
  
  // Combiner l'espace de noms et le nom
  SetLength(OctetsCombines, Length(OctetsEspaceNoms) + Length(OctetsNom));
  Move(OctetsEspaceNoms[0], OctetsCombines[0], Length(OctetsEspaceNoms));
  Move(OctetsNom[0], OctetsCombines[Length(OctetsEspaceNoms)], Length(OctetsNom));
  
  // Calculer le hash SHA-1
  Hash := THashSHA1.Create;
  Hash.Update(OctetsCombines);
  OctetsHash := Hash.HashAsBytes;
  
  // Copier les 16 premiers octets vers le GUID
  Move(OctetsHash[0], Result.D1, 4);
  Move(OctetsHash[4], Result.D2, 2);
  Move(OctetsHash[6], Result.D3, 2);
  Move(OctetsHash[8], Result.D4[0], 8);
  
  // Définir les bits de version (5) et de variante
  Result.D3 := (Result.D3 and $0FFF) or $5000; // Version 5
  Result.D4[0] := (Result.D4[0] and $3F) or $80; // Variante 10
end;

begin
  try
    var NomTest := 'exemple.com/utilisateur/12345';
    
    // Générer des GUIDs déterministes
    var GUID1 := CreerGUIDBaseNom(TEspaceNomsGUID.URL, NomTest);
    WriteLn('GUID basé sur URL pour "', NomTest, '":');
    WriteLn('  ', GUIDToString(GUID1));
    
    // La même entrée produit la même sortie
    var GUID2 := CreerGUIDBaseNom(TEspaceNomsGUID.URL, NomTest);
    WriteLn('  Même entrée: ', GUIDToString(GUID2));
    WriteLn('  Égal: ', GUIDToString(GUID1) = GUIDToString(GUID2));
    
    WriteLn;
    
    // Différents espaces de noms produisent différents GUIDs
    var GUID3 := CreerGUIDBaseNom(TEspaceNomsGUID.DNS, 'exemple.com');
    WriteLn('GUID basé sur DNS pour "exemple.com":');
    WriteLn('  ', GUIDToString(GUID3));
    
    ReadLn;
  except
    on E: Exception do
      WriteLn('Erreur: ', E.Message);
  end;
end.

Cette implémentation crée des UUIDs RFC 4122 Version 5 en utilisant le hachage SHA-1. La même combinaison d'espace de noms et de nom produit toujours le même UUID, ce qui le rend idéal pour les systèmes distribués où différents nœuds doivent générer des identifiants identiques pour la même ressource. L'espace de noms fournit le contexte (DNS, URL, etc.) tandis que le nom est l'identifiant spécifique. Notez que la conversion de l'ordre des octets est nécessaire car le RFC spécifie l'ordre réseau.

Créer une Unité Utilitaire UUID Réutilisable

Pour les applications qui utilisent fréquemment des UUIDs, il est bénéfique de créer une unité utilitaire qui centralise la logique de génération d'UUID. Voici une unité utilitaire complète pour les applications Delphi :

unit UtilitairesUUID;

interface

uses
  System.SysUtils, System.Hash;

type
  /// <summary>
  /// Classe utilitaire pour la génération et manipulation UUID/GUID
  /// </summary>
  TGenerateurUUID = class
  public
    type
      TVersionUUID = (uvAleatoire, uvBaseeTemps, uvBaseeNom);
      
    class var
      // Espaces de noms standard de RFC 4122
      EspaceNomsDNS: TGUID;
      EspaceNomsURL: TGUID;
      EspaceNomsOID: TGUID;
      EspaceNomsX500: TGUID;
      
  private
    class constructor Create;
    class function EchangerEndian(const AGUID: TGUID): TGUID;
    
  public
    /// <summary>Générer un UUID aléatoire (Version 4)</summary>
    class function NouveauGUID: TGUID;
    
    /// <summary>Générer un UUID séquentiel pour optimisation base de données</summary>
    class function NouveauGUIDSequentiel: TGUID;
    
    /// <summary>Générer un UUID basé sur un nom (Version 5)</summary>
    class function NouveauGUIDBaseNom(const EspaceNomsGUID: TGUID; 
      const Nom: string): TGUID;
      
    /// <summary>Générer UUID déterministe basé sur URL</summary>
    class function NouveauGUIDBaseURL(const URL: string): TGUID;
    
    /// <summary>Générer UUID déterministe basé sur DNS</summary>
    class function NouveauGUIDBaseDNS(const DNS: string): TGUID;
    
    /// <summary>Parser chaîne vers GUID avec validation</summary>
    class function EssayerParserGUID(const S: string; out GUID: TGUID): Boolean;
    
    /// <summary>Vérifier si GUID est vide/nil</summary>
    class function EstGUIDVide(const GUID: TGUID): Boolean;
    
    /// <summary>Obtenir GUID sans accolades ni tirets</summary>
    class function ObtenirGUIDCompact(const GUID: TGUID): string;
  end;

implementation

uses
  System.DateUtils, Winapi.Windows;

{ TGenerateurUUID }

class constructor TGenerateurUUID.Create;
begin
  // Initialiser les espaces de noms standard
  EspaceNomsDNS := StringToGUID('{6ba7b810-9dad-11d1-80b4-00c04fd430c8}');
  EspaceNomsURL := StringToGUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}');
  EspaceNomsOID := StringToGUID('{6ba7b812-9dad-11d1-80b4-00c04fd430c8}');
  EspaceNomsX500 := StringToGUID('{6ba7b814-9dad-11d1-80b4-00c04fd430c8}');
end;

class function TGenerateurUUID.NouveauGUID: TGUID;
begin
  if CreateGUID(Result) <> S_OK then
    raise Exception.Create('Échec de création du GUID');
end;

class function TGenerateurUUID.NouveauGUIDSequentiel: TGUID;
var
  Horodatage: Int64;
  Compteur: Word;
begin
  // Générer GUID de base
  Result := NouveauGUID;
  
  // Obtenir l'horodatage actuel
  Horodatage := DateTimeToUnix(Now, False) * 10000000;
  
  // Intégrer l'horodatage dans les 6 premiers octets pour ordre séquentiel
  Result.D1 := LongWord(Horodatage shr 16);
  Result.D2 := Word(Horodatage);
  
  // Conserver les bits de version 4 et de variante
  Result.D3 := (Result.D3 and $0FFF) or $4000;
  Result.D4[0] := (Result.D4[0] and $3F) or $80;
end;

class function TGenerateurUUID.EchangerEndian(const AGUID: TGUID): TGUID;
begin
  Result := AGUID;
  // Échanger D1 (4 octets)
  Result.D1 := ((AGUID.D1 and $000000FF) shl 24) or
               ((AGUID.D1 and $0000FF00) shl 8) or
               ((AGUID.D1 and $00FF0000) shr 8) or
               ((AGUID.D1 and $FF000000) shr 24);
  // Échanger D2 (2 octets)
  Result.D2 := ((AGUID.D2 and $00FF) shl 8) or
               ((AGUID.D2 and $FF00) shr 8);
  // Échanger D3 (2 octets)
  Result.D3 := ((AGUID.D3 and $00FF) shl 8) or
               ((AGUID.D3 and $FF00) shr 8);
end;

class function TGenerateurUUID.NouveauGUIDBaseNom(const EspaceNomsGUID: TGUID;
  const Nom: string): TGUID;
var
  Hash: THashSHA1;
  OctetsHash: TBytes;
  OctetsEspaceNoms: TBytes;
  GUIDEchange: TGUID;
begin
  // Convertir en ordre réseau
  GUIDEchange := EchangerEndian(EspaceNomsGUID);
  
  // Préparer les données pour le hachage
  SetLength(OctetsEspaceNoms, 16);
  Move(GUIDEchange, OctetsEspaceNoms[0], 16);
  
  // Calculer le hash
  Hash := THashSHA1.Create;
  Hash.Update(OctetsEspaceNoms);
  Hash.Update(TEncoding.UTF8.GetBytes(Nom));
  OctetsHash := Hash.HashAsBytes;
  
  // Créer GUID à partir du hash
  Move(OctetsHash[0], Result, 16);
  
  // Reconvertir depuis l'ordre réseau
  Result := EchangerEndian(Result);
  
  // Définir version 5 et variante
  Result.D3 := (Result.D3 and $0FFF) or $5000;
  Result.D4[0] := (Result.D4[0] and $3F) or $80;
end;

class function TGenerateurUUID.NouveauGUIDBaseURL(const URL: string): TGUID;
begin
  Result := NouveauGUIDBaseNom(EspaceNomsURL, URL);
end;

class function TGenerateurUUID.NouveauGUIDBaseDNS(const DNS: string): TGUID;
begin
  Result := NouveauGUIDBaseNom(EspaceNomsDNS, DNS);
end;

class function TGenerateurUUID.EssayerParserGUID(const S: string; 
  out GUID: TGUID): Boolean;
begin
  try
    GUID := StringToGUID(S);
    Result := True;
  except
    Result := False;
  end;
end;

class function TGenerateurUUID.EstGUIDVide(const GUID: TGUID): Boolean;
const
  GUIDVide: TGUID = '{00000000-0000-0000-0000-000000000000}';
begin
  Result := CompareMem(@GUID, @GUIDVide, SizeOf(TGUID));
end;

class function TGenerateurUUID.ObtenirGUIDCompact(const GUID: TGUID): string;
begin
  Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x',
    [GUID.D1, GUID.D2, GUID.D3, GUID.D4[0], GUID.D4[1], GUID.D4[2], 
     GUID.D4[3], GUID.D4[4], GUID.D4[5], GUID.D4[6], GUID.D4[7]]);
end;

end.

Cette unité utilitaire fournit un ensemble complet de méthodes de génération d'UUID encapsulées dans une classe avec des méthodes de classe. Elle inclut la génération d'UUID aléatoire, les UUIDs séquentiels pour l'optimisation de base de données, et les UUIDs déterministes basés sur des noms suivant RFC 4122. L'unité gère la conversion d'endianness pour une génération correcte d'UUID Version 5 et fournit des méthodes d'aide pour l'analyse et le formatage. Vous pouvez facilement étendre cette unité avec des fonctionnalités supplémentaires comme la génération d'UUID Version 1 (basée sur le temps) ou Version 3 (basée sur MD5).

Analyse et Validation des GUIDs en Delphi

Lors du travail avec des GUIDs provenant de sources externes ou d'entrées utilisateur, une validation appropriée est cruciale. Voici comment analyser et valider les GUIDs dans les applications Delphi :

program ValiderGUIDs;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.RegularExpressions;

type
  TValidateurGUID = class
  private
    const
      // Modèle GUID conforme RFC 4122
      ModeleGUID = '^[{(]?[0-9A-Fa-f]{8}[-]?([0-9A-Fa-f]{4}[-]?){3}[0-9A-Fa-f]{12}[)}]?$';
      
  public
    class function EstGUIDValide(const Valeur: string): Boolean;
    class function EssayerParserGUID(const Valeur: string; out GUID: TGUID): Boolean;
    class function NormaliserGUID(const Valeur: string): string;
    class function ExtraireGUIDDeTexte(const Texte: string): TArray<string>;
  end;

class function TValidateurGUID.EstGUIDValide(const Valeur: string): Boolean;
var
  RegEx: TRegEx;
begin
  RegEx := TRegEx.Create(ModeleGUID);
  Result := RegEx.IsMatch(Valeur);
end;

class function TValidateurGUID.EssayerParserGUID(const Valeur: string; 
  out GUID: TGUID): Boolean;
var
  ValeurNormalisee: string;
begin
  Result := False;
  
  // D'abord vérifier avec regex
  if not EstGUIDValide(Valeur) then
    Exit;
    
  try
    // Normaliser la chaîne GUID
    ValeurNormalisee := NormaliserGUID(Valeur);
    
    // Essayer de convertir
    GUID := StringToGUID(ValeurNormalisee);
    Result := True;
  except
    // Format invalide
    Result := False;
  end;
end;

class function TValidateurGUID.NormaliserGUID(const Valeur: string): string;
var
  ValeurPropre: string;
begin
  // Supprimer tous les caractères non-hexadécimaux
  ValeurPropre := TRegEx.Replace(Valeur, '[^0-9A-Fa-f]', '');
  
  // Vérifier si nous avons exactement 32 chiffres hexadécimaux
  if Length(ValeurPropre) <> 32 then
    raise Exception.Create('Longueur GUID invalide');
    
  // Formater comme GUID standard
  Result := Format('{%s-%s-%s-%s-%s}',
    [Copy(ValeurPropre, 1, 8),
     Copy(ValeurPropre, 9, 4),
     Copy(ValeurPropre, 13, 4),
     Copy(ValeurPropre, 17, 4),
     Copy(ValeurPropre, 21, 12)]);
end;

class function TValidateurGUID.ExtraireGUIDDeTexte(const Texte: string): TArray<string>;
var
  RegEx: TRegEx;
  Correspondances: TMatchCollection;
  Correspondance: TMatch;
  i: Integer;
begin
  RegEx := TRegEx.Create(ModeleGUID);
  Correspondances := RegEx.Matches(Texte);
  
  SetLength(Result, Correspondances.Count);
  for i := 0 to Correspondances.Count - 1 do
    Result[i] := Correspondances[i].Value;
end;

// Tester le validateur
var
  ChainesTest: array[0..5] of string = (
    '{8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75}',  // Valide avec accolades
    '8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75',    // Valide sans accolades
    '8C2D56F08A1E4B6CA8D2AF3A99B11F75',        // Valide sans formatage
    '(8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75)',  // Valide avec parenthèses
    'pas-un-guid',                              // Invalide
    '8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F7'      // Invalide (trop court)
  );
  GUIDTest: TGUID;
  ChaineTest: string;
begin
  try
    WriteLn('Tests de Validation GUID:');
    WriteLn('='*50);
    
    for ChaineTest in ChainesTest do
    begin
      Write(Format('%-40s: ', [ChaineTest]));
      
      if TValidateurGUID.EssayerParserGUID(ChaineTest, GUIDTest) then
        WriteLn('Valide - Analysé comme ', GUIDToString(GUIDTest))
      else
        WriteLn('Invalide');
    end;
    
    WriteLn;
    WriteLn('Extraction de GUIDs du texte:');
    WriteLn('='*50);
    
    var TexteExemple := 'L''ID utilisateur est {8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75} ' +
      'et l''ID de session est 123e4567-e89b-12d3-a456-426614174000.';
    
    var GUIDsExtraits := TValidateurGUID.ExtraireGUIDDeTexte(TexteExemple);
    
    WriteLn('Texte: ', TexteExemple);
    WriteLn('GUIDs trouvés:');
    for ChaineTest in GUIDsExtraits do
      WriteLn('  - ', ChaineTest);
      
    ReadLn;
  except
    on E: Exception do
      WriteLn('Erreur: ', E.Message);
  end;
end.

Cet exemple de validation démontre une validation complète des GUID utilisant des expressions régulières. La classe TValidateurGUID peut valider les GUIDs dans divers formats (avec/sans accolades, tirets ou parenthèses), les normaliser vers un format standard et extraire les GUIDs de blocs de texte plus importants. Le modèle regex correspond à toutes les représentations courantes de GUID tandis que la fonction de normalisation assure un formatage cohérent. C'est particulièrement utile lors du traitement des entrées utilisateur ou des données provenant de systèmes externes.

Optimisation des Performances pour la Génération de GUID

Lors de la génération de GUIDs à grande échelle dans les applications Delphi, les performances deviennent critiques. Voici comment optimiser la génération de GUID et mesurer les performances :

program PerformanceGUID;

{$APPTYPE CONSOLE}

uses
  System.SysUtils, System.Diagnostics, System.Classes, 
  System.Generics.Collections, System.Threading;

type
  TCacheGUID = class
  private
    FCache: TThreadList<TGUID>;
    FTailleCache: Integer;
    FSeuilRemplissage: Integer;
    
    procedure RemplirCache;
  public
    constructor Create(ATailleCache: Integer = 1000);
    destructor Destroy; override;
    
    function ObtenirGUID: TGUID;
    property TailleCache: Integer read FTailleCache;
  end;

constructor TCacheGUID.Create(ATailleCache: Integer);
begin
  inherited Create;
  FTailleCache := ATailleCache;
  FSeuilRemplissage := ATailleCache div 4;
  FCache := TThreadList<TGUID>.Create;
  RemplirCache;
end;

destructor TCacheGUID.Destroy;
begin
  FCache.Free;
  inherited;
end;

procedure TCacheGUID.RemplirCache;
var
  Liste: TList<TGUID>;
  i: Integer;
  NouveauGUID: TGUID;
begin
  Liste := FCache.LockList;
  try
    // Remplir le cache à capacité
    while Liste.Count < FTailleCache do
    begin
      CreateGUID(NouveauGUID);
      Liste.Add(NouveauGUID);
    end;
  finally
    FCache.UnlockList;
  end;
end;

function TCacheGUID.ObtenirGUID: TGUID;
var
  Liste: TList<TGUID>;
begin
  Liste := FCache.LockList;
  try
    if Liste.Count = 0 then
      RemplirCache;
      
    Result := Liste.Last;
    Liste.Delete(Liste.Count - 1);
    
    // Remplir le cache de manière asynchrone si en dessous du seuil
    if Liste.Count < FSeuilRemplissage then
    begin
      TTask.Run(procedure
      begin
        RemplirCache;
      end);
    end;
  finally
    FCache.UnlockList;
  end;
end;

// Évaluer différentes approches
procedure EvaluerGenerationGUID(Nombre: Integer);
var
  Chronometre: TStopwatch;
  GUIDs: TArray<TGUID>;
  i: Integer;
  Cache: TCacheGUID;
  
  procedure EvaluerStandard;
  begin
    Chronometre := TStopwatch.StartNew;
    SetLength(GUIDs, Nombre);
    
    for i := 0 to Nombre - 1 do
      CreateGUID(GUIDs[i]);
      
    Chronometre.Stop;
    WriteLn(Format('CreateGUID standard: %d ms pour %d GUIDs (%.2f GUIDs/ms)',
      [Chronometre.ElapsedMilliseconds, Nombre, 
       Nombre / Max(1, Chronometre.ElapsedMilliseconds)]));
  end;
  
  procedure EvaluerAvecCache;
  begin
    Cache := TCacheGUID.Create(1000);
    try
      Chronometre := TStopwatch.StartNew;
      SetLength(GUIDs, Nombre);
      
      for i := 0 to Nombre - 1 do
        GUIDs[i] := Cache.ObtenirGUID;
        
      Chronometre.Stop;
      WriteLn(Format('Génération avec cache: %d ms pour %d GUIDs (%.2f GUIDs/ms)',
        [Chronometre.ElapsedMilliseconds, Nombre,
         Nombre / Max(1, Chronometre.ElapsedMilliseconds)]));
    finally
      Cache.Free;
    end;
  end;
  
  procedure EvaluerParallele;
  begin
    Chronometre := TStopwatch.StartNew;
    SetLength(GUIDs, Nombre);
    
    TParallel.For(0, Nombre - 1, 
      procedure(Index: Integer)
      begin
        CreateGUID(GUIDs[Index]);
      end);
      
    Chronometre.Stop;
    WriteLn(Format('Génération parallèle: %d ms pour %d GUIDs (%.2f GUIDs/ms)',
      [Chronometre.ElapsedMilliseconds, Nombre,
       Nombre / Max(1, Chronometre.ElapsedMilliseconds)]));
  end;
  
begin
  WriteLn(Format('Évaluation de la génération de %d GUIDs...', [Nombre]));
  WriteLn('='*60);
  
  EvaluerStandard;
  EvaluerAvecCache;
  EvaluerParallele;
  
  WriteLn;
  WriteLn('Utilisation mémoire:');
  WriteLn(Format('  Un seul GUID: %d octets', [SizeOf(TGUID)]));
  WriteLn(Format('  %d GUIDs: %.2f Mo', [Nombre, (Nombre * SizeOf(TGUID)) / (1024 * 1024)]));
end;

begin
  try
    // Tester avec différentes tailles
    EvaluerGenerationGUID(10000);
    WriteLn;
    EvaluerGenerationGUID(100000);
    WriteLn;
    EvaluerGenerationGUID(1000000);
    
    ReadLn;
  except
    on E: Exception do
      WriteLn('Erreur: ', E.Message);
  end;
end.

Cet exemple d'optimisation des performances démontre trois approches pour la génération de GUID en volume élevé : génération séquentielle standard, génération avec cache avec remplissage en arrière-plan, et génération parallèle utilisant TParallel. L'approche avec cache pré-génère des GUIDs et remplit le cache de manière asynchrone, réduisant la latence pour les applications nécessitant des GUIDs à la demande. La génération parallèle exploite plusieurs cœurs CPU pour les opérations en masse. Les évaluations aident à identifier l'approche la plus adaptée à votre cas d'utilisation spécifique. Notez que CreateGUID est thread-safe, rendant la génération parallèle sûre sans synchronisation supplémentaire.

Conclusion : Meilleures Pratiques pour la Génération d'UUID en Delphi

Lors de l'implémentation de la génération d'UUID dans les applications Delphi, choisissez la méthode appropriée en fonction de vos exigences. Pour les identifiants uniques à usage général, CreateGUID fournit une solution simple et multiplateforme. Pour les applications spécifiques à Windows nécessitant une compatibilité COM, les appels directs à l'API offrent plus de contrôle. Les GUIDs séquentiels optimisent les performances de base de données mais sacrifient une certaine aléatoireté. Les UUIDs déterministes permettent aux systèmes distribués de générer des identifiants cohérents de manière indépendante. Validez toujours les GUIDs externes et envisagez la mise en cache pour les scénarios haute performance.

Le développement Delphi moderne bénéficie du support robuste des GUID intégré dans la RTL, avec des capacités supplémentaires disponibles via les API Windows et les implémentations personnalisées. Que vous construisiez des applications de bureau avec VCL, des applications multiplateformes avec FireMonkey, ou des applications serveur, les techniques présentées dans ce guide fournissent une base solide pour l'implémentation d'UUID. N'oubliez pas de considérer la sécurité des threads, les implications de performance et les exigences de stockage lors de la conception de votre stratégie UUID. Pour les applications de base de données, évaluez si les GUIDs séquentiels offrent des avantages de performance significatifs pour votre cas d'utilisation spécifique.

Pour une génération rapide d'UUID sans écrire de code, vous pouvez toujours utiliser notre outil de génération d'UUID en ligne, qui fournit une génération instantanée d'UUID dans divers formats d'un simple clic.

© UUIDGenerator.co v1.0 All rights reserved (2025)