1. #1
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    3 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Sénégal

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 3 029
    Points : 10 996
    Points
    10 996
    Billets dans le blog
    4

    Par défaut Analyse d'un fichier PGN par les expressions régulières

    Bonjour ! Je vous propose une procédure qui analyse le contenu d'un fichier PGN, représentant un ensemble de parties d'échecs.
    La procédure est basée sur les expressions régulières.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
     
    unit PGN;
     
    interface
     
    procedure ParsePGNText(const aText: string);
     
    implementation
     
    uses
      RegExpr;
     
    procedure ParsePGNText(const aText: string);
    const
      TAG = '\[\w+\s+"[^"]+"\]';
      TAGS = '(' + TAG + '\s+)+';
      FULLMOVENUMBER = '\d+\.\s+';
      FULLMOVE = '([A-Za-z][\w-+#]+\s+){1,2}';
      MOVE = FULLMOVENUMBER + FULLMOVE;
      MOVES = '(' + MOVE + ')+';
      RESULT = '(1-0|0-1|1/2-1/2|\*)';
      GAME = TAGS + MOVES + RESULT;
     
    type
      TPattern = (ptGame, ptTag, ptMove, ptResult);
     
    var
      vExpr: array[TPattern] of TRegExpr;
      vPattern: TPattern;
      vText: string;
     
    begin
      vText := aText;
     
      vExpr[ptGame] := TRegExpr.Create(GAME);
      vExpr[ptTag] := TRegExpr.Create(TAG);
      vExpr[ptMove] := TRegExpr.Create(MOVE);
      vExpr[ptResult] := TRegExpr.Create(RESULT);
     
      try
        if vExpr[ptGame].Exec(vText) then
          repeat
            vText := vExpr[ptGame].Match[0];
     
            with vExpr[ptTag] do
              if Exec(vText) then
              repeat
                WriteLn('TAG ', Match[0]);
              until not ExecNext;
     
            with vExpr[ptMove] do
              if Exec(vText) then
              repeat
                WriteLn('MOVE ', ReplaceRegExpr('\s', Match[0], ' ', FALSE));
              until not ExecNext;
     
            with vExpr[ptResult] do
              if Exec(vText) then
                WriteLn('RESULT ', Match[0]);
     
          until not vExpr[ptGame].ExecNext;
      finally
        for vPattern in TPattern do
        vExpr[vPattern].Free;
      end;
    end;
     
    end.
    Qu'en pensez-vous ?
    Fichiers attachés Fichiers attachés

  2. #2
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    3 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Sénégal

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 3 029
    Points : 10 996
    Points
    10 996
    Billets dans le blog
    4

    Par défaut

    Voici une nouvelle version qui pousse plus loin l'analyse du contenu du fichier. Les groupes de caractères qui représentent les coups sont décomposés dans leurs différents éléments, par exemple :

    Code X : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [MOVE]36. Ra6+ Kc5 
      ELEM[1]R
      ELEM[2]
      ELEM[3]a6
      ELEM[4]
      ELEM[5]+
      ELEM[1]K
      ELEM[2]
      ELEM[3]c5

    Il y a aussi certaines subtilités du standard PGN que je n'avais pas prises en compte dans la version précédente.

    Pour le moment, la fonction ParsePGNText ne fait qu'écrire dans la console les sous-chaînes extraites. Au lieu de cela, il serait intéressant que la fonction renvoie un tableau contenant toutes les données. Je réfléchis à la forme que pourrait prendre ce tableau.
    Fichiers attachés Fichiers attachés

  3. #3
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    3 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Sénégal

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 3 029
    Points : 10 996
    Points
    10 996
    Billets dans le blog
    4

    Par défaut

    J'ai revu ma copie, après m'être aperçu que certaine subtilités de la "notation algébrique standard" m'avaient échappé.

    Cette fois j'ai mis les petits plats dans les grands :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    const
      COLUMN = '[a-h]';
      ROW = '[1-8]';
      KINGSIDE_CASTLING = 'O-O';
      QUEENSIDE_CASTLING = 'O-O-O';
     
      PIECE = '[PNBRQK]';
      DISAMBIGUATION = COLUMN + '|' + ROW + '|' + COLUMN + ROW;
      CAPTURE = 'x';
      SQUARE_OR_CASTLING = COLUMN + ROW + '|' + QUEENSIDE_CASTLING + '|' + KINGSIDE_CASTLING;
      PROMOTION = '=[NBRQ]';
      CHECK_OR_CHECKMATE = '[+#]';
      SAN_MOVE =
        '(' + PIECE + ')?' +
        '(' + DISAMBIGUATION + ')?' +
        '(' + CAPTURE + ')?' +
        '(' + SQUARE_OR_CASTLING + ')' +
        '(' + PROMOTION + ')?' +
        '(' + CHECK_OR_CHECKMATE + ')?';
     
      FULL_MOVE = '(\d+)\.\s+(' + SAN_MOVE + '\s+){1,2}';
      MOVES_SECTION = '(' + FULL_MOVE + ')+';
     
      TAG_PAIR = '\[(\w+)\s+"([^"]+)"\]';
      TAG_PAIRS_SECTION  = '(' + TAG_PAIR + '\s+)+';
     
      GAME_TERMINATION = '(1-0|0-1|1/2-1/2|\*)';
     
      GAME = TAG_PAIRS_SECTION + MOVES_SECTION + GAME_TERMINATION;
    Maintenant je cherche sous quelle forme renvoyer ces données, sachant qu'il peut y avoir zéro ou une ou plusieurs parties dans un fichier, que chaque partie a une liste de coups de longueur variable...

    Le seul point pour lequel j'ai une idée précise, c'est pour les paires nom-valeurs : je pense utiliser un TStringList.
    Fichiers attachés Fichiers attachés

  4. #4
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    3 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Sénégal

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 3 029
    Points : 10 996
    Points
    10 996
    Billets dans le blog
    4

    Par défaut

    J'ai retravaillé l'unité PGN. Les données, au lieu d'être simplement écrites dans la console, sont renvoyées au programme principal sous la forme d'un tableau.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
     
    unit PGN;
     
    interface
     
    uses
      SysUtils, Classes;
     
    type
      TGroupIndex = 1..6;
     
      TChessGame = class
        tags: TStringList;
        moves: array of array[TGroupIndex] of string;
        termination: string;
        constructor Create; overload;
        destructor Destroy; override;
      end;
     
      TChessGameArray = array of TChessGame;
     
    function ParsePGNText(aText: string): TChessGameArray;
     
    implementation
     
    uses
      RegExpr;
     
    constructor TChessGame.Create;
    begin
      inherited;
      tags := TStringList.Create;
    end;
     
    destructor TChessGame.Destroy;
    begin
      tags.Free;
      SetLength(moves, 0);
      inherited;
    end;
     
    function ParsePGNText(aText: string): TChessGameArray;
    const
      COLUMN = '[a-h]';
      ROW = '[1-8]';
      KINGSIDE_CASTLING = 'O-O';
      QUEENSIDE_CASTLING = 'O-O-O';
      PIECE = '[PNBRQK]';
      DISAMBIGUATION = COLUMN + '|' + ROW + '|' + COLUMN + ROW;
      CAPTURE = 'x';
      SQUARE_OR_CASTLING = COLUMN + ROW + '|' + QUEENSIDE_CASTLING + '|' + KINGSIDE_CASTLING;
      PROMOTION = '=[NBRQ]';
      CHECK_OR_CHECKMATE = '[+#]';
      SAN_MOVE =
        '(' + PIECE + ')?' +
        '(' + DISAMBIGUATION + ')?' +
        '(' + CAPTURE + ')?' +
        '(' + SQUARE_OR_CASTLING + ')' +
        '(' + PROMOTION + ')?' +
        '(' + CHECK_OR_CHECKMATE + ')?';
      FULL_MOVE = '(\d+)\.\s+(' + SAN_MOVE + '\s+){1,2}';
      MOVES_SECTION = '(' + FULL_MOVE + ')+';
      TAG_PAIR = '\[(\w+)\s+"([^"]+)"\]';
      TAG_PAIRS_SECTION  = '(' + TAG_PAIR + '\s+)+';
      GAME_TERMINATION = '(1-0|0-1|1/2-1/2|\*)';
      GAME = TAG_PAIRS_SECTION + MOVES_SECTION + GAME_TERMINATION;
    type
      TSearch = (searchGame, searchTagPair, searchFullMove, searchTermination, searchMove);
    const
      PATTERNS: array[TSearch] of string = (GAME, TAG_PAIR, FULL_MOVE, GAME_TERMINATION, SAN_MOVE);
    var
      vExpr: array[TSearch] of TRegExpr;
      vSearch: TSearch;
      vText: string;
    procedure ParseFullMove(const aFullMove: string);
    var
      vIndex: integer;
    begin
      with vExpr[searchMove] do
        if Exec(aFullMove) then
          with result[High(result)] do
          repeat
            SetLength(moves, Succ(Length(moves)));
            for vIndex := 1 to SubExprMatchCount do
              moves[High(moves)][vIndex] := Match[vIndex];
          until not ExecNext;
    end;
    begin
      for vSearch in TSearch do
        vExpr[vSearch] := TRegExpr.Create(PATTERNS[vSearch]);
     
      SetLength(result, 0);
     
      try
        if vExpr[searchGame].Exec(aText) then
          repeat
            aText := vExpr[searchGame].Match[0];
     
            SetLength(result, Succ(Length(result)));
            result[High(result)] := TChessGame.Create;
            result[High(result)].tags.Sorted := TRUE;
     
            with vExpr[searchTagPair] do
              if Exec(aText) then
              repeat
                result[High(result)].tags.Append(Format('%s=%s', [Match[1], Match[2]]));
              until not ExecNext;
     
            with vExpr[searchFullMove] do
              if Exec(aText) then
              repeat
                ParseFullMove(Match[0]);
              until not ExecNext;
     
            with vExpr[searchTermination] do
              if Exec(aText) then
                result[High(result)].termination := Match[0];
     
          until not vExpr[searchGame].ExecNext;
      finally
        for vSearch in TSearch do
          vExpr[vSearch].Free;
      end;
    end;
     
    end.
    Les observations sur ce code sont les bienvenues.

    Que pensez-vous de la forme sous laquelle les données sont renvoyées ? L'auriez-vous déclarée plus ou moins de la même façon ? Je parle du type TChessGameArray.
    Fichiers attachés Fichiers attachés

  5. #5
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    avril 2002
    Messages
    2 417
    Détails du profil
    Informations personnelles :
    Âge : 32

    Informations forums :
    Inscription : avril 2002
    Messages : 2 417
    Points : 4 101
    Points
    4 101

    Par défaut

    Bonjour Roland,

    Sans avoir lu en détail le programme... pour ce genre de choses, je passe plutôt par une TList, qui est beaucoup plus flexible qu'un array.
    Vu qu'on est à fond dans le monde objet mode Delphi (tu utilises des classes, plutôt que des objects ou des records), on peut estimer que pour la cohérence philosophique, la classe TList soit plus adaptée
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  6. #6
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    3 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Sénégal

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 3 029
    Points : 10 996
    Points
    10 996
    Billets dans le blog
    4

    Par défaut

    @M.Dlb

    Bonjour ! Merci pour ta réponse et ta suggestion, que j'ai suivie.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    type
      TGroupIndex = 1..6;
      PGroups = ^TGroups;
      TGroups = array[TGroupIndex] of string;
     
      TChessGame = class
        tags: TStringList;
        moves: TList;
        termination: string;
        constructor Create; overload;
        destructor Destroy; override;
      end;
     
    function ParsePGNText(aText: string): TList;
    La seule qui m'a mis un peu mal à l'aise avec la classe TList c'est qu'il faut savoir, pour ne pas dire deviner, ce qu'elle contient. Ou alors y a-t-il un moyen de le contrôler dans le code ?
    Fichiers attachés Fichiers attachés

  7. #7
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    avril 2002
    Messages
    2 417
    Détails du profil
    Informations personnelles :
    Âge : 32

    Informations forums :
    Inscription : avril 2002
    Messages : 2 417
    Points : 4 101
    Points
    4 101

    Par défaut

    Citation Envoyé par Roland Chastain Voir le message
    La seule qui m'a mis un peu mal à l'aise avec la classe TList c'est qu'il faut savoir, pour ne pas dire deviner, ce qu'elle contient. Ou alors y a-t-il un moyen de le contrôler dans le code ?
    Que veux-tu dire par là ?
    TList représente une liste de pointeurs, qui pointe vers des objets. J'évite toujours de mélanger les types d'objets dans mes listes : une instance TList pour chaque type d'objet du programme. Ainsi, lors du transtypage pour l'accès aux infos par le biais de la propriété Items, aucune question à se poser.
    Pour la gestion, la propriété Count donne le nombre d'éléments dans la liste, puis après un fait joujou avec Add, Delete, Pack (qui est bien utile pour faire le ménage), etc...
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  8. #8
    Membre expert
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mai 2002
    Messages
    2 488
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : mai 2002
    Messages : 2 488
    Points : 3 944
    Points
    3 944

    Par défaut

    salut

    utilise les collections c'est pratique aussi
    Nous souhaitons la vérité et nous trouvons qu'incertitude. [...]
    Nous sommes incapables de ne pas souhaiter la vérité et le bonheur, et sommes incapables ni de certitude ni de bonheur.
    Blaise Pascal
    PS : n'oubliez pas le tag

  9. #9
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    décembre 2011
    Messages
    3 029
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Sénégal

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : décembre 2011
    Messages : 3 029
    Points : 10 996
    Points
    10 996
    Billets dans le blog
    4

    Par défaut

    Citation Envoyé par anapurna Voir le message
    salut

    utilise les collections c'est pratique aussi
    Bonjour ! Merci pour ta réponse. J'avais fait moi-même un tutoriel sur les collections en Pascal.

    Les collections en Pascal

    Est-ce que c'est à ce genre de code que tu penses ou à autre chose ?

  10. #10
    Membre expert
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mai 2002
    Messages
    2 488
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : mai 2002
    Messages : 2 488
    Points : 3 944
    Points
    3 944

    Par défaut

    salut

    pas tout a fait j'etais plus sur un truc du genre


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    type
      TMove = class (TCollectionItem)
      private
        Fdata : ...;
      published
        property Data: ... read Fdata write Fdata;
      end;    
     
     TMoves = class (TCollection)
      private
        function GetItem(Index: Integer): TMove;
      public
        function Add: TMove;
        property Item[Index: Integer]: TMove read GetItem;
      end;  
     
    .....
     
      function TMoves.Add: TMove;
      begin
        result := inherited Add as TMove;
      end;
     
      function TMoves.GetItem(Index: Integer): TMove;
      begin
        result := inherited Items[Index] as TMove;
      end;
    Nous souhaitons la vérité et nous trouvons qu'incertitude. [...]
    Nous sommes incapables de ne pas souhaiter la vérité et le bonheur, et sommes incapables ni de certitude ni de bonheur.
    Blaise Pascal
    PS : n'oubliez pas le tag

Discussions similaires

  1. Réponses: 2
    Dernier message: 18/10/2014, 23h54
  2. Validation d'une chaîne FEN par les expressions régulières
    Par Roland Chastain dans le forum Delphi
    Réponses: 10
    Dernier message: 01/10/2014, 21h27
  3. Remplacer les lignes d'un fichier excel par les lignes d'un autre fichier
    Par nandy.c dans le forum Développement de jobs
    Réponses: 1
    Dernier message: 17/05/2013, 15h35
  4. Réponses: 13
    Dernier message: 30/11/2011, 10h04
  5. Réponses: 4
    Dernier message: 22/11/2008, 09h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo