IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Bases de données Delphi Discussion :

Chargement d'un fichier CSV dans une FDMemTable


Sujet :

Bases de données Delphi

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 7
    Points : 5
    Points
    5
    Par défaut Chargement d'un fichier CSV dans une FDMemTable
    Bonjour à tous,

    J'essaie de charger dans une FDMemTable un fichier .CSV de la manière suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TForm2.btn1Click(Sender: TObject);
    var
      FDMemTable: TFDMemTable;
    begin
      FDMemTable:=TFDMemTable.Create(nil);
      try
        LoadFileIntoMemoryTable(edt1.Text, FDMemTable);
      finally
        FDMemTable.Free;
      end;
    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
    procedure TForm2.LoadFileIntoMemoryTable(AFileName:string; var AOutFDMemTable: TFDMemTable);
    var
      FDBatchMove:        TFDBatchMove;
    begin
      //load into FDMemTable 'MemoryFile'
      AOutFDMemTable.Name:='MemoryFile';
      FDBatchMove:=TFDBatchMove.Create(nil);
      try
        FDBatchMove.Options:=FDBatchMove.Options + [poClearDest];
     
        with TFDBatchMoveTextReader.Create(FDBatchMove) do
        begin
          FileName:=AFileName;
          DataDef.WithFieldNames := False;
          DataDef.RecordFormat:=rfCustom;
          DataDef.Separator := ';';
          DataDef.Delimiter:='¤';
        end;
     
        with TFDBatchMoveDataSetWriter.Create(FDBatchMove) do
        begin
          DataSet := AOutFDMemTable;
        end;
     
        FDBatchMove.Analyze := [taHeader, taFields];
        FDBatchMove.AnalyzeSample:=1;
        FDBatchMove.Execute;
      finally
        FDBatchMove.Free;
      end;
    end;
    J'ai simplifié mon fichier pour chercher l'erreur et du coup, il ne me reste qu'une entête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Header1;Header2;Header3;Header4;Header5;Header6;Header7;Header8;Header9;Header10;Header11;Header12;Header13;Header14;Header15;Header16;Header17;Header18;Header19;Header20;Header21;Header22;Header23;Header24;Header25;Header26;Header27;Header28;Header29;Header30;Header31
    Si mon entête de fichier est comme celle-ci pas de problème. Par contre si je rajoute une colonne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Header1;Header2;Header3;Header4;Header5;Header6;Header7;Header8;Header9;Header10;Header11;Header12;Header13;Header14;Header15;Header16;Header17;Header18;Header19;Header20;Header21;Header22;Header23;Header24;Header25;Header26;Header27;Header28;Header29;Header30;Header31;Header32
    J'ai le droit à une belle Violation d'accès dans le module...

    et là je ne comprends pas pourquoi. Si quelqu'un à une petite idée, elle est la bienvenue.

    Merci d'avance.
    Cordialement,
    Vincent

  2. #2
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    J'utilise Delphi XE7

  3. #3
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 563
    Points : 25 165
    Points
    25 165
    Par défaut
    J'ai XE7 Pro et je ne trouve pas FireDAC.Comp.BatchMove.pas, on a que les DCU !
    C'est pas cool ça !
    Comme les deux dépassent 255, ce n'est pas une limite de buffer pour la ligne (du moins le choix de la taille du buffer serait très étrange)

    Cela ne supporterait pas plus de 31 colonnes
    32 ce n'est pas un nombre anodin c'est comme 64, 128,
    c'est le grand classique du tableau [0..31], le 0 est réservé pour une utilisation interne, il ne reste plus que 31 colonnes utiles

    Si tu as un petit CSV, un TStringList et fera l'affaire pour chaque ligne par ligne
    Puis, via un second TStringList et DelimiterText, tu découpes champ par champ

    Si ton CSV, fait plusieurs Go
    Si tu veux, j'ai un parser basique de CSV (il ne supporte pas un CRLF dans les données) mais permet de lire un gros très fichier

    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
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815
    816
    817
    818
    819
    820
    821
    822
    823
    824
    825
    826
    827
    828
    829
    830
    831
    832
    833
    834
    835
    836
    837
    838
    839
    840
    841
    842
    843
    844
    845
    846
    847
    848
    849
    850
    851
    852
    853
    854
    855
    856
    857
    858
    859
    860
    861
    862
    863
    864
    865
    866
    867
    868
    869
    870
    871
    872
    873
    874
    875
    876
    877
    878
    879
    880
    881
    882
    883
    884
    885
    886
    887
    888
    889
    890
    891
    892
    893
    894
    895
    896
    897
    898
    899
    900
    901
    902
    903
    904
    905
    906
    907
    908
    909
    910
    911
    912
    913
    914
    915
    916
    917
    918
    919
    920
    921
    922
    923
    924
    925
    926
    927
    928
    929
    930
    931
    932
    933
    934
    935
    936
    937
    938
    939
    940
    941
    942
    943
    944
    945
    946
    947
    948
    949
    950
    951
    952
    953
    954
    955
    956
    957
    958
    959
    960
    961
    962
    963
    964
    965
    966
    967
    968
    969
    970
    971
    972
    973
    974
    975
    976
    977
    978
    979
    980
    981
    982
    983
    984
    985
    986
    987
    988
    989
    990
    991
    992
    993
    994
    995
    996
    997
    998
    999
    1000
    1001
    1002
    1003
    1004
    1005
    1006
    1007
    1008
    1009
    1010
    1011
    1012
    1013
    1014
    1015
    1016
    1017
    1018
    1019
    1020
    1021
    1022
    1023
    1024
    1025
    1026
    1027
    1028
    1029
    1030
    1031
    1032
    1033
    1034
    1035
    1036
    1037
    1038
    1039
    1040
    1041
    1042
    1043
    1044
    1045
    1046
    1047
    1048
    1049
    1050
    1051
    1052
    1053
    1054
    1055
    1056
    1057
    1058
    1059
    1060
    1061
    1062
    1063
    1064
    1065
    1066
    1067
    1068
    1069
    1070
    1071
    1072
    1073
    1074
    1075
    1076
    1077
    1078
    1079
    1080
    1081
    1082
    1083
    1084
    1085
    1086
    1087
    1088
    1089
    1090
    1091
    1092
    1093
    1094
    1095
    1096
    1097
    1098
    1099
    1100
    1101
    1102
    1103
    1104
    1105
    1106
    1107
    1108
    1109
    1110
    1111
    1112
    1113
    1114
    1115
    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130
    1131
    1132
    1133
     
    //-----------------------------------------------------------------------------)
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Copyright ou © ou Copr. "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 Delphi XE2 (Support de C++Builder XE2 a prévoir)
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *                                                                             -
     * 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.ETL.Import_CSV;
     
    interface
     
    uses System.Classes, System.SysUtils, System.Types,
      Generics.Collections,
      SLT.ETL.Import,
      SLT.Common.CSVUtils;
     
    type
      { Forward class declarations }
      TSLTETLImportCSV = class;
      ISLTETLImportCSVSourceReader = interface;
      TSLTETLImportCSVSourceReader = class;
      TSLTETLImportCSVSourceChecker = class;
      ISLTETLImportCSVSourceDispatcher = interface;
      TSLTETLImportCSVSourceDispatcher = class;
      ISLTETLImportCSVDestinationDispatcher = interface;
      TSLTETLImportCSVDestinationDispatcher = class;
      TSLTETLImportCSVColumns = class;
      TSLTETLImportCSVColumn = class;
      TSLTETLImportCSVHeader = class;
     
      { Aliases class declarations }
      ISLTETLImportCSVFormatSpecification = ISLTCSVFormatSpecification;
      TSLTETLImportCSVHeaderSpecification = TSLTETLImportCSVHeader;
     
      TSLTETLImportCSVRawHeader = TStringDynArray;
      TSLTETLImportCSVRawHeaders = array of TSLTETLImportCSVRawHeader;
     
      /// <summary>Erreur de base liée à l'importation des données correspondant aux spécifications du format CSV</summary>
      ESLTETLImportCSVError = class(Exception);
     
      /// <summary>Importe des données correspondant aux spécifications du format CSV</summary>
      TSLTETLImportCSV = class abstract(TSLTETLAbstractImportFromStream)
      private
        // Membres privés
        FSourceReader: ISLTETLImportSourceReader;
        FSourceChecker: ISLTETLImportSourceChecker;
        FSourceDispatcher: ISLTETLImportSourceDispatcher;
        FDestinationDispatcher: ISLTETLImportDestinationDispatcher;
      protected
        // Accesseurs Abstraits redéfinis
        /// <summary>Fournit une implémentation de ISLTETLImportSourceReader chargée de lire le contenu de la Source</summary>
        function GetSourceReader(): ISLTETLImportSourceReader; override;
        /// <summary>Fournit une implémentation de ISLTETLImportSourceChecker chargée de vérifier le contenu de la Source</summary>
        function GetSourceChecker(): ISLTETLImportSourceChecker; override;
        /// <summary>Fournit une implémentation de ISLTETLImportDispatcher chargée de fournir les données à importer depuis de la Source</summary>
        function GetSourceDispatcher(): ISLTETLImportSourceDispatcher; override;
        /// <summary>Fournit une implémentation de ISLTETLImportDestinationDispatcher chargée d'intégrer les données</summary>
        function GetDestinationDispatcher(): ISLTETLImportDestinationDispatcher; override;
        /// <summary>Fournit une implémentation de ISLTETLImportSourceStreamReader chargée de lire le contenu de la Source à partir d'un flux générique</summary>
        function GetStreamReader(): ISLTETLImportSourceStreamReader; override;
     
        // Méthodes Abstraites
        /// <summary>La redéfinition doit fournir le format du fichier CSV attendu</summary>
        procedure InitializeCSVFormat(ASourceReader: ISLTETLImportCSVSourceReader); virtual; abstract;
        /// <summary>La redéfinition doit fournir le traitement d'importation des données issu du fichier CSV</summary>
        procedure ProcessCSVLine(const ACSVLine: TStringDynArray); virtual; abstract;
     
        // Accesseurs
        /// <summary>Fournit une implémentation de ISLTETLImportCSVSourceReader chargée de lire le contenu de la Source correspondant aux spécifications du format CSV</summary>
        function GetCSVSourceReader(): ISLTETLImportCSVSourceReader;
        /// <summary>Fournit une implémentation de ISLTETLImportCSVDestinationDispatcher chargée d'intégrer les données issues d'une source correspondant aux spécifications du format CSV</summary>
        function GetCSVDestinationDispatcher(): ISLTETLImportCSVDestinationDispatcher;
     
        // Gestionnaire d'Evenements
        procedure DispatchLineEventHandler(Sender: TObject; const ALine: TStringDynArray);
      public
        // Constructeurs
        constructor Create();
        destructor Destroy(); override;
     
        // Méthodes - Redéfinition de TSLTETLImport
        function Import(): Boolean; override;
     
      protected
        // Propriétés protégées
        /// <summary>Fournit l'interface responsable de la lecture de la source de données correspondant aux spécifications du format CSV</summary>
        property CSVSourceReader: ISLTETLImportCSVSourceReader read GetCSVSourceReader;
        /// <summary>Fournit l'interface responsable de fournir les données à importer depuis de la Source de données correspondant aux spécifications du format CSV</summary>
        property CSVDestinationDispatcher: ISLTETLImportCSVDestinationDispatcher read GetCSVDestinationDispatcher;
      end;
     
      /// <summary>Cette interface permet de lire la source de données correspondant aux spécifications du format CSV</summary>
      ISLTETLImportCSVSourceReader = interface
        ['{FD57280C-DEFE-49E4-8EE9-0EB67E3CEA58}']
     
        // Accesseurs - Implémentation de ISLTETLImportSourceReader
        function GetExpectedColumns(): TSLTETLImportCSVColumns;
        function GetFoundColumns(): TSLTETLImportCSVColumns;
        function GetExpectedHeader(): TSLTETLImportCSVHeader;
        function GetFoundHeader(): TSLTETLImportCSVHeader;
        function GetData(): ISLTCSVReader;
        procedure SetData(const Value: ISLTCSVReader);
     
        // Méthodes
        /// <summary>Efface les paramètres attendues et trouvée</summary>
        procedure Clear();
     
        // Propriétés
        /// <summary>Description des Colonnes attendues dans la source CSV</summary>
        property ExpectedColumns: TSLTETLImportCSVColumns read GetExpectedColumns;
        /// <summary>Description des Colonnes présentes dans la source CSV</summary>
        property FoundColumns: TSLTETLImportCSVColumns read GetFoundColumns;
        /// <summary>Indique si la source CSV doit contenir des lignes d'entête</summary>
        property ExpectedHeader: TSLTETLImportCSVHeader read GetExpectedHeader;
        /// <summary>Indique si la source CSV contient des lignes d'entête</summary>
        property FoundHeader: TSLTETLImportCSVHeader read GetFoundHeader;
        /// <summary>Accès au données du CSV</summary>
        property Data: ISLTCSVReader read GetData write SetData;
      end;
     
      /// <summary>Lit les données correspondant aux spécifications du format CSV</summary>
      TSLTETLImportCSVSourceReader = class(TInterfacedObject, ISLTETLImportSourceReader, ISLTETLImportSourceStreamReader, ISLTETLImportCSVSourceReader)
      private
        // Membres privés de Classe
        class var
          FDefaultCSVReaderClass: TClass;
      private
        // Membres privés
        FReaderIntf: ISLTCSVReader;
        FExpectedColumns: TSLTETLImportCSVColumns;
        FFoundColumns: TSLTETLImportCSVColumns;
        FExpectedHeader: TSLTETLImportCSVHeader;
        FFoundHeader: TSLTETLImportCSVHeader;
     
        // Méthodes Internes
        function ReaderIntfFactory(): ISLTCSVReader;
      protected
        // Accesseurs - Implémentation de ISLTETLImportSourceStreamReader
        function GetSource(): TStream;
        procedure SetSource(const Value: TStream);
     
        // Accesseurs - Implémentation de ISLTETLImportCSVSourceReader
        function GetExpectedColumns(): TSLTETLImportCSVColumns;
        function GetFoundColumns(): TSLTETLImportCSVColumns;
        function GetExpectedHeader(): TSLTETLImportCSVHeader;
        function GetFoundHeader(): TSLTETLImportCSVHeader;
        function GetData(): ISLTCSVReader;
        procedure SetData(const Value: ISLTCSVReader);
     
      public
        // Méhodes - Implémentation de ISLTETLImportCSVSourceReader
        procedure Clear();
     
      public
        // Constructeurs
        constructor Create();
        destructor Destroy(); override;
     
      public
        // Constructeurs de Classe
        class constructor Create();
     
        // Propriétés de Classe
        /// <summary>Indique la classe par défaut utilisée comme Implémentation de ISLTCSVReader</summary>
        class property DefaultCSVReaderClass: TClass read FDefaultCSVReaderClass write FDefaultCSVReaderClass;
      end;
     
      /// <summary>Vérifie que la source correspondant aux spécifications du format CSV</summary>
      TSLTETLImportCSVSourceChecker = class(TInterfacedObject, ISLTETLImportSourceChecker)
      private
        // Membres privés
        FCheckMessages: TStrings;
     
        // Méthodes
        function ReadHeaders(ASourceReader: ISLTETLImportCSVSourceReader; out ARawHeaders: TSLTETLImportCSVRawHeaders): Boolean;
        function CheckHeaders(ASourceReader: ISLTETLImportCSVSourceReader): Boolean;
      protected
        // Accesseurs - Implémentation de ISLTETLImportSourceChecker
        function GetCheckMessages(): TStrings;
      public
        // Méthodes - Implémentation de ISLTETLImportSourceChecker
        function CheckSource(ASourceReader: ISLTETLImportSourceReader): Boolean;
      public
        // Méthodes
        class function IsCSVFile(ASourceReader: ISLTETLImportSourceReader): Boolean;
        class function IsCSVFileName(AFileName: TFileName): Boolean;
        class function IsCSVStream(AStream: TStream): Boolean;
      public
        // Constructeurs
        constructor Create();
        destructor Destroy(); override;
      end;
     
      /// <summary>Cette interface permet de récuperer les données prêtes à être importer</summary>
      ISLTETLImportCSVSourceDispatcher = interface
        ['{8D425059-98E8-4377-8B4A-38CD6B0F4972}']
     
        // Méthodes
        /// <summary>Extrait les Données CSV</summary>
        function DispacthCSVLine(ASourceReader: ISLTETLImportSourceReader; out ACSVLine: TStringDynArray): Boolean;
      end;
     
      /// <summary>Fourni les données à importer</summary>
      TSLTETLImportCSVSourceDispatcher = class(TInterfacedObject, ISLTETLImportSourceDispatcher, ISLTETLImportCSVSourceDispatcher)
      public
        // Méthodes - Implémentation de ISLTETLImportDispatcher
        function DispatchSourceToDestination(ASourceReader: ISLTETLImportSourceReader; ADestinationDispatcher: ISLTETLImportDestinationDispatcher): Boolean;
     
        // Méthodes - Implémentation de ISLTETLImportCSVSourceDispatcher
        function DispacthCSVLine(ASourceReader: ISLTETLImportSourceReader; out ACSVLine: TStringDynArray): Boolean;
      end;
     
      /// <summary>Evènement qui se produit pour chaque ligne du CSV</summary>
      TSLTETLImportCSVDestinationDispatchEvent = procedure(Sender: TObject; const ACSVLine: TStringDynArray) of object;
     
      /// <summary>Cette interface permet d'importer les données correspondant aux spécifications du format CSV</summary>
      ISLTETLImportCSVDestinationDispatcher = interface
        ['{AEBB0F46-D11A-43B8-B1A4-580C7E0B081B}']
     
        // Accesseurs
        function GetOnDispatch(): TSLTETLImportCSVDestinationDispatchEvent;
        procedure SetOnDispatch(const Value: TSLTETLImportCSVDestinationDispatchEvent);
     
        // Méthodes
        procedure DoDispatch(const ACSVLine: TStringDynArray);
     
        // Propriétés
        /// <summary>Evènement qui se produit pour chaque ligne du CSV</summary>
        property OnDispatch: TSLTETLImportCSVDestinationDispatchEvent read GetOnDispatch write SetOnDispatch;
      end;
     
      /// <summary>Fourni les données à importer</summary>
      TSLTETLImportCSVDestinationDispatcher = class(TInterfacedObject, ISLTETLImportDestinationDispatcher, ISLTETLImportCSVDestinationDispatcher)
      private
        // Membres privés
        FOnDispatch: TSLTETLImportCSVDestinationDispatchEvent;
      protected
        // Accesseurs - Implémentation de ISLTETLImportCSVDestinationDispatcher
        function GetOnDispatch(): TSLTETLImportCSVDestinationDispatchEvent;
        procedure SetOnDispatch(const Value: TSLTETLImportCSVDestinationDispatchEvent);
      public
        // Méthodes - Implémentation de ISLTETLImportCSVDestinationDispatcher
        procedure DoDispatch(const ACSVLine: TStringDynArray);
     
        // Méthodes - Implémentation de ISLTETLImportDestinationDispatcher
        function DispatchDestinationFromSource(ASourceReader: ISLTETLImportSourceReader; ASourceDispatcher: ISLTETLImportSourceDispatcher): Boolean;
      end;
     
      /// <summary>Description des Colonnes d'une source CSV</summary>
      TSLTETLImportCSVColumns = class(TObject)
      private
        // Types internes
        type
          TColumnList = Generics.Collections.TObjectList<TSLTETLImportCSVColumn>;
      private
        // Membres privés
        FColumnList: TColumnList;
        FDuplicates: TDuplicates;
     
        // Accesseurs
        function GetCount: Integer;
        function GetItems(Index: Integer): TSLTETLImportCSVColumn;
     
        // Méthodes Privées
        function IndexOfColumnName(const AColumnName: string): Integer;
      public
        // Constructeurs
        constructor Create();
        destructor Destroy(); override;
     
        // Méthodes
        /// <summary>Crée une nouvelle colonne nommée et utilise l'index dans la liste comme position par défaut dans la source CSV</summary>
        function AddColumn(const AColumnName: string): TSLTETLImportCSVColumn; overload;
        /// <summary>Crée une nouvelle colonne nommée et typée. Utilise l'index dans la liste comme position par défaut dans la source CSV</summary>
        function AddColumn(const AColumnName: string; AColumnType: TVarType): TSLTETLImportCSVColumn; overload;
        /// <summary>Recopie les colonnes depuis une autre liste</summary>
        procedure CopyFrom(ASource: TSLTETLImportCSVColumns);
        /// <summary>Efface les colonnes</summary>
        procedure Clear();
     
        // Propriétés
        /// <summary>Description des Colonnes attendues dans la source CSV</summary>
        property Count: Integer read GetCount;
        /// <summary>Description des Colonnes présentes dans la source CSV</summary>
        property Items[Index: Integer]: TSLTETLImportCSVColumn read GetItems; default;
        /// <summary> Indique si l'ajout de colonnes dupliquées est autorisé</summary>
        property Duplicates: TDuplicates read FDuplicates write FDuplicates;
      end;
     
      /// <summary>Description d'une Colonne CSV</summary>
      TSLTETLImportCSVColumn = class(TObject)
      private
        // Membres privés
        FColumnName: string;
        FColumnType: TVarType;
        FColumnPosition: Integer;
        FColumnSizeMax: Integer;
        FColumnSizeMin: Integer;
      public
        // Constructeurs
        constructor Create(const AColumnName: string);
     
        // Méthodes
        /// <summary>Recopie les données d'une colonne depuis une autre colonne hormis son nom</summary>
        procedure CopyFrom(ASource: TSLTETLImportCSVColumn);
     
        // Propriétés
        /// <summary>Nom de la Colonne CSV</summary>
        property ColumnName: string read FColumnName;
        /// <summary>Nom de la Colonne CSV</summary>
        property ColumnType: TVarType read FColumnType write FColumnType;
        /// <summary>Position de la Colonne CSV</summary>
        property ColumnPosition: Integer read FColumnPosition write FColumnPosition;
        /// <summary>Taille minimale de la Colonne CSV</summary>
        property ColumnSizeMin: Integer read FColumnSizeMin write FColumnSizeMin;
        /// <summary>Taille maximale dde la Colonne CSV</summary>
        property ColumnSizeMax: Integer read FColumnSizeMax write FColumnSizeMax;
      end;
     
      /// <summary>Description des Entêtes d'une source CSV</summary>
      TSLTETLImportCSVHeader = class(TObject)
      public
        // Types publiques
        /// <summary>listes des options des Entêtes d'une source CSV</summary>
        /// <remarks>Indique si l'Entête est autorisé sans être obligatoire, obligatoire, présent, ...</remarks>
        type
          THeaderState = (csvhsNone, csvhsAllowed, csvhsRequired, csvhsPotential, csvhsFound, csvhsMissing, csvhsIncomplete);
      private
        // Membres privés
        FLineCount: Integer;
        FLineOfColumnName: Integer;
        FState: THeaderState;
      public
        // Constructeurs
        constructor Create();
     
        // Méthodes
        /// <summary>Ajoute un contenu brut d'une ligne pouvant faire parti des entêtes</summary>
        procedure Add(RawHeader: TSLTETLImportCSVRawHeader);
        /// <summary>Efface le Nombre de Ligne et le contenu brut des entêtes</summary>
        procedure Clear();
     
        // Propriétés
        /// <summary>Nombre de Ligne de l'Entête</summary>
        property LineCount: Integer read FLineCount write FLineCount;
        /// <summary>Indice de Ligne de l'Entête contenant les noms de Colonnes</summary>
        property LineOfColumnName: Integer read FLineOfColumnName write FLineOfColumnName;
        /// <summary>Indique si l'Entête est autorisé sans être obligatoire, obligatoire, présent, ...</summary>
        property State: THeaderState read FState write FState;
     end;
     
    implementation
     
    uses System.Math, System.StrUtils,
      SLT.Common.SysUtilsEx;
     
    const
      ERR_EMPTY_COLUMN_NAME_NOT_ALLOWED = 'Le nom de la colonne d''une source CSV est obligatoire, il ne peut pas être vide';
      ERR_DUPLICATED_COLUMN_NAME_NOT_ALLOWED_FMT = 'La colonne "%s" ne peut pas être ajoutée plusieurs fois pour une même source CSV';
      ERR_NO_ENCODING = 'Le format d''encodage de la source CSV doit être défini';
     
    { TSLTETLImportCSV }
     
    //--------------------------------------------
    constructor TSLTETLImportCSV.Create();
    begin
      inherited Create();
     
      // Les Interfaces sont gérées par compteur de référence.
      FSourceReader := TSLTETLImportCSVSourceReader.Create();
      FSourceChecker := TSLTETLImportCSVSourceChecker.Create();
      FSourceDispatcher := TSLTETLImportCSVSourceDispatcher.Create();
      FDestinationDispatcher := TSLTETLImportCSVDestinationDispatcher.Create();
    end;
     
    //--------------------------------------------
    destructor TSLTETLImportCSV.Destroy();
    begin
      // Les Interfaces sont gérées par compteur de référence,
      // L'affectation à nil doit les libérer (si plus aucun autre objet les utilises, ce qui DOIT être le cas)
      FDestinationDispatcher := nil;
      FSourceDispatcher := nil;
      FSourceReader := nil;
      FSourceChecker := nil;
     
      inherited Destroy();
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSV.DispatchLineEventHandler(Sender: TObject; const ALine: TStringDynArray);
    begin
      ProcessCSVLine(ALine);
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetCSVDestinationDispatcher(): ISLTETLImportCSVDestinationDispatcher;
    begin
      // Même si l'on choisi de redéfinir DestinationDispatcher, il doit implémenter ISLTETLImportCSVDestinationDispatcher en plus de ISLTETLImportDestinationDispatcher
      if not Supports(DestinationDispatcher, ISLTETLImportCSVDestinationDispatcher, Result) then
        raise ESTLIntfCastError.Create(DestinationDispatcher, ISLTETLImportCSVDestinationDispatcher); // Pseudo-Assert Exception !
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetCSVSourceReader(): ISLTETLImportCSVSourceReader;
    begin
      // Même si l'on choisi de redéfinir SourceReader, il doit implémenter ISLTETLImportCSVSourceReader en plus de ISLTETLImportSourceReader
      if not Supports(SourceReader, ISLTETLImportCSVSourceReader, Result) then
        raise ESTLIntfCastError.Create(SourceReader, ISLTETLImportCSVSourceReader); // Pseudo-Assert Exception !
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetDestinationDispatcher: ISLTETLImportDestinationDispatcher;
    begin
      Result := FDestinationDispatcher;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetSourceDispatcher(): ISLTETLImportSourceDispatcher;
    begin
      Result := FSourceDispatcher;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetSourceChecker(): ISLTETLImportSourceChecker;
    begin
      Result := FSourceChecker;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetSourceReader(): ISLTETLImportSourceReader;
    begin
      Result := FSourceReader;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.GetStreamReader(): ISLTETLImportSourceStreamReader;
    begin
      // Même si l'on choisi de redéfinir SourceReader, il doit implémenter ISLTETLImportSourceStreamReader en plus de ISLTETLImportSourceReader
      if not Supports(FSourceReader, ISLTETLImportSourceStreamReader, Result) then
        raise ESTLIntfCastError.Create(SourceReader, ISLTETLImportSourceStreamReader); // Pseudo-Assert Exception !
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSV.Import(): Boolean;
    begin
      CSVSourceReader.Clear();
     
      if not Supports(CSVSourceReader.Data, ISLTCSVReader) then
        raise ESTLIntfCastError.Create(CSVSourceReader.Data, ISLTCSVReader); // Pseudo-Assert Exception !
     
      InitializeCSVFormat(CSVSourceReader);
      CSVDestinationDispatcher.OnDispatch := DispatchLineEventHandler;
     
      Result := inherited Import();
    end;
     
    { TSLTETLImportCSVSourceChecker }
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceChecker.CheckHeaders(ASourceReader: ISLTETLImportCSVSourceReader): Boolean;
     
      //--------------------------------------------------------------------------
      function MakeRawHeaderFromColumns(AColumns: TSLTETLImportCSVColumns): TSLTETLImportCSVRawHeader;
      var
        I: Integer;
      begin
        SetLength(Result, AColumns.Count);
        for I := 0 to AColumns.Count - 1 do
          Result[I] := AColumns[I].ColumnName;
      end;
     
      //--------------------------------------------------------------------------
      function FindColumnsDefinition(const ARawHeaders: TSLTETLImportCSVRawHeaders; AColumns: TSLTETLImportCSVColumns; out AColumnDefinitionIndex: Integer): Boolean;
      var
        iRH: Integer;
        ColFoundCount, ColIgnoredCount: Integer;
        iCol: Integer;
      begin
        // Cherche parmi les lignes d'entête, la ligne décrivant les colonnes
        Result := False;
        for iRH := Low(ARawHeaders) to High(ARawHeaders) do
        begin
          if Length(ARawHeaders[iRH]) > 0 then
          begin
            // Cherche les colonnes attendues dans les lignes d'entête
            ColFoundCount := 0;
            ColIgnoredCount := 0;
            for iCol := 0 to AColumns.Count - 1 do
            begin
              // Seules les colonnes typées sont intéressantes, les autres sont considérées optionnelles
              if AColumns[iCol].ColumnType <> varEmpty then
              begin
                if System.StrUtils.MatchText(AColumns[iCol].ColumnName, ARawHeaders[iRH]) then
                  Inc(ColFoundCount);
              end
              else
                Inc(ColIgnoredCount);
            end;
     
            // Si l'on a trouvé des colonnes
            if ColFoundCount > 0 then
            begin
              AColumnDefinitionIndex := iRH;
              if (ColFoundCount + ColIgnoredCount) = AColumns.Count then
              begin
                Result := True;
                AColumnDefinitionIndex := iRH;
                Exit;
              end;
            end;
          end;
        end;
      end;
     
      //--------------------------------------------------------------------------
      procedure MakeColumnsFromRawHeader(const ARawHeader: TSLTETLImportCSVRawHeader; AColumns: TSLTETLImportCSVColumns);
      var
        I: Integer;
        ColName: string;
      begin
        for I := Low(ARawHeader) to High(ARawHeader) do
        begin
          ColName := ARawHeader[i];
          if ColName = '' then
            ColName := 'UNNAMED_COL_' + IntToStr(I);
     
          with AColumns.AddColumn(ColName) do
          begin
            ColumnType := varUString;
          end;
        end;
      end;
     
     
    var
      RawHeaders: TSLTETLImportCSVRawHeaders;
      ColumnDefinitionFound: Boolean;
      ColumnDefinitionAsExpected: Boolean;
      ColumnDefinitionIndex: Integer;
    begin
      Result := False;
      ColumnDefinitionAsExpected := False;
     
      // Commence par lire l'entête (si il existe)
      if ReadHeaders(ASourceReader, RawHeaders) then
      begin
        // Il y au moins autant de ligne que doit en contenir l'entête
        // Soit c'est un entête
        // Soit c'est les 1ères lignes de données
        if ASourceReader.FoundHeader.State = csvhsPotential then
        begin
          // Cherche la ligne d'entête prévue
          ColumnDefinitionIndex := ASourceReader.ExpectedHeader.LineOfColumnName;
          ColumnDefinitionFound := FindColumnsDefinition(RawHeaders, ASourceReader.ExpectedColumns, ColumnDefinitionIndex);
          ASourceReader.FoundHeader.LineOfColumnName := ColumnDefinitionIndex;
          if (ASourceReader.ExpectedHeader.LineOfColumnName >= 0) and (ASourceReader.FoundHeader.LineOfColumnName <> ASourceReader.ExpectedHeader.LineOfColumnName) then
            FCheckMessages.Add(Format('La description des colonnes a été trouvée en ligne (%d) au lieu de la ligne prévue (%d)', [ASourceReader.FoundHeader.LineOfColumnName, ASourceReader.ExpectedHeader.LineOfColumnName]));
     
          // Si la ligne de description des colonnes est trouvée, on charge ces colonnes depuis cette description
          if ColumnDefinitionFound then
          begin
            MakeColumnsFromRawHeader(RawHeaders[ColumnDefinitionIndex], ASourceReader.FoundColumns);
            ASourceReader.FoundHeader.State := csvhsFound;
            Result := True;
          end
          else
          begin
            // Si l'entête est obligatoire, c'est une erreur
            // Soit on a eu une correspondance partielle et donc l'entête est incomplet
            // Soit on a aucune correspondance, c'est que l'entête est manquant
            // Si l'entête est optionnel, c'est soit normal soit une erreur
            // Soit on a eu une correspondance partielle et donc l'entête est incomplet
            // Soit on a aucune correspondance, c'est que l'entête n'est pas présent
            if ColumnDefinitionIndex >= 0 then
            begin
              ASourceReader.FoundHeader.State := csvhsIncomplete;
            end
            else
            begin
              if ASourceReader.ExpectedHeader.State = csvhsRequired then
                ASourceReader.FoundHeader.State := csvhsMissing
              else
              begin
                ASourceReader.FoundHeader.State := csvhsNone;
                ColumnDefinitionAsExpected := True;
              end;
            end;
          end;
        end
        else
      end
      else
        // Pas d'entête, si aucun n'était attendu, ce n'est pas un problème
        // On considère les colonnes prévues comme des colonnes présentes dans l'ordre prévue
        if ASourceReader.FoundHeader.State = csvhsNone then
          ColumnDefinitionAsExpected := True;
     
      if ColumnDefinitionAsExpected then
      begin
        ASourceReader.FoundColumns.CopyFrom(ASourceReader.ExpectedColumns);
        Result := True;
      end;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceChecker.CheckSource(ASourceReader: ISLTETLImportSourceReader): Boolean;
    var
      lSourceReader: ISLTETLImportCSVSourceReader;
    begin
      // TODO -oSPT -cImplémentation : Ecrire TSLTETLImportCSVSourceChecker.CheckSource
      Result := False;
     
      if IsCSVFile(ASourceReader) then
      begin
        if Supports(ASourceReader, ISLTETLImportCSVSourceReader, lSourceReader) then
        begin
          lSourceReader.FoundHeader.Clear();
          lSourceReader.FoundColumns.Clear();
     
          if not Assigned(lSourceReader.Data.FormatSpecification.Encoding) then
          begin
            FCheckMessages.Add(ERR_NO_ENCODING);
            Exit;
          end;
     
          Result := CheckHeaders(lSourceReader);
     
          // ??
        end;
      end;
    end;
     
    //--------------------------------------------
    constructor TSLTETLImportCSVSourceChecker.Create();
    begin
      inherited Create();
     
      FCheckMessages := TStringList.Create();
     
    end;
     
    //--------------------------------------------
    destructor TSLTETLImportCSVSourceChecker.Destroy();
    begin
      FreeAndNil(FCheckMessages);
     
      inherited Destroy();
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceChecker.GetCheckMessages: TStrings;
    begin
      Result := FCheckMessages;
    end;
     
    //--------------------------------------------
    class function TSLTETLImportCSVSourceChecker.IsCSVFile(ASourceReader: ISLTETLImportSourceReader): Boolean;
    var
      StreamReaderIntf: ISLTETLImportSourceStreamReader;
    begin
      Result := Assigned(ASourceReader)
        and Supports(ASourceReader, ISLTETLImportSourceStreamReader, StreamReaderIntf)
        and IsCSVStream(StreamReaderIntf.Source);
    end;
     
    //--------------------------------------------
    class function TSLTETLImportCSVSourceChecker.IsCSVFileName(AFileName: TFileName): Boolean;
    begin
      Result := SameText(ExtractFileExt(AFileName), '.CSV');
    end;
     
    //--------------------------------------------
    class function TSLTETLImportCSVSourceChecker.IsCSVStream(AStream: TStream): Boolean;
    begin
      Result := ((AStream is TFileStream) and IsCSVFileName(TFileStream(AStream).FileName))
        or (AStream is TMemoryStream);
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceChecker.ReadHeaders(ASourceReader: ISLTETLImportCSVSourceReader; out ARawHeaders: TSLTETLImportCSVRawHeaders): Boolean;
    var
      I: Integer;
      L: Integer;
      CSVLine: TStringDynArray;
    begin
      Result := False;
      SetLength(ARawHeaders, 0);
     
      if (ASourceReader.ExpectedHeader.State in [csvhsAllowed, csvhsRequired]) then
      begin
        L := ASourceReader.ExpectedHeader.LineCount;
     
        if L > 0 then
        begin
          SetLength(ARawHeaders, L);
          with ASourceReader.Data do
          begin
            Open();
            try
              I := 0;
              while not EOF and (I < L) do
              begin
                if ReadNextLine(CSVLine) then
                begin
                  ARawHeaders[I] := CSVLine;
                  ASourceReader.FoundHeader.Add(CSVLine);
                end;
     
                Inc(I);
              end;
            finally
              Close();
            end;
     
            if ASourceReader.FoundHeader.LineCount > 0 then
            begin
              if ASourceReader.FoundHeader.LineCount = ASourceReader.ExpectedHeader.LineCount then
              begin
                case ASourceReader.ExpectedHeader.State of
                  csvhsRequired : ASourceReader.FoundHeader.State := csvhsPotential;
                  csvhsAllowed : ASourceReader.FoundHeader.State := csvhsPotential;
                end
              end
              else
                ASourceReader.FoundHeader.State := csvhsIncomplete;
     
              Result := True;
            end
            else
              if ASourceReader.ExpectedHeader.State = csvhsRequired then
                ASourceReader.FoundHeader.State := csvhsMissing;
          end;
        end;
      end;
    end;
     
    { TSLTETLImportCSVSourceReader }
     
    //--------------------------------------------
    constructor TSLTETLImportCSVSourceReader.Create();
    begin
      inherited Create();
     
      FExpectedColumns := TSLTETLImportCSVColumns.Create();
      FExpectedColumns.Duplicates := dupError; // Pas de colonne en double dans celle attendue
      FExpectedHeader := TSLTETLImportCSVHeader.Create();
     
      FFoundColumns := TSLTETLImportCSVColumns.Create();
      FFoundColumns.Duplicates := dupAccept; // Une colonne en double dans les colonnes trouvées est possible et doit être gérée comme une anomalie du CSV
      FFoundHeader := TSLTETLImportCSVHeader.Create();
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVSourceReader.Clear();
    begin
      FExpectedColumns.Clear();
      FExpectedHeader.Clear();
      FFoundColumns.Clear();
      FFoundHeader.Clear();
    end;
     
    //--------------------------------------------
    class constructor TSLTETLImportCSVSourceReader.Create();
    begin
      // Par défaut, on utilise l'implémentation naturelle du ISLTCSVReader : le TSLTCSVReader
      DefaultCSVReaderClass := TSLTCSVReader;
    end;
     
    //--------------------------------------------
    destructor TSLTETLImportCSVSourceReader.Destroy();
    begin
      FreeAndNil(FFoundHeader);
      FreeAndNil(FFoundColumns);
     
      FreeAndNil(FExpectedHeader);
      FreeAndNil(FExpectedColumns);
     
      // Les Interfaces sont gérées par compteur de référence
      // Peu importe si il a été créé en interne ou fourni par la propriété Data,
      // la libération sera faite dès que la référence ne sera plus utilisé
      FReaderIntf := nil;
     
      inherited Destroy();
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.GetData(): ISLTCSVReader;
    begin
      if not Assigned(FReaderIntf) then
        FReaderIntf := ReaderIntfFactory();
     
      Result := FReaderIntf;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.GetExpectedColumns(): TSLTETLImportCSVColumns;
    begin
      Result := FExpectedColumns;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.GetExpectedHeader(): TSLTETLImportCSVHeader;
    begin
      Result := FExpectedHeader;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.GetFoundColumns(): TSLTETLImportCSVColumns;
    begin
      Result := FFoundColumns;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.GetFoundHeader(): TSLTETLImportCSVHeader;
    begin
      Result := FFoundHeader;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.GetSource(): TStream;
    begin
      Result := FReaderIntf.Stream;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceReader.ReaderIntfFactory(): ISLTCSVReader;
    var
      Impl: TObject;
    begin
      // Attention, cette pseudo-factory n'est pas thread safe,
      // Veillez à gérer l'instance avec un verrou si cela s'avère nécessaire dans une version redéfinie de GetData qui appel ReaderIntfFactory
      Impl := DefaultCSVReaderClass.Create();
     
      // La classe par défaut utilisée comme Implémentation de ISLTCSVReader doit évidemment fourni son support !
      // L'utilisation d'une TClass est très ouverte mais nécessite que l'on cast l'instance vers son interface
      if not Supports(Impl, ISLTCSVReader, Result) then
        raise ESTLIntfCastError.Create(Impl, ISLTCSVReader); // Pseudo-Assert Exception !
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVSourceReader.SetData(const Value: ISLTCSVReader);
    begin
      // Les Interfaces sont gérées par compteur de référence
      // Si l'on substitue une implémentation par une autre,
      // cela gère naturellement les libérations dès que les références ne sont plus utilisées
      if FReaderIntf <> Value then
        FReaderIntf := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVSourceReader.SetSource(const Value: TStream);
    begin
      FReaderIntf.Stream := Value;
    end;
     
    { TSLTETLImportCSVColumns }
     
    //--------------------------------------------
    function TSLTETLImportCSVColumns.AddColumn(const AColumnName: string): TSLTETLImportCSVColumn;
    var
      ResultIndex: Integer;
    begin
      if AColumnName = '' then
        raise ESLTETLImportCSVError.Create(ERR_EMPTY_COLUMN_NAME_NOT_ALLOWED);
     
      // Colonne en double :
      // - Interdite pour les Expected
      // - Autorisée pour les Found
      if Duplicates <> dupAccept then
      begin
        ResultIndex := IndexOfColumnName(AColumnName);
        if ResultIndex >= 0 then
        begin
          if Duplicates = dupIgnore then
          begin
            Result := Items[ResultIndex];
            Exit; // On ignore l'ajout !
          end
          else
            raise ESLTETLImportCSVError.CreateFmt(ERR_DUPLICATED_COLUMN_NAME_NOT_ALLOWED_FMT, [AColumnName])
        end;
      end;
     
      // Crée une nouvelle colonne nommée et utilise l'index dans la liste comme position par défaut
      Result := TSLTETLImportCSVColumn.Create(AColumnName);
      Result.ColumnPosition := FColumnList.Add(Result);
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVColumns.AddColumn(const AColumnName: string; AColumnType: TVarType): TSLTETLImportCSVColumn;
    begin
      Result := AddColumn(AColumnName);
      Result.ColumnType := AColumnType;
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVColumns.Clear();
    begin
      FColumnList.Clear();
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVColumns.CopyFrom(ASource: TSLTETLImportCSVColumns);
    var
      I: Integer;
      S: TSLTETLImportCSVColumn;
    begin
      for I := 0 to ASource.Count - 1 do
      begin
        S := ASource[I];
        AddColumn(S.ColumnName).CopyFrom(S);
      end;
    end;
     
    //--------------------------------------------
    constructor TSLTETLImportCSVColumns.Create();
    begin
      inherited Create();
     
      // Les Items de FColumnList seront libérés implicitement par FColumnList
      FColumnList := TColumnList.Create(True);
      FDuplicates := dupAccept;
    end;
     
    //--------------------------------------------
    destructor TSLTETLImportCSVColumns.Destroy();
    begin
      FreeAndNil(FColumnList);
     
      inherited Destroy();
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVColumns.GetCount(): Integer;
    begin
      Result := FColumnList.Count;
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVColumns.GetItems(Index: Integer): TSLTETLImportCSVColumn;
    begin
      Result := FColumnList.Items[Index];
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVColumns.IndexOfColumnName(const AColumnName: string): Integer;
    begin
      for Result := 0 to Count - 1 do
        if SameText(Items[Result].ColumnName, AColumnName) then
          Exit;
     
      Result := -1;
    end;
     
    { TSLTETLImportCSVColumn }
     
    //--------------------------------------------
    procedure TSLTETLImportCSVColumn.CopyFrom(ASource: TSLTETLImportCSVColumn);
    begin
      // Tout sauf ColumnName !
      FColumnType := ASource.ColumnType;
      FColumnPosition := ASource.ColumnPosition;
      FColumnSizeMax := ASource.ColumnSizeMax;
      FColumnSizeMin := ASource.ColumnSizeMin;
    end;
     
    //--------------------------------------------
    constructor TSLTETLImportCSVColumn.Create(const AColumnName: string);
    begin
      inherited Create();
     
      FColumnName := AColumnName;
      FColumnPosition := -1; // Indique une position indéterminée !
      FColumnSizeMin := 0; // Vide autorisé
      FColumnSizeMax := MaxInt; // Pas de limite par défaut (presque)
    end;
     
    { TSLTETLImportCSVHeader }
     
    //--------------------------------------------
    procedure TSLTETLImportCSVHeader.Add(RawHeader: TSLTETLImportCSVRawHeader);
    begin
      { TODO -oSPT -cSécurité Donnée : Gestion des entêtes CSV }
      Inc(FLineCount);
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVHeader.Clear();
    begin
      State := csvhsNone;
      FLineCount := 0;
      FLineOfColumnName := -1;
    end;
     
    //--------------------------------------------
    constructor TSLTETLImportCSVHeader.Create();
    begin
      inherited Create();
     
      Clear();
    end;
     
    { TSLTETLImportCSVDispatcher }
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceDispatcher.DispacthCSVLine(ASourceReader: ISLTETLImportSourceReader; out ACSVLine: TStringDynArray): Boolean;
    var
      lSourceReader: ISLTETLImportCSVSourceReader;
    begin
      Result := False;
      if Supports(ASourceReader, ISLTETLImportCSVSourceReader, lSourceReader) then
        Result := lSourceReader.Data.ReadNextLine(ACSVLine);
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVSourceDispatcher.DispatchSourceToDestination(ASourceReader: ISLTETLImportSourceReader; ADestinationDispatcher: ISLTETLImportDestinationDispatcher): Boolean;
    var
      lSourceReader: ISLTETLImportCSVSourceReader;
      HeaderCount: Integer;
    begin
      Result := False;
     
      // La boucle qui lit le CSV
      if Supports(ASourceReader, ISLTETLImportCSVSourceReader, lSourceReader) then
      begin
        with lSourceReader.Data do
        begin
          Open();
          try
            if lSourceReader.FoundHeader.State = csvhsFound then
            begin
              HeaderCount := lSourceReader.FoundHeader.LineCount;
              while not EOF and (HeaderCount > 0) do
              begin
                Next();
                Dec(HeaderCount);
              end;
            end;
     
            FormatSpecification.ExpectedColumnCount := lSourceReader.ExpectedColumns.Count;
     
            while not EOF do
            begin
              if not ADestinationDispatcher.DispatchDestinationFromSource(ASourceReader, Self) then
                Exit;
            end;
     
            Result := True;
          finally
            Close();
          end;
        end;
      end;
    end;
     
    { TSLTETLImportCSVDestinationDispatcher }
     
    //--------------------------------------------
    function TSLTETLImportCSVDestinationDispatcher.DispatchDestinationFromSource(ASourceReader: ISLTETLImportSourceReader; ASourceDispatcher: ISLTETLImportSourceDispatcher): Boolean;
    var
      lSourceDispatcher: ISLTETLImportCSVSourceDispatcher;
      CSVLine: TStringDynArray;
    begin
      Result := False;
      if Supports(ASourceDispatcher, ISLTETLImportCSVSourceDispatcher, lSourceDispatcher) then
      begin
        if lSourceDispatcher.DispacthCSVLine(ASourceReader, CSVLine) then
        begin
          // Exclusion des lignes vides
          if Length(CSVLine) > 0 then
            DoDispatch(CSVLine);
     
          Result := True;
        end;
      end;
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVDestinationDispatcher.DoDispatch(const ACSVLine: TStringDynArray);
    begin
      if Assigned(FOnDispatch) then
        FOnDispatch(Self, ACSVLine);
    end;
     
    //--------------------------------------------
    function TSLTETLImportCSVDestinationDispatcher.GetOnDispatch: TSLTETLImportCSVDestinationDispatchEvent;
    begin
      Result := FOnDispatch;
    end;
     
    //--------------------------------------------
    procedure TSLTETLImportCSVDestinationDispatcher.SetOnDispatch(const Value: TSLTETLImportCSVDestinationDispatchEvent);
    begin
      FOnDispatch := Value;
    end;
     
     
     
    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
     
    //-----------------------------------------------------------------------------)
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Copyright ou © ou Copr. "SLT Solutions", (2006)                            -
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *                                                                             -
     *                                                                             -
     * 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.ETL.Import;
     
    interface
     
    uses System.Classes, System.SysUtils;
     
    type
      { Forward class declarations }
      TSLTETLAbstractImport = class;
      TSLTETLAbstractImportFromStream = class;
      TSLTETLAbstractImportFromFile = class;
      ISLTETLImportSourceReader = interface;
      ISLTETLImportSourceStreamReader = interface;
      ISLTETLImportSourceFileReader = interface;
      ISLTETLImportSourceChecker = interface;
      ISLTETLImportSourceDispatcher = interface;
      ISLTETLImportDestinationDispatcher = interface;
     
      /// <summary>Importe des données en déléguant à différentes interfaces la responsabilité de chaque phase</summary>
      TSLTETLAbstractImport = class abstract(TObject)
      protected
        // Accesseurs Abstraits
        /// <summary>Fournit une implémentation de ISLTETLImportSourceReader chargée de lire le contenu de la Source</summary>
        function GetSourceReader(): ISLTETLImportSourceReader; virtual; abstract;
        /// <summary>Fournit une implémentation de ISLTETLImportSourceChecker chargée de vérifier le contenu de la Source</summary>
        function GetSourceChecker(): ISLTETLImportSourceChecker; virtual; abstract;
        /// <summary>Fournit une implémentation de ISLTETLImportSourceDispatcher chargée de fournir les données à importer depuis de la Source</summary>
        function GetSourceDispatcher(): ISLTETLImportSourceDispatcher; virtual; abstract;
        /// <summary>Fournit une implémentation de ISLTETLImportDestinationDispatcher chargée d'intégrer les données</summary>
        function GetDestinationDispatcher(): ISLTETLImportDestinationDispatcher; virtual; abstract;
      public
        // Méthodes Virtuelles Redéfinissables
        function CheckSource(): Boolean; virtual;
        function DispatchSource(): Boolean; virtual;
        function Import(): Boolean; virtual; abstract;
     
        // Propriétés
        /// <summary>Fournit l'interface responsable de la lecture de la source</summary>
        property SourceReader: ISLTETLImportSourceReader read GetSourceReader;
        /// <summary>Fournit l'interface responsable de la vérification la conformité de la source</summary>
        property SourceChecker: ISLTETLImportSourceChecker read GetSourceChecker;
        /// <summary>Fournit l'interface responsable de fournir les données à importer depuis de la Source</summary>
        property SourceDispatcher: ISLTETLImportSourceDispatcher read GetSourceDispatcher;
        /// <summary>Fournit l'interface responsable de fournir les données à importer depuis de la Source</summary>
        property DestinationDispatcher: ISLTETLImportDestinationDispatcher read GetDestinationDispatcher;
      end;
     
      /// <summary>Importe des données en déléguant à différentes interfaces la responsabilité de chaque phase</summary>
      TSLTETLAbstractImportFromStream = class abstract(TSLTETLAbstractImport)
      private
        // Membres privés
        FSource: TStream;
        FOwnsSource: Boolean;
      protected
        // Accesseurs
        function GetSource(): TStream;
        procedure SetSource(const Value: TStream);
        // Accesseurs Abstraits
        /// <summary>Fournit une implémentation de ISLTETLImportSourceStreamReader chargée de lire le contenu de la Source à partir d'un flux générique</summary>
        function GetStreamReader(): ISLTETLImportSourceStreamReader; virtual; abstract;
      public
        // Constructeurs
        destructor Destroy(); override;
     
        // Méthodes Virtuelles Redéfinissables
        function Import(): Boolean; override;
     
        // Propriétés
        /// <summary>Source des données à importer</summary>
        property Source: TStream read GetSource write SetSource;
        /// <summary>Indique si Source sera libérée par l'instance de TSLTETLAbstractImport qui l'utilise</summary>
        property OwnsSource: Boolean read FOwnsSource write FOwnsSource;
        /// <summary>Fournit l'interface responsable de la lecture de la source à partir d'un flux générique</summary>
        property StreamReader: ISLTETLImportSourceStreamReader read GetStreamReader;
      end;
     
      /// <summary>Importe des données en déléguant à différentes interfaces la responsabilité de chaque phase</summary>
      TSLTETLAbstractImportFromFile = class abstract(TSLTETLAbstractImport)
      private
        // Membres privés
        FSource: TFileName;
        FOwnsSource: Boolean;
      protected
        // Accesseurs
        function GetSource(): TFileName;
        procedure SetSource(const Value: TFileName);
        // Accesseurs Abstraits
        /// <summary>Fournit une implémentation de ISLTETLImportSourceFileReader chargée de lire le contenu de la Source depuis un fichier</summary>
        function GetFileReader(): ISLTETLImportSourceFileReader; virtual; abstract;
      public
        // Méthodes Virtuelles Redéfinissables
        function Import(): Boolean; override;
     
        // Propriétés
        /// <summary>Source des données à importer</summary>
        property Source: TFileName read GetSource write SetSource;
        /// <summary>Indique si Source sera libérée par l'instance de TSLTETLAbstractImport qui l'utilise</summary>
        property OwnsSource: Boolean read FOwnsSource write FOwnsSource;
        /// <summary>Fournit l'interface responsable de la lecture de la source à partir d'un fichier</summary>
        property FileReader: ISLTETLImportSourceFileReader read GetFileReader;
      end;
     
      /// <summary>Cette interface est l'ancêtre des interfaces permettant de lire la source de données</summary>
      ISLTETLImportSourceReader = interface
        ['{6A730F95-835C-45D8-9BBB-29B314E4A505}']
      end;
     
      /// <summary>Cette interface permet de lire la source de données à partir d'un flux générique</summary>
      ISLTETLImportSourceStreamReader = interface(ISLTETLImportSourceReader)
        ['{EB61CFA8-FF07-43C4-BD6B-C4FC072FCC81}']
     
        // Accesseurs
        function GetSource(): TStream;
        procedure SetSource(const Value: TStream);
     
        // Propriétés
        /// <summary>Source des données à importer</summary>
        property Source: TStream read GetSource write SetSource;
      end;
     
      /// <summary>Cette interface permet de lire la source de données à partir d'un fichier</summary>
      ISLTETLImportSourceFileReader = interface(ISLTETLImportSourceReader)
        ['{163862B7-253E-4667-8176-3E15082354F8}']
     
        // Accesseurs
        function GetSource(): TFileName;
        procedure SetSource(const Value: TFileName);
     
        // Propriétés
        /// <summary>Source des données à importer</summary>
        property Source: TFileName read GetSource write SetSource;
      end;
     
      /// <summary>Cette interface permet de vérifier la conformité de la source</summary>
      ISLTETLImportSourceChecker = interface
        ['{53A47C92-D9E9-4191-AC65-5A8E04041808}']
        // Accesseurs
        function GetCheckMessages(): TStrings;
     
        // Méthodes
        /// <summary>Vérifie la conformité de la source</summary>
        function CheckSource(ASourceReader: ISLTETLImportSourceReader): Boolean;
     
        // Propriétés
        /// <summary>Contient les Messages générés durant la vérification</summary>
        property CheckMessages: TStrings read GetCheckMessages;
      end;
     
      /// <summary>Cette interface utilise la source et en réparti des données à importer</summary>
      ISLTETLImportSourceDispatcher = interface
        ['{B84BA3BD-150A-434D-B5E8-7C61F1042459}']
     
        // Méthodes
        /// <summary>Utilise la source et en réparti les données à importer</summary>
        function DispatchSourceToDestination(ASourceReader: ISLTETLImportSourceReader; ADestinationDispatcher: ISLTETLImportDestinationDispatcher): Boolean;
      end;
     
      /// <summary>Cette interface intègre les données réparties depuis la Source</summary>
      ISLTETLImportDestinationDispatcher = interface
        ['{EF46E9DB-C161-409D-AF79-65B05CB90F07}']
     
        // Méthodes
        /// <summary>Intègre les données</summary>
        function DispatchDestinationFromSource(ASourceReader: ISLTETLImportSourceReader; ASourceDispatcher: ISLTETLImportSourceDispatcher): Boolean;
      end;
     
     
    implementation
     
    { TSLTETLAbstractImport }
     
    //--------------------------------------------
    function TSLTETLAbstractImport.CheckSource(): Boolean;
    begin
      Result := SourceChecker.CheckSource(SourceReader);
    end;
     
    //--------------------------------------------
    function TSLTETLAbstractImport.DispatchSource(): Boolean;
    begin
      Result := SourceDispatcher.DispatchSourceToDestination(SourceReader, DestinationDispatcher);
    end;
     
    { TSLTETLAbstractImportFromStream }
     
    //--------------------------------------------
    destructor TSLTETLAbstractImportFromStream.Destroy();
    begin
      if OwnsSource and Assigned(FSource) then
        FreeAndNil(FSource);
     
      inherited Destroy();
    end;
     
    //--------------------------------------------
    function TSLTETLAbstractImportFromStream.Import(): Boolean;
    begin
      StreamReader.Source := FSource;
      try
        Result := CheckSource() and DispatchSource();
      finally
        StreamReader.Source := nil;
      end;
    end;
     
    //--------------------------------------------
    function TSLTETLAbstractImportFromStream.GetSource(): TStream;
    begin
      Result := FSource;
    end;
     
    //--------------------------------------------
    procedure TSLTETLAbstractImportFromStream.SetSource(const Value: TStream);
    begin
      if FSource <> Value then
        FSource := Value;
    end;
     
    { TSLTETLAbstractImportFromFile }
     
    //--------------------------------------------
    function TSLTETLAbstractImportFromFile.Import(): Boolean;
    begin
      FileReader.Source := FSource;
      try
        Result := CheckSource() and DispatchSource();
      finally
        FileReader.Source := '';
      end;
    end;
     
    //--------------------------------------------
    function TSLTETLAbstractImportFromFile.GetSource(): TFileName;
    begin
      Result := FSource;
    end;
     
    //--------------------------------------------
    procedure TSLTETLAbstractImportFromFile.SetSource(const Value: TFileName);
    begin
      if FSource <> Value then
        FSource := Value;
    end;
     
    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
    //-----------------------------------------------------------------------------)
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Copyright ou © ou Copr. "SLT Solutions", (2006)                            -
     *  contributeur : ShaiLeTroll (2007) - Utilisation de Explode en parallèle de ExplodeLazy
     *  contributeur : ShaiLeTroll (2012) - Renommage Fichier et Correction XE2    -
     *  contributeur : ShaiLeTroll (2012) - Documentation Insight                  -
     *                                                                             -
     * 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.CSVUtils;
     
    interface
     
    uses System.SysUtils, System.Classes, System.Types,
      SLT.Common.TextFileReader;
     
    type
      { Forward class declarations }
      ISLTCSVReader = interface;
      ISLTCSVFormatSpecification = interface;
      TSLTCSVReader = class;
     
      /// <summary>Erreur de base liée au fichier CSV</summary>
      ESLTCSVError = class(Exception);
      /// <summary>Erreur de base liée à lecture de fichier CSV</summary>
      ESLTCSVReaderError = class(ESLTCSVError);
      /// <summary>Erreur de base liée à la génération de fichier CSV</summary>
      ESLTCSVWriterError = class(ESLTCSVError);
     
      /// <summary>listes des options disponibles gérées par le lecteur ou le générateur de fichier CSV</summary>
      TSLTCSVOption = (csvoAllowLineBreakInData);
      /// <summary>Options gérées par le lecteur ou le générateur de fichier CSV</summary>
      TSLTCSVOptions = set of TSLTCSVOption;
     
      /// <summary>Cette interface défini les prototypes nécessaires à lecture d'un fichier CSV</summary>
      ISLTCSVReader = interface
        ['{D3EF8442-1021-4B47-B0E8-6ED409870BFF}']
        // Méthodes
        procedure Open();
        procedure Close();
        function ReadNextLine(out ACSVLine: TStringDynArray): Boolean;
        procedure Next();
     
        // Accesseurs
        function GetStream(): TStream;
        procedure SetStream(const Value: TStream);
        function GetFormatSpecification(): ISLTCSVFormatSpecification;
        function GetEOF(): Boolean;
     
        // Propriétés
        /// <summary>Flux de données à lire en format CSV</summary>
        property Stream: TStream read GetStream write SetStream;
        /// <summary>Spécifications de format d'un fichier CSV/summary>
        property FormatSpecification: ISLTCSVFormatSpecification read GetFormatSpecification;
        /// <summary>Détermine si la position en cours du pointeur se trouve en fin de fichier CSV</summary>
        property EOF: Boolean read GetEOF;
      end;
     
      /// <summary>Cette interface défini les prototypes nécessaires fournissant les spécifications de format d'un fichier CSV</summary>
      ISLTCSVFormatSpecification = interface
        ['{5654659F-FA50-4752-95C6-D7DE6A84D2B3}']
        // Accesseurs
        function GetEncoding(): TEncoding;
        procedure SetEncoding(const Value: TEncoding);
        function GetUseCROnly(): Boolean;
        procedure SetUseCROnly(const Value: Boolean);
        function GetSeparator(): Char;
        procedure SetSeparator(const Value: Char);
        function GetQuote(): Char;
        procedure SetQuote(const Value: Char);
        function GetOptions(): TSLTCSVOptions;
        procedure SetOptions(const Value: TSLTCSVOptions);
        function GetExpectedColumnCount(): Integer;
        procedure SetExpectedColumnCount(const Value: Integer);
     
        // Propriétés
        /// <summary>Source des données à lire en format CSV</summary>
        property Encoding: TEncoding read GetEncoding write SetEncoding;
        /// <summary>Détermine le type de retour charriot : Soit [CR]LF (Windows\Linux) soit CR isolé (MacOS)</summary>
        property UseCROnly: Boolean read GetUseCROnly write SetUseCROnly;
        /// <summary>Détermine le séparateur du CSV</summary>
        /// <remarks>Exemple : Séparateur Point-Virgule ';' pour un CSV Français (Valeur par Défaut) et Séparateur Virgule ',' pour un CSV Anglais</remarks>
        property Separator: Char read GetSeparator write SetSeparator;
        /// <summary>Détermine le délimiteur du CSV, Séparateur Guillement '"' pour un CSV Français (Valeur par Défaut)</summary>
        /// <remarks>Si la Source CSV n'est pas sensé contenir de délimiteur, utilisez le caractère Zéro : Cela aura pour effet d'accélerer le découpage</remarks>
        property Quote: Char read GetQuote write SetQuote;
        /// <summary>Options gérées par le lecteur de fichier CSV</summary>
        property Options: TSLTCSVOptions read GetOptions write SetOptions;
        /// <summary>Nombre de colonnes attendues</summary>
        property ExpectedColumnCount: Integer read GetExpectedColumnCount write SetExpectedColumnCount;
      end;
     
      TSLTCSVReader = class(TInterfacedObject, ISLTCSVReader, ISLTCSVFormatSpecification)
      private
        // Membres Privés - Propriétés
        FStream: TStream;
        FEncoding: TEncoding;
        FUseCROnly: Boolean;
        FSeparator: Char;
        FQuote: Char;
        FOptions: TSLTCSVOptions;
        FExpectedColumnCount: Integer;
     
        // Membres Privés - Internes
        FTextReader: TSLTTextFileReader;
      public
        // Accesseurs - Implémentation de ISLTCSVReader
        function GetStream(): TStream;
        procedure SetStream(const Value: TStream);
        function GetFormatSpecification(): ISLTCSVFormatSpecification;
        function GetEOF(): Boolean;
     
        // Accesseurs - Implémentation de ISLTCSVFormatSpecification
        function GetEncoding(): TEncoding;
        procedure SetEncoding(const Value: TEncoding);
        function GetUseCROnly(): Boolean;
        procedure SetUseCROnly(const Value: Boolean);
        function GetSeparator(): Char;
        procedure SetSeparator(const Value: Char);
        function GetQuote(): Char;
        procedure SetQuote(const Value: Char);
        function GetOptions(): TSLTCSVOptions;
        procedure SetOptions(const Value: TSLTCSVOptions);
        function GetExpectedColumnCount(): Integer;
        procedure SetExpectedColumnCount(const Value: Integer);
     
        // Méthodes - Implémentation de ISLTCSVReader
        procedure Open();
        procedure Close();
        function ReadNextLine(out ACSVLine: TStringDynArray): Boolean;
        procedure Next();
      public
        // Constructeurs
        constructor Create();
        destructor Destroy(); override;
      end;
     
    implementation
     
    uses SLT.Common.StrUtilsEx;
     
    const
      ERR_UNSUPPORTED_LINEBREAK_IN_DATA = 'Cette implémentation ne supporte pas la présence de "Retour-Charriot" dans les données';
     
    { TSLTCSVReader }
     
    //--------------------------------------------
    procedure TSLTCSVReader.Close();
    begin
      FreeAndNil(FTextReader);
    end;
     
    //--------------------------------------------
    constructor TSLTCSVReader.Create();
    begin
      inherited Create();
     
      FSeparator := ';'; // Séparateur typique en Localisation Française
      FQuote := '"'; // Délimiteur typique en Localisation Française
    end;
     
    //--------------------------------------------
    destructor TSLTCSVReader.Destroy();
    begin
      Close();
     
      inherited Destroy();
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetEncoding(): TEncoding;
    begin
      Result := FEncoding;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetEOF(): Boolean;
    begin
      Result := Assigned(FTextReader) and FTextReader.EOF;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetExpectedColumnCount(): Integer;
    begin
      Result := FExpectedColumnCount;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetFormatSpecification(): ISLTCSVFormatSpecification;
    begin
      Result := Self;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetOptions(): TSLTCSVOptions;
    begin
      Result := FOptions;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetQuote(): Char;
    begin
      Result := FQuote;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetSeparator(): Char;
    begin
      Result := FSeparator;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetStream(): TStream;
    begin
      Result := FStream;
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.GetUseCROnly(): Boolean;
    begin
      Result := FUseCROnly;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.Next();
    begin
      FTextReader.ReadNextLine();
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.Open();
    begin
      FTextReader := TSLTTextFileReader.Create(FStream, FEncoding, True, False);
    end;
     
    //--------------------------------------------
    function TSLTCSVReader.ReadNextLine(out ACSVLine: TStringDynArray): Boolean;
    var
      BufLine: string;
      SepCount: Integer;
    begin
      BufLine := FTextReader.ReadNextLine();
     
      // Exclusion des lignes vides
      if BufLine <> '' then
      begin
        if FQuote = #0 then
          SepCount := ExplodeLazy(BufLine, ACSVLine, FSeparator)
        else
          SepCount := Explode(BufLine, ACSVLine, FSeparator, False, FQuote);
     
        // Le nombre de colonne attendu n'est fourni qu'après avoir lu l'entête
        // RFC 4180 - Common Format and MIME Type for Comma-Separated Values (CSV) Files
        // The last field in the record must not be followed by a comma = La dernière colonne ne se termine pas par un séparateur !
        // la présence d'un séparateur en fin de ligne peut provoquer l'oubli d'une dernière colonne vide
        // Si le nombre de séparateur est correct (Col-1), il manque donc la dernière colonne !
        if (FExpectedColumnCount > 0) and (SepCount > 0) and (SepCount = FExpectedColumnCount - 1) and (Length(ACSVLine) = SepCount) then
          SetLength(ACSVLine, FExpectedColumnCount);
      end;
     
      Result := True;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetEncoding(const Value: TEncoding);
    begin
      FEncoding := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetExpectedColumnCount(const Value: Integer);
    begin
      FExpectedColumnCount := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetOptions(const Value: TSLTCSVOptions);
    begin
      if csvoAllowLineBreakInData in Value then
        raise ESLTCSVReaderError.Create(ERR_UNSUPPORTED_LINEBREAK_IN_DATA);
     
      FOptions := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetQuote(const Value: Char);
    begin
      FQuote := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetSeparator(const Value: Char);
    begin
      FSeparator := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetStream(const Value: TStream);
    begin
      FStream := Value;
    end;
     
    //--------------------------------------------
    procedure TSLTCSVReader.SetUseCROnly(const Value: Boolean);
    begin
      FUseCROnly := Value;
    end;
     
    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
    //--------------------------------------------
    (*                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 ou © ou Copr. "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                  -
     *                                                                             -
     * 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.

  4. #4
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Merci pour ces informations.

    Ce qui m'intéresse dans le fait de charger cela dans un TFDMemTable, c'est de pouvoir faire des requêtes SQL sur mes données pour les trier, les modifier.
    Donc charger mon fichier avec des TStringList ne correspond pas à mes attentes.

    Dans tous les tests que je fais pour trouver le problème, je me suis aperçu qu'avant j'arrivé à charger ce fichier avec plus de 40 colonnes.
    Entre temps je n'ai pas touché cette partie du programme. C'est très étrange.

    Je continue mes recherche...

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 563
    Points : 25 165
    Points
    25 165
    Par défaut
    Tu charges dans la TStringList pour ensuite générer du SQL INSERT remplissant une table temporaire pour y faire du SQL

  6. #6
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Pourquoi pas.

    Le problème vient apparemment du fichier de données. J'en ai un qui passe sans problème et l'autre qui ne veut rien savoir.
    J'ai supprimé tous les caractères "bizarre" mais ça ne change rien. Ces 2 fichier on bien le même format ANSI.

    Je continue mes recherches...

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    OK. Du coup j'ai remplacé mon chargement de fichier par des TStringList puis insertion dans ma TFDMemTable et ça marche.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 20/04/2011, 15h26
  2. [MySQL] Importer les données d'un fichier CSV dans une base de données
    Par joueur dans le forum PHP & Base de données
    Réponses: 7
    Dernier message: 12/11/2008, 11h59
  3. chargement d'un fichier csv dans une base
    Par db2newbie dans le forum DB2
    Réponses: 2
    Dernier message: 21/01/2008, 22h29
  4. Réponses: 6
    Dernier message: 18/07/2006, 20h32
  5. Impossible d'importer un fichier csv dans une table sous MySQL
    Par manue85 dans le forum SQL Procédural
    Réponses: 5
    Dernier message: 20/04/2006, 12h06

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