u_c_text_file - John COLIBRI. | - mots clé: gestion de fichier ASCII - utilitaire
- logiciel utilisé: Windows 98, Delphi 5.0
- matériel utilisé: Pentium 500Mhz, 128 M de mémoire
- champ d'application: Delphi 1 à 6 sur Windows, Kylix
- niveau: débutant en Pascal et Delphi
- uses: u_c_basic_object, u_c_display, (u_c_log,
u_loaded, u_strings, u_types_constants, u_c_basic_file,
u_c_file_name, u_dir)
- plan:
1 - Introduction
Cet article présente une classe permettant de manipuler un fichier ASCII comme un tableau. L'avantage de cette technique par rapport à l'emploi de fichiers .TEXT sont: - rapidité
- traitement uniforme des données (pas de lecture en deux niveaux: les lignes, puis les caractères de la ligne)
- possibilité de lire ET d'écrire
Certes les tStream offraient une autre possibilité. Elle n'a pas été retenue:
- pour des raisons purement historiques. La manipulation de fichiers ASCII dans des tableaux en mémoire, je la pratique depuis l'Apple ][, et, toujours à court de temps, je n'ai pas modifié la technique
- les traitements tels que les décalages de textes vers la fin du tampon (pour remplacer un mot par un mot plus long) ne sont pas à priori possibles. "Stream" dit flux, le modèle est donc l'ancien modèle des dérouleurs de
bandes cher au Pascal original de Wirth, encore que les propriétés telles que Position permettent de revenir en arrière.
Quoiqu'il en soit, les tableaux de caractères sont restés.
Historiquement, la classe avait 2 caractérstiques que Delphi 2 m'ont permis de supprimer: - le traitement se faisait par une mécanique de 'tampon double' qui permettait
de traiter efficacement des textes ayant des tailles des plus de 64 K
- la taille précise du tableau étant inconnue, un tableau ayant un indice 0..0 était utilisé, et l'ensemble des accès se faisait sous R-. Les tableaux
indéfinis (ARRAY OF BYTE) ont résolu ce problume:
- FileSize fournit la taille du fichier
- SetLength dimensionne le tableau à l'octet près
Et R+ permet à nouveau de détecter tout débordement d'indice.
2 - Utilisation 2.1 Interface L'interface est la suivante:
type c_text_file= class(c_basic_file)
public
m_pt_buffer: t_oa_characters;
m_buffer_size, m_buffer_index: Integer;
m_has_removed_ctr_z: boolean;
m_has_line_feed: Boolean;
m_c_line: c_line;
m_from_to_end, m_from_to_begin: Integer;
constructor create_text_file(p_name, p_file_name: String); Virtual;
function f_load: Boolean; virtual;
function f_load_with_fat(p_factor: Integer): Boolean; virtual;
procedure assign_buffer(p_pt: Pointer; p_size: Integer);
function f_read_line: Boolean; virtual;
function f_end_of_text: boolean; virtual;
procedure list; virtual;
procedure dump_hex(p_begin, p_end: Integer);
function f_save: Boolean;
procedure save_in_u_directory_with_extension(p_name, p_extension: String);
function f_write(p_text: String): Boolean;
function f_write_line(p_text: String): Boolean;
function f_read_from_to(p_begin, p_end: Integer): String;
function f_write_from_to(p_text: String): Boolean;
function f_extract_string_start_length(p_start, p_length: Integer): String;
function f_extract_string_start_end(p_start, p_end: Integer): String;
procedure skip_alternate_return_line_feed(var pv_index: Integer);
procedure skip_end_of_line(var pv_index: Integer);
function f_ascii_signature: Integer;
destructor Destroy; Override;
end; | Et:
- m_pt_buffer: le tampon des caractères
- m_buffer_size, m_buffer_index: taille du tampon et indice courant
- m_has_line_feed: l'analyse a trouvé un caractère 10 dans le tampon
- m_c_line: la classe ligne courante, si elle est utilisée
- m_from_to_end, m_from_to_begin: indices utilisés pour les lectures partielles
Quant aux méthodes: - create_text_file: construit la classe
- f_load: charge le tampon
- f_load_with_fat: alloue un multiple de la taille du fichier et charge le tampon (pour les transformations qui produisent un texte plus important que l'original)
- assign_buffer: affecte un autre tampon à la classe
- f_read_line: lecture d'une ligne
- f_end_of_text: détection de la fin du texte
- list; virtual;
- dump_hex(p_begin, p_end: Integer);
- f_save: sauvegarde du tampon
- save_in_u_directory_with_extension: sauvegarde du tampon dans un sous répertoire "u"
- écriture par BlockWrite
- lecture par BlockRead
- f_read_from_to
- f_write_from_to
- analyse du texte:
- f_extract_string_start_length
- f_extract_string_start_end
- skip_alternate_return_line_feed
- skip_end_of_line
- f_ascii_signature: calcul d'une signature simple
2.2 Un premier exemple simple
Voici un exemple qui liste simplement le contenu d'un fichier ASCII:
with c_text_file.create_text_file('self', k_file_name) do
begin list;
Free; end; // with c_text_file
| 2.3 Un mini analyseur syntaxique Notre second exemple sera un analyseur syntaxique simplifié. - Nous isolons les symboles suivants:
type t_token_type= (e_unknown,
e_identifier, e_number, e_string_litteral, e_operator,
e_comment,
e_end_token); |
- La détection de chaque type de symbole se fait à partir du premier caractère:
if m_buffer_index< m_buffer_size
then begin
case m_pt_buffer[m_buffer_index] of
'a'..'z', 'A'..'Z' : get_identifier;
'0'..'9' : get_number;
'(' : if (m_buffer_index+ 1< m_buffer_size) and (m_pt_buffer[m_buffer_index+ 1]= '*')
then skip_comment_parenthesis
else get_operator;
'/' : if (m_buffer_index+ 1< m_buffer_size) and (m_pt_buffer[m_buffer_index+ 1]= '/')
then skip_comment_slash
else get_operator;
'{' : skip_comment_curly_braket;
else get_operator;
end; // case |
Notez nous devons traiter spécialement les cas où un seul caractère ne suffit pas ("//", "(*" dans notre cas, ":=" "<=" etc pour Pascal). En effet seul le premier caractères ne suffit pas dans ces cas ("/" peut être
l'opérateur de division, ou le double slash du commentaire en début de ligne). - chaque cas appelle une procédure spécifique qui analyse la suite du symbole. Voici, à titre d'exemple le traitement pour les identificateurs:
procedure get_identifier;
var l_start, l_length: Integer;
begin
l_token_type:= e_identifier;
l_start:= m_buffer_index;
while (m_buffer_index< m_buffer_size) and (m_pt_buffer[m_buffer_index] in k_identifier) do
inc(m_buffer_index);
l_length:= m_buffer_index- l_start;
if l_length> 0
then begin
SetLength(l_token_string, l_length);
Move(m_pt_buffer[l_start], l_token_string[1], l_length);
end
else l_token_string:= '';
end; // get_identifier |
Notez que nous aurions pu utiliser ici f_extract_string_start_end 2.4 Voir aussi Cette classe est souvent utilisée avec: - c_line qui permet une analyse ligne à ligne
- u_handle_files_in_dirs qui balaye une arborescence DOS et peut effectuer des traitements sur tous les fichiers ASCII rencontrés
2.5 - Répertoires et Directives de compilation L'unité est prévue pour être placée dans: C: programs colibri_helpers units
Vous pouvez naturellement changer cette organisation par Projet | Options | Directories Les directives de compilation sont: - R+ (vérification des intervalles)
- S+ (vérification de la pile)
- pas d'optimisation
3 - Programmation Cette classe bénéficie tout d'abord de toutes les possibilités de sa classe ancêtre `c_basic_file: ouverture, création, changement de nom etc.
Elle offre en plus la gestion du tampon de caractères. 4 - Améliorations 5 - Télécharger Vous pouvez télécharger: Avec les mentions d'usage: - j'apprécie tous les commentaires, remarques ou critiques
- signalez-moi les bugs que vous trouverez.
L'auteur
John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |