Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre régulier
    Lecture fichier ligne par ligne, StringList, Stream : Questions de debutant
    Bonjour

    Je souhaite lire un fichier .csv de grande taille (cotations boursieres tick/tick, plusieurs ticks/seconde, plus de 10 ans d historique...)

    Actuellement il ne fait que 300 mo et la lecture dans un TStringList est possible et la plus rapide. Toutefois je ne souhaite pas mettre en mémoire le fichier mais seulement le lire ligne par ligne, formater la ligne et la mettre dans une base de données en mémoire d'où mon intérêt de ne pas avoir en plus l intégralité du fichier lu en mémoire .

    J'ai donc cherché et trouvé un code qui est plus lent (mais le plus rapide que j ai trouvé) que la Tstringlist mais lit ligne par ligne ...

    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
    procedure TFormReaderWriter.btnReadBufferedClick(Sender: TObject);
    var
      fStr: TBufferedFileStream;
      Total, I: Integer;
      sw: TStopwatch;
      ch: Char;
    begin
      sw := TStopwatch.StartNew;
      fStr := TBufferedFileStream.Create('test.txt', fmOpenRead);
      try
        Total := 0;
        while fStr.Read (ch, 1) = 1 do begin
          if ch = #13 then
            Inc(Total);
        end;
        Memo1.Lines.Add ('Lines: ' + Total.ToString);
      finally
        fStr.Free;
      end;
      sw.Stop;
      Memo1.Lines.Add ('msec: ' +   sw.ElapsedMilliseconds.ToString);
    end;


    Il lit caractère par caractère et considère la ligne lue quand il trouve un #10.
    Considérant que mes lignes ont une longueur constante (50 caractères 'visibles'), y a t il moyen de la lire dans son intégralité en une seule fois ?

    Je débute et suis ouvert à tout conseil. Si ma direction n est pas la bonne et qu une autre solution est envisageable je suis preneur.

    Pour mon test j ai lu un fichier de près de 313Mo, 6,4 millions de lignes :
    Tstringlist : 1784 ms
    TBufferedFileStream : 2945 ms
    Readln : 10703 ms

    Merci pour vos conseils

  2. #2
    Rédacteur/Modérateur

    Citation Envoyé par MoiStéphane Voir le message
    Bonjour
    Considérant que mes lignes ont une longueur constante (50 caractères 'visibles'), y a t il moyen de la lire dans son intégralité en une seule fois ?
    Bonjour ! Peut-être BlockRead ?

  3. #3
    Membre actif
    Bonjour,
    Si tes données sont de format connu, tu peux aussi lire des Records.
    Tu trouveras ces infos dans la Faq:
    https://fbeaulieu.developpez.com/guide/?page=page_13
    Bob Code
    Windows 7 / Delphi Tokyo
    "Les choses ne changent pas. Change ta façon de les voir, cela suffit" Lao Tseu

  4. #4
    Membre émérite
    Via TFileStream

  5. #5
    Rédacteur/Modérateur

    FireDac est capable de charger un fichier csv directement dans une table en mémoire.

    TFDBatchMoveTextReader + TFDBatchMove + TFDBatchMoveDataSetWriter + TFDMemTable

  6. #6
    Membre régulier
    merci pour toutes ces suggestions.

    mon pb réside dans le fait que le fichier doit etre manipulé avant l insertion dans la base de données. Il contient des dates et heures qui doivent etre encapsulées avec des guillemets...

    je vais essayer tout ça et reviendrais pour faire un bilan des perfs de chaque mode d'insertion.

  7. #7
    Rédacteur/Modérateur

    Citation Envoyé par MoiStéphane Voir le message
    le fichier doit etre manipulé avant l insertion dans la base de données. Il contient des dates et heures qui doivent etre encapsulées avec des guillemets...
    Oui mais là le but était sans doute d'écrire un script SQL (à base d'INSERT) mais avec TFDBatchMove, la base de données est créée, plus besoin de script.

  8. #8
    Membre régulier
    Citation Envoyé par Andnotor Voir le message
    Oui mais là le but était sans doute d'écrire un script SQL (à base d'INSERT) mais avec TFDBatchMove, la base de données est créée, plus besoin de script.
    Merci de t intéresser à mon pb mais je n ai pas compris ce que tu expliques.
    La base est effectivement crée.
    Dans ma boucle chaque fois que je lis une ligne je la formate et écrit une requête insert dans sql.add();
    quand j ai lu 10 lignes j exécute la suite de requêtes.

    mon objectif était d optimiser le temps de lecture du fichier pour ne pas le mettre entièrement en mémoire de peur d avoir un message d erreur 'mémoire insuffisante'

  9. #9
    Expert éminent sénior
    Si tu as toujours 50 + CR + LF, utilise une lecture par un packed record + array of AnsiChar
    et le nombre de ligne c'est donc GetFizeSize / 52

    Si tu n'es pas sur de la structure alors reste en texte, voici un vieux code TTextFileReader qui lit le fichier pour en créer une sorte d'index des lignes, cela permet de se déplacer dans le fichier si l'on doit le lire plusieurs fois ou que l'on doit revenir en arrière pour certaines situations, tester avec des fichiers de 10Go - pour genre 500M de lignes à une époque où les PC avait 512Mo de RAM complètement occupé par Delphi, Word et FireFox ... il ne restait presque plus rien pour son propre programme.


    la classe fonctionne en deux modes : lecture unidirectionnel bufferisé et lecture via le fichier d'index
    Vu ton code, il faut la version XE2 ci-dessous

    ReadLn, il y a une version bufferisée qui évite la lecture caractère un par un qui est pourrie

    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
    procedure TFormReaderWriter.btnReadBufferedClick(Sender: TObject);
    var
      fStr: TSLTTextFileReader ;
      sw: TStopwatch;
    begin
      sw := TStopwatch.StartNew;
      fStr := TSLTTextFileReader.Create('test.txt', TEncoding.ANSI);
      try
        while not fStr.OEF do
          fStr.ReadNextLine(); // nouveau nom de ReadString();
     
        Memo1.Lines.Add ('Lines: ' + fStr.Count.ToString);
      finally
        fStr.Free;
      end;
      sw.Stop;
      Memo1.Lines.Add ('msec: ' +   sw.ElapsedMilliseconds.ToString);
    end;


    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
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    716
    717
    718
    719
    720
    721
    722
    723
    724
    725
    726
    727
    728
    729
    730
    731
    732
    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    //------------------------------------------------------------------------------
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Version alternative publiée sur "www.developpez.net"                       -
     *  Post : "[D7][StringGrid] Très gros fichiers à parser (comment faire un streaming?)"
     *  Post Number : 2292692                                                      -
     *  Post URL = "http://www.developpez.net/forums/d374632/environnements-developpement/delphi/composants-vcl/d7-stringgrid-tres-gros-fichiers-parser-faire-streaming/#post2292692"
     *                                                                             -
     *  Version alternative publiée sur "www.developpez.net"                       -
     *  Post : "Fichier CSV, Compteur de lignes"                                   -
     *  Post Number : 2419880                                                      -
     *  Post URL = "http://www.developpez.net/forums/d394452/environnements-developpement/delphi/langage/fichier-csv-compteur-lignes/#post2419880"
     *                                                                             -
     *  Copyright "SLT Solutions", (©2006)                                         -
     *  contributeur : ShaiLeTroll (2007) - Passage en Classe d'un code procédural -
     *  contributeur : ShaiLeTroll (2011) - Préparation au passage Ansi vers Unicode sous C++Builder XE2 (code en Delphi XE2 mais utilisé par projet C++Builder XE2)
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *                                                                             -
     * ShaiLeTroll                                                                 -
     *                                                                             -
     * Ce logiciel est un programme informatique servant à aider les développeurs  -
     * Delphi avec une bibliothèque polyvalente, adaptable et fragmentable.        -
     *                                                                             -
     * Ce logiciel est régi par la licence CeCILL-C soumise au droit français et   -
     * respectant les principes de diffusion des logiciels libres. Vous pouvez     -
     * utiliser, modifier et/ou redistribuer ce programme sous les conditions      -
     * de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA    -
     * sur le site "http://www.cecill.info".                                       -
     *                                                                             -
     * En contrepartie de l'accessibilité au code source et des droits de copie,   -
     * de modification et de redistribution accordés par cette licence, il n'est   -
     * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,   -
     * seule une responsabilité restreinte pèse sur l'auteur du programme,  le     -
     * titulaire des droits patrimoniaux et les concédants successifs.             -
     *                                                                             -
     * A cet égard  l'attention de l'utilisateur est attirée sur les risques       -
     * associés au chargement,  à l'utilisation,  à la modification et/ou au       -
     * développement et à la reproduction du logiciel par l'utilisateur étant      -
     * donné sa spécificité de logiciel libre, qui peut le rendre complexe à       -
     * manipuler et qui le réserve donc à des développeurs et des professionnels   -
     * avertis possédant  des  connaissances  informatiques approfondies.  Les     -
     * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du      -
     * logiciel à leurs besoins dans des conditions permettant d'assurer la        -
     * sécurité de leurs systèmes et ou de leurs données et, plus généralement,    -
     * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.          -
     *                                                                             -
     * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez      -
     * pris connaissance de la licence CeCILL-C, et que vous en avez accepté les   -
     * termes.                                                                     -
     *                                                                             -
     *----------------------------------------------------------------------------*)
    unit SLT.Common.TextFileReader;
     
    interface
     
    uses System.SysUtils, System.Classes;
     
    type
      { Forward class declarations }
      TSLTTextFileReader = class;
      ISLTTextStreamReader = interface;
      TSLTTextStreamReaderAnsi = class;
     
      PTextFileReaderIndex = ^TSLTTextFileReaderIndex;
      TSLTTextFileReaderIndex = packed record
        OffSet: Int64;
        Length: Integer; // une Chaine Delphi ne peut pas dépasser cette taille
      end;
     
      TSLTTextFileReaderBuildIndexProgress = procedure(Sender: TObject; const Position, Size: Int64; var Aborting: Boolean) of object;
     
      /// <summary>Erreur de lecture du fichier d'index associé à un fichier texte</summary>
      ETextFileReaderErrorIndex = class(Exception);
      /// <summary>Erreur de lecture d'un fichier texte</summary>
      ETextFileReaderError = class(Exception);
     
      /// <summary>Classe permettant de lire et d'indexer un fichier texte</summary>
      TSLTTextFileReader = class(TObject)
      private
        // Membres privés
        FTextStream: TStream;
        FOwnsTextStream: Boolean;
        FFileName: TFileName;
        FIndexName: TFileName;
        FWorkingSuffix: string;
        FWorkingPath : TFileName;
        FWorkingPathIsTemp : Boolean;
        FIndexFile: TFileStream; // un File of aurait été plus pratique mais limité à 2Go, donc environ 178 millions de lignes, hors un fichier texte de 4Go, peut en contenir bien plus ...
        FIndexed: Boolean;
        FAutoIndexed: Boolean;
        FIndexCount: Int64;
        FIndexRecSize: Byte;
        FStreamReader: ISLTTextStreamReader;
        FUseCROnly: Boolean;
        FOnBuildIndexProgress: TSLTTextFileReaderBuildIndexProgress;
        FDeleteIndexOnFree: Boolean;
        FAllowUseExistingIndexFile: Boolean;
     
        // Méthodes privés
        function GetIndexed: Boolean;
        procedure SetIndexed(const Value: Boolean);
        function GetCount: Int64;
        function GetEOF: Boolean;
        function ReadLineFromIndexRec(const IndexRec: TSLTTextFileReaderIndex): string;
        function ReadIndex(const Index: Int64): TSLTTextFileReaderIndex;
        function ReadLineRaw(const Index: Int64; const OffSet: Int64 = 0): string;
        procedure BuildLinesIndexes();
        procedure BuildIndexProgressEventHandler(Sender: TObject; const Position, Size: Int64; var Aborting: Boolean);
        function GetIndexFileName(): TFileName;
        function GetLinearPosition(): Int64;
      public
        // Constructeurs
        constructor Create(const AFileName: string; AEncoding: TEncoding; AAutoIndexed: Boolean = True; AAllowUseExistingIndexFile: Boolean = False); overload;
        constructor Create(ATextStream: TStream; AEncoding: TEncoding; AAutoIndexed: Boolean = True; AAllowUseExistingIndexFile: Boolean = False); overload;
        destructor Destroy; override;
     
        // Méthodes
        class function BuildLinesIndexesFor(const AFileName: string; AEncoding: TEncoding): TFileName;
     
        // Méthodes
        function ReadLine(const Index: Int64): string;
        procedure ReadLines(const IndexBegin, IndexEnd: Int64; Lines: TStrings; DoClear: Boolean = True);
        function ReadNextLine(): string;
        procedure ReadNextLines(const ACount: Word; Lines: TStrings; DoClear: Boolean = True);  // Il semble raisonnable de limiter la lecture par TStrings à 65535
        procedure DeleteLine(const Index: Int64); // Opération Lente
        procedure DeleteLines(const IndexBegin, IndexEnd: Int64); // Opération Lente
     
        // Propriétés
        property Count: Int64 read GetCount;
        property EOF: Boolean read GetEOF;
        property TextStream: TStream read FTextStream;
        property OwnsTextStream: Boolean read FOwnsTextStream write FOwnsTextStream;
        property FileName: TFileName read FFileName;
        property IndexName: TFileName read FIndexName;
        property WorkingSuffix: string read FWorkingSuffix;
        property WorkingPath: TFileName read FWorkingPath;
        property Indexed: Boolean read GetIndexed write SetIndexed; // pour mettre la valeur à True, il faut que le fichier existe, ou alors que AutoIndexed soit à true
        property AutoIndexed: Boolean read FAutoIndexed write FAutoIndexed; // Si l'on change la propriété Indexed, cela génère le fichier automatiquement
        property Lines[const Index: Int64]: string read ReadLine;
        property LinearPosition: Int64 read GetLinearPosition;
        property UseCROnly: Boolean read FUseCROnly write FUseCROnly;
        property OnBuildIndexProgress: TSLTTextFileReaderBuildIndexProgress read FOnBuildIndexProgress write FOnBuildIndexProgress;
        property DeleteIndexOnFree: Boolean read FDeleteIndexOnFree write FDeleteIndexOnFree;
        property AllowUseExistingIndexFile: Boolean read FAllowUseExistingIndexFile write FAllowUseExistingIndexFile; // Par prudence, un fichier index doit être créé pour garantir son adéquation avec le fichier lu
      end;
     
      ISLTTextStreamReader = interface
        ['{2E0ABC26-A69D-46C5-A8DF-FA7D544C972E}']
     
        // Méthodes
        function ReadLineFromIndexRec(const IndexRec: TSLTTextFileReaderIndex): string;
        function ReadLineRaw(AIndexFile: TFileStream; AUseCROnly: Boolean; const Index: Int64; const OffSet: Int64 = 0): string;
        procedure BuildLinesIndexes(AIndexFile: TFileStream; AUseCROnly: Boolean);
     
        // Accesseurs
        function GetTextStream(): TStream;
        procedure SetTextStream(const Value: TStream);
        function GetTextSize(): Int64;
        function GetLinearPosition(): Int64;
        procedure SetLinearPosition(const Value: Int64);
        function GetOnBuildIndexProgress(): TSLTTextFileReaderBuildIndexProgress;
        procedure SetOnBuildIndexProgress(const Value: TSLTTextFileReaderBuildIndexProgress);
     
        // Propriétés
        property TextStream: TStream read GetTextStream write SetTextStream;
        property TextSize: Int64 read GetTextSize;
        property LinearPosition: Int64 read GetLinearPosition write SetLinearPosition;
        property OnBuildIndexProgress: TSLTTextFileReaderBuildIndexProgress read GetOnBuildIndexProgress write SetOnBuildIndexProgress;
      end;
     
      TSLTTextStreamReaderAnsi = class(TInterfacedObject, ISLTTextStreamReader)
      private
        // Membres privés
        FTextStream: TStream;
        FTextSize: Int64;
        FLinearPosition: Int64;
        FOnBuildIndexProgress: TSLTTextFileReaderBuildIndexProgress;
     
        // Méthodes privés
        function DoBuildIndexProgress(const Position, Size: Int64): Boolean;
      public
        // Méthodes - Implémentation de ISLTTextStreamReader
        function ReadLineFromIndexRec(const IndexRec: TSLTTextFileReaderIndex): string;
        function ReadLineRaw(AIndexFile: TFileStream; AUseCROnly: Boolean; const Index: Int64; const OffSet: Int64 = 0): string;
        procedure BuildLinesIndexes(AIndexFile: TFileStream; AUseCROnly: Boolean);
     
        // Accesseurs - Implémentation de ISLTTextStreamReader
        function GetTextStream(): TStream;
        procedure SetTextStream(const Value: TStream);
        function GetTextSize(): Int64;
        function GetLinearPosition(): Int64;
        procedure SetLinearPosition(const Value: Int64);
        function GetOnBuildIndexProgress(): TSLTTextFileReaderBuildIndexProgress;
        procedure SetOnBuildIndexProgress(const Value: TSLTTextFileReaderBuildIndexProgress);
      end;
     
     
    implementation
     
    uses
    {$IFDEF MSWINDOWS}
      Winapi.Windows,
    {$ELSE MSWINDOWS}
    {$MESSAGE ERROR 'Implémentation uniquement Windows pour TSLTTextFileReader'}
    {$ENDIF MSWINDOWS}
      System.IOUtils,
      SLT.Common.FileUtilsEx;
     
    resourcestring
      SEncodingNotSupported = 'Encodage non géré : %s';
      SIndexNotExist = 'Le Fichier d''Index n''existe pas !';
      STooMuchRead = 'Le Nombre Maximal de la Lecture par lot doit inférieur ou égale à %d au lieu de %d';
      STooMuchDelete = 'Le Nombre Maximal de la Suppression par lot doit être inférieur ou égale à %d au lieu de %d';
      SNoIndexedDelete = 'La Suppression n''est possible qu''avec un Fichier Indexé';
     
    { TSLTTextFileReader }
     
    { TSLTTextFileReader - Constructeurs }
     
    //------------------------------------------------------------------------------
    constructor TSLTTextFileReader.Create(const AFileName: string; AEncoding: TEncoding; AAutoIndexed: Boolean = True; AAllowUseExistingIndexFile: Boolean = False);
    begin
      FFileName := AFileName;
     
      FTextStream := TFileStream.Create(FileName, fmOpenRead, fmShareDenyWrite);
      FOwnsTextStream := True;
     
      Create(FTextStream, AEncoding, AAutoIndexed, AAllowUseExistingIndexFile);
    end;
     
    //------------------------------------------------------------------------------
    constructor TSLTTextFileReader.Create(ATextStream: TStream; AEncoding: TEncoding; AAutoIndexed: Boolean = True; AAllowUseExistingIndexFile: Boolean = False);
    begin
      inherited Create();
     
      FIndexCount := -1;
      FIndexRecSize := SizeOf(TSLTTextFileReaderIndex);
     
      if AEncoding = TEncoding.ANSI then
        FStreamReader := TSLTTextStreamReaderAnsi.Create()
      else
        raise ETextFileReaderError.CreateResFmt(@SEncodingNotSupported, [AEncoding.EncodingName]);
     
      FTextStream := ATextStream;
      FIndexFile := nil;
      FStreamReader.TextStream := FTextStream;
      FStreamReader.LinearPosition := 0;
      FStreamReader.OnBuildIndexProgress := BuildIndexProgressEventHandler;
     
      Indexed := False;
      AutoIndexed := AAutoIndexed;
      AllowUseExistingIndexFile := AAllowUseExistingIndexFile;
    end;
     
    //------------------------------------------------------------------------------
    destructor TSLTTextFileReader.Destroy();
    var
      Dir: TFileName;
    begin
      FreeAndNil(FIndexFile);
     
      if Assigned(FStreamReader) then
        FStreamReader.TextStream := nil;
     
      if FOwnsTextStream and Assigned(FTextStream) then
        FreeAndNil(FTextStream);
     
      if FDeleteIndexOnFree then
      begin
        // Nettoyage du fichier d'indexe
        if System.SysUtils.FileExists(FIndexName) then
          System.SysUtils.DeleteFile(FIndexName);
     
        // Nettoyage du dossier temporaire si celui est vide
        if FWorkingPathIsTemp then
        begin
          Dir := ExcludeTrailingPathDelimiter(FWorkingPath);
          RemoveDir(Dir);
          RemoveDir(ExtractFileDir(Dir));
        end;
      end;
     
      inherited;
    end;
     
    { TSLTTextFileReader - Méthodes Publiques }
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.BuildIndexProgressEventHandler(Sender: TObject; const Position, Size: Int64; var Aborting: Boolean);
    begin
      if Assigned(FOnBuildIndexProgress) then
        FOnBuildIndexProgress(Sender, Position, Size, Aborting);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.BuildLinesIndexes();
    begin
      FIndexFile := TFileStream.Create(GetIndexFileName(), fmCreate, fmShareExclusive);
      try
        FStreamReader.BuildLinesIndexes(FIndexFile, FUseCROnly);
      finally
        FIndexFile.Free();
        FIndexFile := nil;
      end;
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTTextFileReader.BuildLinesIndexesFor(const AFileName: string; AEncoding: TEncoding): TFileName;
    begin
      with TSLTTextFileReader.Create(AFileName, AEncoding, True, False) do
      try
        DeleteIndexOnFree := False;
        BuildLinesIndexes();
        Result := FIndexName;
      finally
        Free();
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.ReadLine(const Index: Int64): string;
    begin
      if FIndexed then
        Result := ReadLineFromIndexRec(ReadIndex(Index))
      else
        Result := ReadLineRaw(Index);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.ReadLines(const IndexBegin, IndexEnd: Int64; Lines: TStrings; DoClear: Boolean = True);
    var
      Index: Word; // le For Delphi 6 ne gère pas le Int64, et il ne serait pas raisonnable de lire autant
      IndexCount: Int64;
      IndexMax: Int64;
    begin
      if Assigned(Lines) then
      begin
        if DoClear then
          Lines.Clear();
     
        if IndexEnd < Count then
          IndexMax := IndexEnd
        else
          IndexMax := Count - 1;
     
        IndexCount := IndexMax - IndexBegin;
        if (0 <= IndexCount) and (IndexCount <= High(Word)) then
        begin
          Lines.Capacity := Lines.Count + IndexCount;
          for Index := 0 to IndexMax - IndexBegin do
            Lines.Add(ReadLine(IndexBegin + Index));
        end else
        begin
          raise ETextFileReaderError.CreateResFmt(@STooMuchRead, [High(Word), IndexCount]);
        end;
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.ReadNextLine: string;
    begin
      // Lecture d'une ligne depuis la position en cours
      Result := ReadLineRaw(0, FStreamReader.LinearPosition);
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.ReadNextLines(const ACount: Word; Lines: TStrings; DoClear: Boolean = True);
    var
      Index: Word; // le For Delphi 6 ne gère pas le Int64, et il ne serait pas raisonnable de lire autant
    begin
      if Assigned(Lines) then
      begin
        if DoClear then
          Lines.Clear();
     
        Lines.Capacity := ACount;
        for Index := ACount - 1 downto 0 do
          Lines.Add(ReadNextLine());
      end;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.DeleteLine(const Index: Int64);
    begin
      if Indexed then
      begin
        // Je n'ai jamais eu le courage de le faire !
      end else
      begin
        raise ETextFileReaderError.Create(SNoIndexedDelete);
      end;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.DeleteLines(const IndexBegin, IndexEnd: Int64);
    var
      Index: Cardinal; // le For Delphi 6 ne gère pas le Int64, mais la suppression de 4 Millards de Lignes cela suffit
      ACount: Int64;
    begin
      ACount := IndexEnd - IndexBegin;
      if (0 <= ACount) and (ACount <= High(Word)) then
      begin
        for Index := IndexBegin to IndexEnd do
          DeleteLine(Index);
      end else
      begin
        raise ETextFileReaderError.CreateResFmt(@STooMuchDelete, [High(Cardinal), ACount]);
      end;
    end;
     
    { TSLTTextFileReader - Méthodes d'Accès }
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.GetIndexed: Boolean;
    begin
      Result := FIndexed and Assigned(FIndexFile);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.GetIndexFileName(): TFileName;
    var
      lFileName: TFileName;
    begin
      if FFileName = '' then
      begin
        FWorkingPathIsTemp := True;
    {$IFDEF MSWINDOWS}
        FWorkingPath := GetTemporaryPathForProcess(FWorkingSuffix);
    {$ELSE MSWINDOWS}
        FWorkingPath := GetTemporaryPathWithSuffix(FWorkingSuffix);
    {$ENDIF MSWINDOWS}
        lFileName := 'SLTTFR_' + UIntToStr(NativeUInt(FTextStream)) + '.idx';
     
        // Si l'on peut créer le répertoire, on l'utilise comme dossier de travail
        // Sinon, on tente dans le repertoire en cours du processus (avec tous les effets de bords possibles)
        if SimpleForceDirectories(FWorkingPath) then
          Result := FWorkingPath + lFileName
        else
          Result := lFileName;
      end
      else
      begin
        FWorkingPathIsTemp := False;
        FWorkingPath := ExtractFilePath(FFileName);
        Result := FFileName + '.idx';
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.GetLinearPosition(): Int64;
    begin
      Result := FStreamReader.LinearPosition;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextFileReader.SetIndexed(const Value: Boolean);
    begin
      if FIndexed <> Value then
      begin
        if Assigned(FIndexFile) then
        begin
          FIndexFile.Free();
          FIndexFile := nil;
        end;
     
        FIndexed := Value;
        FIndexCount := 0;
     
        if FIndexed then
        begin
          if not AllowUseExistingIndexFile or not FileExists(FIndexName) then
            if AutoIndexed then
              BuildLinesIndexes()
            else
              raise ETextFileReaderErrorIndex.CreateRes(@SIndexNotExist);
     
          FIndexFile := TFileStream.Create(GetIndexFileName(), fmOpenRead, fmShareDenyWrite);
          FIndexCount := FIndexFile.Size div FIndexRecSize;
        end;
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.GetCount: Int64;
    begin
      Result := FIndexCount;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.GetEOF: Boolean;
    begin
      Result := FStreamReader.LinearPosition >= FStreamReader.TextSize;
    end;
     
    { TSLTTextFileReader - Méthodes Privées }
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.ReadIndex(const Index: Int64): TSLTTextFileReaderIndex;
    begin
      if Indexed and (Index < FIndexCount) then
      begin
        FIndexFile.Seek(Index * FIndexRecSize, soBeginning);
        FIndexFile.Read(Result, FIndexRecSize);
      end else
        ZeroMemory(@Result, FIndexRecSize); // Windows uniquement
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.ReadLineFromIndexRec(const IndexRec: TSLTTextFileReaderIndex): string;
    begin
      Result := FStreamReader.ReadLineFromIndexRec(IndexRec);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextFileReader.ReadLineRaw(const Index: Int64; const OffSet: Int64 = 0): string;
    begin
      Result := FStreamReader.ReadLineRaw(FIndexFile, FUseCROnly, Index, OffSet);
    end;
     
    { TSLTTextStreamReaderAnsi }
     
    //------------------------------------------------------------------------------
    procedure TSLTTextStreamReaderAnsi.BuildLinesIndexes(AIndexFile: TFileStream; AUseCROnly: Boolean);
    const
      BUF_SIZE = 1024;
      REC_BUF_SIZE = 65536;
      LF: Byte = 10;
      CR: Byte = 13;
    var
      TextBuf: array[0..BUF_SIZE-1] of Byte;
      IndexRec: PTextFileReaderIndex;
      IndexesRec: packed array[0..REC_BUF_SIZE-1] of TSLTTextFileReaderIndex;
      iBuf, Readed: Integer;
      AByte: Byte;
      LastIsCR: Boolean;
      iRec, MaxRec: Integer;
      IRSize: Integer;
      EndOfLine: Byte;
    begin
      // Positionnement au début du Fichier
      FTextStream.Seek(0, soBeginning);
      // Compteur/Index/Drapeau à Zéro
      iRec := 0;
      MaxRec := REC_BUF_SIZE - 1;
      IRSize := REC_BUF_SIZE * SizeOf(IndexRec^);
      ZeroMemory(@IndexesRec, IRSize); // Windows Uniquement
      IndexRec := @IndexesRec[iRec];
      LastIsCR := False;
      if AUseCROnly then
        EndOfLine := CR // Compatible MacOS avec CR isolé
      else
        EndOfLine := LF; // Compatible Windows couple CR LF et Linux LF
     
      // Boucle jusqu'à la fin
      while (FTextStream.Position < FTextSize) do
      begin
        if not DoBuildIndexProgress(FTextStream.Position, FTextSize) then
          Abort;
     
        Readed := FTextStream.Read(TextBuf, BUF_SIZE);
        for iBuf := 0 to Readed - 1 do
        begin
          AByte := TextBuf[iBuf];
          if (AByte = EndOfLine) then
          begin
            IndexRec^.Length := (FTextStream.Position - Readed + iBuf) - IndexRec^.OffSet;
            if not AUseCROnly and LastIsCR then
              Dec(IndexRec^.Length); // -1 car on inclu pas le CR dans un couple CR LF
     
            if iRec = MaxRec then
            begin
              AIndexFile.Write(IndexesRec, IRSize);
              iRec := 0;
            end else
              Inc(iRec);
     
            IndexRec := @IndexesRec[iRec];
            IndexRec^.OffSet := FTextStream.Position - Readed + iBuf + 1; // + 1 car on inclu pas le LF du couple CR LF ou le CR isolé
          end;
     
          if not AUseCROnly then
            LastIsCR := (AByte = CR);
        end;
      end;
      if IndexRec^.OffSet < FTextSize then
      begin
        IndexRec^.Length := FTextStream.Position - IndexRec^.OffSet;
        if iRec = MaxRec then
        begin
          AIndexFile.Write(IndexesRec, IRSize);
          iRec := 0;
        end else
          Inc(iRec);
      end;
     
      if iRec > 0 then
        AIndexFile.Write(IndexesRec, iRec * SizeOf(IndexRec^));
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.DoBuildIndexProgress(const Position, Size: Int64): Boolean;
    begin
      if Assigned(FOnBuildIndexProgress) then
      begin
        Result := False;
        FOnBuildIndexProgress(Self, Position, Size, Result);
        Result := not Result; // la fonctionne renvoie True si on continue
      end
      else
        Result := True;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.GetLinearPosition: Int64;
    begin
      Result := FLinearPosition;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.GetOnBuildIndexProgress: TSLTTextFileReaderBuildIndexProgress;
    begin
      Result := FOnBuildIndexProgress;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.GetTextStream(): TStream;
    begin
      Result := FTextStream;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.GetTextSize: Int64;
    begin
      Result := FTextSize;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.ReadLineFromIndexRec(const IndexRec: TSLTTextFileReaderIndex): string;
    var
      ResultAnsi: AnsiString;
    begin
      if (IndexRec.OffSet >= 0) and (IndexRec.Length > 0) and (IndexRec.OffSet + IndexRec.Length <= FTextSize)then
      begin
        SetLength(ResultAnsi, IndexRec.Length);
        FTextStream.Seek(IndexRec.OffSet, soBeginning);
        FTextStream.Read(ResultAnsi[1], IndexRec.Length);
        Result := string(ResultAnsi); // Transtypage de chaîne explicite de AnsiString en string (UnicodeString)
      end else
        Result := '';
    end;
     
    //------------------------------------------------------------------------------
    function TSLTTextStreamReaderAnsi.ReadLineRaw(AIndexFile: TFileStream; AUseCROnly: Boolean; const Index, OffSet: Int64): string;
    const
      BUF_SIZE = 1024;
      LF: Byte = 10;
      CR: Byte = 13;
    var
      TextBuf: array[0..BUF_SIZE-1] of Byte;
      IndexRec: TSLTTextFileReaderIndex;
      iBuf, Readed: Integer;
      AByte: Byte;
      LastIsCR: Boolean;
      LineReaded: Int64;
      EndOfLine: Byte;
    begin
      // Positionnement au début du Fichier ou sur le Curseur de lecture linéaire
      FTextStream.Seek(OffSet, soBeginning);
     
      // Compteur/Index/Drapeau à Zéro
      IndexRec.OffSet := OffSet;
      IndexRec.Length := 0;
      LastIsCR := False;
      if AUseCROnly then
        EndOfLine := CR // Compatible MacOS avec CR isolé
      else
        EndOfLine := LF; // Compatible Windows couple CR LF et Linux LF
      LineReaded := 0;
      // Boucle jusqu'à la fin
      while (FTextStream.Position < FTextSize) do
      begin
        Readed := FTextStream.Read(TextBuf, BUF_SIZE);
        for iBuf := 0 to Readed - 1 do
        begin
          AByte := TextBuf[iBuf];
          if (AByte = EndOfLine) then
          begin
            FLinearPosition := FTextStream.Position - Readed + iBuf;
            IndexRec.Length := FLinearPosition - IndexRec.OffSet;
            Inc(FLinearPosition); // car on n'inclu pas la fin de ligne dans la prochaine ligne
            if not AUseCROnly and LastIsCR then
              Dec(IndexRec.Length); // -1 car on inclu pas le CR dans un couple CR LF
     
            // Si l'on trouve la fin de ligne, on récupère directement les données
            if (LineReaded = Index) then
            begin
              Result := ReadLineFromIndexRec(IndexRec);
              Exit;
            end;
            // la prochaine ligne commencera après ce dernier séparateur de ligne
            IndexRec.OffSet := FLinearPosition;
            Inc(LineReaded);
          end;
          if not AUseCROnly then
            LastIsCR := (AByte = CR);
        end;
      end;
      // On a pas trouvé de fin de ligne, il est fort possible que ce soit la dernière sans fin de ligne
      // On récupère l'ensemble de la fin du fichier, on lit (Fin - Offset) Byte à partir de Offset
      if IndexRec.OffSet < FTextSize then
      begin
        IndexRec.Length := FTextStream.Position - IndexRec.OffSet;
        FLinearPosition := FTextStream.Position + 1; // + 1 ainsi on sera définitivment en dehors !
        if (LineReaded = Index) then
        begin
          Result := ReadLineFromIndexRec(IndexRec);
          Exit;
        end;
      end;
      Result := '';
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextStreamReaderAnsi.SetLinearPosition(const Value: Int64);
    begin
      FLinearPosition := Value;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextStreamReaderAnsi.SetOnBuildIndexProgress(const Value: TSLTTextFileReaderBuildIndexProgress);
    begin
      FOnBuildIndexProgress := Value;
    end;
     
    //------------------------------------------------------------------------------
    procedure TSLTTextStreamReaderAnsi.SetTextStream(const Value: TStream);
    begin
      FTextStream := Value;
      if Assigned(FTextStream) then
        FTextSize := FTextStream.Size;
    end;
     
    end.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  10. #10
    Membre régulier
    Merci mais j ai ces erreurs à la compilation :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    uses
    {$IFDEF MSWINDOWS}
      Winapi.Windows,
    {$ELSE MSWINDOWS}
    {$MESSAGE ERROR 'Implémentation uniquement Windows pour TSLTTextFileReader'}
    {$ENDIF MSWINDOWS}
      System.IOUtils,
      SLT.Common.FileUtilsEx;
    SLT.Common.FileUtilsEx non trouvée .

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {$IFDEF MSWINDOWS}
        FWorkingPath := GetTemporaryPathForProcess(FWorkingSuffix);
    {$ELSE MSWINDOWS}
        FWorkingPath := GetTemporaryPathWithSuffix(FWorkingSuffix);
    {$ENDIF MSWINDOWS}
        lFileName := 'SLTTFR_' + UIntToStr(NativeUInt(FTextStream)) + '.idx';
     
        // Si l'on peut créer le répertoire, on l'utilise comme dossier de travail
        // Sinon, on tente dans le repertoire en cours du processus (avec tous les effets de bords possibles)
        if SimpleForceDirectories(FWorkingPath) then

    GetTemporaryPathForProcess non déclaré
    SimpleForceDirectories non déclaré

  11. ###raw>post.musername###
    Expert éminent sénior
    Ah oui cette verison à quelques dépendances
    Retire le "Simple" c'est pour un bug des version XE2 sur les disques réseau
    et GetTemporaryPathForProcess se remplace par System.IOUtils.TPath.GetTempPath

    sinon, j'ai ajouté ce commentaire

    Si tu as toujours 50 + CR + LF, utilise une lecture par un packed record + array of AnsiChar
    et le nombre de ligne c'est donc GetFizeSize / 52


    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
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
     
    //------------------------------------------------------------------------------
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  SimpleDirectoryExists et SimpleForceDirectories ont été écrit à l'origine en C++Builder XE2
     *  Version C++ alternative publiée sur "www.developpez.net"                   -
     *  Post : "teste de l'existence d'un répertoire "                             -
     *  Post Number : 7300206                                                      -
     *  Post URL = "http://www.developpez.net/forums/d1345384/environnements-developpement/delphi/debutant/teste-l-existence-d-repertoire/#post7300206"
     *                                                                             -
     *  Copyright "SLT Solutions", (©2006)                                         -
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *                                                                             -
     * ShaiLeTroll                                                                 -
     *                                                                             -
     * Ce logiciel est un programme informatique servant à aider les développeurs  -
     * Delphi avec une bibliothèque polyvalente, adaptable et fragmentable.        -
     *                                                                             -
     * Ce logiciel est régi par la licence CeCILL-C soumise au droit français et   -
     * respectant les principes de diffusion des logiciels libres. Vous pouvez     -
     * utiliser, modifier et/ou redistribuer ce programme sous les conditions      -
     * de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA    -
     * sur le site "http://www.cecill.info".                                       -
     *                                                                             -
     * En contrepartie de l'accessibilité au code source et des droits de copie,   -
     * de modification et de redistribution accordés par cette licence, il n'est   -
     * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,   -
     * seule une responsabilité restreinte pèse sur l'auteur du programme,  le     -
     * titulaire des droits patrimoniaux et les concédants successifs.             -
     *                                                                             -
     * A cet égard  l'attention de l'utilisateur est attirée sur les risques       -
     * associés au chargement,  à l'utilisation,  à la modification et/ou au       -
     * développement et à la reproduction du logiciel par l'utilisateur étant      -
     * donné sa spécificité de logiciel libre, qui peut le rendre complexe à       -
     * manipuler et qui le réserve donc à des développeurs et des professionnels   -
     * avertis possédant  des  connaissances  informatiques approfondies.  Les     -
     * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du      -
     * logiciel à leurs besoins dans des conditions permettant d'assurer la        -
     * sécurité de leurs systèmes et ou de leurs données et, plus généralement,    -
     * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.          -
     *                                                                             -
     * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez      -
     * pris connaissance de la licence CeCILL-C, et que vous en avez accepté les   -
     * termes.                                                                     -
     *                                                                             -
     *----------------------------------------------------------------------------*)
    unit SLT.Common.FileUtilsEx;
     
    interface
     
    uses System.SysUtils, System.IOUtils;
     
    /// <summary>Encapsule System.IOUtils.TPath.GetTempPath et suffixe le chemin par un nom de groupe</summary>
    /// <param name="AGroupSuffix">Permet de regrouper les dossiers temporaires sous une même thématique</param>
    /// <returns>Une Chaine de la forme '?:\...\temp\%AGroupSuffix%\' ou '?:\...\temp\'</returns>
    function GetTemporaryPathWithSuffix(const AGroupSuffix: string = ''): TFileName;
     
    /// <summary>Encapsule l'API GetTempPath et suffixe le chemin par un nom de groupe puis le nom du processus en cours </summary>
    /// <param name="AGroupSuffix">Permet de regrouper les dossiers temporaires sous une même thématique</param>
    /// <param name="AModuleSuffix">Permet de répartir les log dans plusieurs sous-dossiers temporaires</param>
    /// <returns>Une Chaine de la forme '?:\...\temp\%AGroupSuffix%\%ModuleFileName%\%AModuleSuffix%' ou '?:\...\temp\%ModuleFileName%\%AModuleSuffix%'</returns>
    {$IFDEF MSWINDOWS}
    function GetTemporaryPathForProcess(const AGroupSuffix: string = ''; const AModuleSuffix: string = ''): TFileName;
    {$ENDIF MSWINDOWS}
     
     
    /// <summary>Version simplifiée et fonctionnelle compensant la version buggée de DirectoryExists</summary>
    /// <param name="ADirectory">Répertoire à vérifer</param>
    /// <returns>Si le répertoire existe, la fonction renvoie True. Si le répertoire n'existe pas, la fonction renvoie False</returns>
    {$IFDEF MSWINDOWS}
    function SimpleDirectoryExists(const ADirectory: TFileName): Boolean;
    {$ENDIF MSWINDOWS}
     
    /// <summary>Version simplifiée et fonctionnelle compensant la version buggée de ForceDirectories</summary>
    /// <param name="ADirectory">Répertoire à créer</param>
    /// <returns>ForceDirectories renvoie true s'il réussit à créer tous les répertoires nécessaires, false s'il n'a pas pu créer le répertoire voulu.</returns>
    {$IFDEF MSWINDOWS}
    function SimpleForceDirectories(const ADirectory: TFileName): Boolean;
    {$ENDIF MSWINDOWS}
     
     
    implementation
     
    uses
    {$IFDEF MSWINDOWS}
      Winapi.Windows;
    {$ENDIF MSWINDOWS}
     
    //------------------------------------------------------------------------------
    function GetTemporaryPathWithSuffix(const AGroupSuffix: string = ''): TFileName;
    begin
      Result := System.IOUtils.TPath.GetTempPath();
      if Result <> '' then
        Result := IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(Result) + AGroupSuffix);
    end;
     
    {$IFDEF MSWINDOWS}
    //------------------------------------------------------------------------------
    function GetTemporaryPathForProcess(const AGroupSuffix: string = ''; const AModuleSuffix: string = ''): TFileName;
    var
      ModuleFileName: array[0..MAX_PATH - 1] of Char;
    begin
      Result := GetTemporaryPathWithSuffix(AGroupSuffix);
      if Result <> '' then
      begin
        GetModuleFileName(0, ModuleFileName, MAX_PATH); // 0 c'est pour l'EXE même si depuis une DLL, avec HInstance cela fournirait le nom de la DLL
        Result := IncludeTrailingPathDelimiter(Result + System.IOUtils.TPath.GetFileNameWithoutExtension(ModuleFileName));
        if AModuleSuffix <> '' then
           Result := IncludeTrailingPathDelimiter(Result + AModuleSuffix);
      end;
    end;
    {$ENDIF MSWINDOWS}
     
    {$IFDEF MSWINDOWS}
    function SimpleDirectoryExists(const ADirectory: TFileName): Boolean;
    var
      Code: DWORD;
    begin
      // La fonction DirectoryExists a subi une modification entre 2007 et XE2 (QC 108480, 107686 et 105954)
      // Cela ne gère pas :
      // - ERROR_ACCESS_DENIED, si l'on a pas de droit suffisant
      // - ERROR_BAD_NET_NAME, si l'on a perdu le réseau ou fourni un mauvais nom de serveur de fichier
     
      Code := GetFileAttributes(PChar(ADirectory));
      Result := (Code <> INVALID_FILE_ATTRIBUTES) and ((FILE_ATTRIBUTE_DIRECTORY and Code) <> 0);
    end;
     
    function SimpleForceDirectories(const ADirectory: TFileName): Boolean;
    var
      Dir: TFileName;
    begin
      // ForceDirectories utilise DirectoryExists
      // Une regression dans DirectoryExists provoque un retour érroné de ForceDirectories sur le chemin réseau innaccessible
     
      Result := False;
      if (ADirectory <> '') then
      begin
        Dir := ExcludeTrailingPathDelimiter(ADirectory);
        if not SimpleDirectoryExists(Dir) then
        begin
          if (Length(Dir) < 3) or (ExtractFilePath(Dir) = Dir) then
            Result := CreateDir(Dir)
          else
            Result := SimpleForceDirectories(ExtractFilePath(Dir)) and CreateDir(Dir);
        end
        else
          Result := True; // Existe déjà !
      end;
    end;
    {$ENDIF MSWINDOWS}
     
    end.
      0  0

  12. #12
    Rédacteur/Modérateur

    Citation Envoyé par MoiStéphane Voir le message
    La base est effectivement crée.
    Dans ma boucle chaque fois que je lis une ligne je la formate et écrit une requête insert dans sql.add();
    D'après ce que je comprends, la structure de la base de données en mémoire dont tu as besoin n'a pas la même structure que le csv. Est-ce bien ça ?

    Si c'est le cas, sache qu'une fois créée en mémoire (BatchMove), cette base de données est accessible par d'autres composants, un TFDQuery par exemple. Dans la requête, il suffit d'utiliser le nom du composant TFDMemTable comme source de données.

    Exemple si FDMemTable1, la requête d'un TFDQuery serait :
    Code sql :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT
      MyDateTime,
      DATE(MyDateTime) AS MyDate,
      TIME(MyDateTime) AS MyTime,
      CONCAT('"', MyDateTime, '"') AS QuotedDateTime,
      -- etc
     
    FROM FDMemTable1

  13. #13
    Rédacteur/Modérateur

    Bonjour,

    Déjà si on avait un extrait du CSV et la structure de la table à remplir on nagerait moins dans le théorique.
    On verrait alors si le FDBatchmove d'Andnotor est possible. AMHA oui et ce serait certainement plus optimum qu'une insertion à chaque ligne. Il existe aussi une solution "intermédiaire" à base de Array DML
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio, Sidney) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd

  14. #14
    Membre régulier
    Merci pour cette réponse à laquelle je n ai pas pu répondre avant faute d un pb avec laposte.net qui m empêchait de recevoir les notifications...

    Finalement j ai opté pour une insertion par blocs de 10 lignes avant d avoir vu la dernière réponse sur laquelle je vais me pencher

  15. #15
    Expert éminent sénior
    la lecture caractère par caractère est trop lente

    il faut lire par block et recherche les fins de lignes dans le block

    ou encore utiliser un TextFile qui fait tout cela déjà

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    var
     f: TextFile;
    begin
      Assignfile(f, 'test.csv');
      Reset(f);
      while not Eof(f) do
      begin
        ReadLn(f, ligne);
      end;
      CloseFile(f);
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  16. #16
    Membre actif
    Merci Sergio
    Bonjour
    la bonne question à enfin été posée par Sergio :-)
    Déjà si on avait un extrait du CSV et la structure de la table à remplir
    Ayant beaucoup bossé sur ce genre de programme (insert dans DB les cotations venant d'automates ou BloomBerg, Telekurs, ...),
    ces fichiers sont en général des fichiers textes <CRLF>, ANSI (8 bits mais on s'en fout) avec une lignes de Header, des lignes de données, une ligne footer. Pour chaque type de ligne, le format est fixe, sans séparateur entre les champs, avec des 0 à gauche pour les num, des espaces à droite pour les string), parfois les num sont sans . décimaux, les cours sont souvent num(12,6).

    Le header contient un Id du format des données, le code fournisseur, le code market (bourse) concerné, qq trucs inutiles

    les lignes de données ressemblent à qq'chose comme çà: Date, heure, code valeur, cours,<CRLF>
    20200101121000EURUSD 000001.129780<CRLF>
    20200101121002EURUSD 000000.129720<CRLF>

    la ligne footer a un flag de fin, le nb de lignes de données, + trucs inutiles

    Lire çà c'est tout facile donne nous
    - un exemple (2 3 lignes) des données
    - ta DB et la structure de la table
    Je te filerai des exemples.

    solilog

  17. #17
    Membre régulier
    Les données sont extraites du logiciel QuantDataManager. pour la structure d'une ligne elle est la suivante :
    Séparateur de colonne ',' séparateur décimal '.', taille constante,les lignes se terminent par le caractère #10 en delphi

    exemple : 2018-08-30,15:50:00,5476.5,5479,5476.5,5477.5

    Il n y a ni ligne d’Entête ni ligne footer

    Pour ce qui est de la strcture de la table
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    CREATE TABLE tb_import ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "Jour"	DATE,"Heure" TIME,"price_open" REAL,"price_high" REAL,"price_low" REAL,"price_close" REAL)


    Merci pour le coup de main

  18. #18
    Rédacteur/Modérateur

    Bonjour,

    les seuls problèmes que j'aperçois serait les FormatSettings (date et point décimal) qui se résolvent fort bien.
    Mais, cela tombe bien, FDBatchmove du moins le FDBatchMoveTextReader permet de changer les formatsettings



    En regardant les diverses discussions ouvertes (forum Delphi, SQLite) je pose les fondations : Delphi 10.3 , VCL, Firedac.
    j'ai repris votre code SQL pour enlever les " "
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE TABLE tb_import (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        Jour        DATE,
        Heure       TIME,
        price_open  REAL,
        price_high  REAL,
        price_low   REAL,
        price_close REAL
    );

    AMHA il manque des index (jour ?) pour plus de fluidité par la suite et une indication sur l'action cotée (car je présume qu'il ne doit pas y en avoir qu'une seule) mais peu importe, il s'agit de transfert.

    Bref, voilà comment devrait se présenter la forme (deux étapes pour adapter le batchmove


    et ... même pas besoin de lancer le programme, clic droit sur batchmove/ exécuter me fait déjà mon premier transfert test, vérifiable dans SQLiteStudio


    bon, j'ai légèrement triché en augmentant le nombre d'éléments dans le fichier et en mettant un .0 à la première valeur de price_high pour forcer une détection en float et non en integer
    2018-08-30,15:50:00,5476.5,5479.0,5476.5,5477.5
    2018-09-30,15:50:00,5476.5,5479,5476.5,5477.5
    2018-10-30,15:50:00,5476.5,5479,5476.5,5477.5
    2018-11-30,15:50:00,5476.5,5479,5476.5,5477.5
    2018-12-30,15:50:00,5476.5,5479,5476.5,5477.5
    2018-01-30,15:50:00,5476.5,5479,5476.5,5477.5
    2018-02-28,15:50:00,5476.5,5479,5476.5,5477.5
    2018-03-30,15:50:00,5476.5,5479,5476.5,5477.5
    un peu galéré à un moment à cause de mon jeu d'essai car j'avais mis un mois 13, mais aussi un mois de février à 30 jours
    Mais, ma conclusion est que FDBatchmove serait parfaitement adapté.

    Je n'ai pas finalisé plus le programme, mais si besoin je peux le faire
    La seule chose absolue dans un monde comme le nôtre, c'est l'humour. » Albert Einstein

    Delphi installés : D3,D7,D2010,XE4,XE7,D10 (Tokyo, Rio, Sidney) et peut être quelques autres
    SGBD : Firebird 2.5, 3, SQLite
    générateurs Etats : FastReport, Rave, QuickReport
    OS : Window Vista, Windows 10, Ubuntu, Androïd

  19. #19
    Membre régulier
    Merci pour cette réponse on ne peut plus précise.

    Je testerai ce soir.

    Je n ai effectivement pas d index car je le créé une fois la table remplie pour diminuer le temps de remplissage de la table.

  20. #20
    Membre régulier
    Donc j ai testé comme suit :

    J ai créé la bdd et la table avec DBBrowser for sqlite
    J ai affecté le bon driver et la bonne base au TFdConnection


    FDTable1 :
    Table : tb_import
    Connection : FDConnection1

    FDBatchMoveDataSetWriter1 :
    Dataset : FDTable1

    FDBatchMoveTextReader1 :
    J ai modifié les formats comme indiqué sur ton image et remplis la proprieté DestinationFieldName
    Quand je vais sur la propriété datadef.fields je n ai que 5 champs affichés contrairement à toi qui en a 6 !!


    FDBatchMove1 :
    Reader : FDBatchMoveTextReader1
    Writer : FDBatchMoveDataSetWriter1
    Dans mapping j ai créé les champs

    Quand j exécute le BatchMove, j ai l erreur Correspondance non définie entre les champs source et destination.

    Je comprends que cela vient du fait que dans mon FDBatchMoveTextReader1 je n'ai que 5 champs alors que dans le writer et la table j en ai 6 mais je ne sais pas comment y remedier.

    Je vois que tu as fait un programme pour tester. Te serait il possible de le mettre à disposition ici ?

    merci encore pour ton aide.