How to Edit ini Section of File in Archive lazarus / 49-FPSpreadsheet ---------------------------------------------------------------------- flywire · 2015-03-10 11:01 I want to edit the numbered layer lines in the [LAYERS] section of 00map.ini which is one of the files in an archive that I have in a non-text TfileStream, so I know the start and end of the ini file. The layer section is in csv space delimited format comprised of 42 columns (assuming 4 blank columns at the double spaces). It is likely that editing the section will change the file size so the whole archive will have to be rewritten. Sample start to [LAYERS] section: Code: [Select][LAYERS] NUMBER=23 0=00_Freeways 00lay0 20 1 0 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK US_INTERSTATE_HW_LINE 1=00_Highways 00lay1 20 1 0 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK US_STATE_HW_LINE 2=00_Major_Roads 00lay2 20 1 0 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLACK US_MAJOR_RD_LINE Sample ini file: Code: [Select][GROUPS] SEARCH_GROUPS=3 0 6 8 NUMBER=8 0=00_Roads 5 0 ( 0 ) 1 ( 78 ) 2 ( 79 ) 3 ( 80 ) 4 ( 81 ) 1=00_Railroads 1 5 ( 0 ) 2=00_Hydrography 2 6 ( 70 ) 7 ( 83 ) 3=00_Parks 1 8 ( 71 ) 4=00_MAN 3 9 ( 72 ) 10 ( 84 ) 11 ( 73 ) 5=00_NATURE 3 12 ( 85 ) 13 ( 74 ) 14 ( 75 ) 6=00_CITIES 4 15 ( 10 ) 16 ( 11 ) 17 ( 12 ) 18 ( 13 ) 7=00_Help 4 19 ( 86 ) 20 ( 87 ) 21 ( 14 ) 22 ( 15 ) [COLORS4BITS] LAY_COLOR=10 4 RED MSOLID_LINE  5 DARK_GRAY US_RAIL_ROAD  6 BLUE SOLID_FILL  7 BLUE MSOLID_LINE  8 PAIL_GREEN SOLID_FILL  12 BLUE DASH_LINE  13 BLUE SLANT_PATT  14 YELLOW SOLID_FILL  19 CYAN DASH_LINE  20 CYAN MSOLID_LINE MYCOLORS=3 1 0xBDEE71 2 0x02742A 3 0x669966 [MAP_INFO] VERSION=20 BYTE_ORDER=I BND_BOX=-64.997276 -64.291092 -32.685204 -32.002411 IS_ADD=0 START_SCALE=4500.00 MAP_NAME=Bermudas [LIMITRECTANGLE] BOUNDRECT=-198.158401 198.158401 -99.158401 99.158401 [COPYRIGHT] 1=(c) 2011 Sockeye 2=http://www.maps4me.net 3=Version 26.11.2011 [LAYERS] NUMBER=23 0=00_Freeways 00lay0 20 1 0 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK US_INTERSTATE_HW_LINE 1=00_Highways 00lay1 20 1 0 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK US_STATE_HW_LINE 2=00_Major_Roads 00lay2 20 1 0 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLACK US_MAJOR_RD_LINE 3=00_Streets 00lay3 20 1 0 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLACK US_STREET_LINE 4=00_Trails_4WD 00lay4 20 1 0 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 RED MSOLID_LINE 5=00_Railroads 00lay5 20 1 1 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 DARK_GRAY US_RAIL_ROAD 6=00_Hydro_Polygons 00lay6 20 1 2 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLUE SOLID_FILL 7=00_Hydro_Lines 00lay7 20 1 2 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLUE MSOLID_LINE 8=00_Parks 00lay8 20 1 3 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 PAIL_GREEN SOLID_FILL 9=00_PLAZA 00lay9 20 1 4 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 WHITE SOLID_FILL 10=00_POWER_LINE 00lay10 20 1 4 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 LIGHT_GRAY MSOLID_LINE 11=00_URBAN 00lay11 20 1 4 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 LIGHT_GRAY SOLID_FILL 12=00_STREAM 00lay12 20 1 5 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLUE DASH_LINE 13=00_SWAMP 00lay13 20 1 5 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLUE SLANT_PATT 14=00_BEACH 00lay14 20 1 5 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 YELLOW SOLID_FILL 15=00_METROPOLE 00lay15 20 1 6 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK MAJOR_CITIES 16=00_CITIES 00lay16 20 1 6 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK LARGE_CITIES 17=00_TOWN 00lay17 20 1 6 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLACK MEDIUM_CITIES 18=00_HAMLET 00lay18 20 1 6 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLACK SMALL_CITIES 19=00_FERRY 00lay19 20 1 7 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 CYAN DASH_LINE 20=00_Depthline 00lay20 20 1 7 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 CYAN MSOLID_LINE 21=00_SpotSounding 00lay21 20 1 7 0 4 0 4 0 4  0 4 0 4 0 4  0 3 0 3 0 3  0 2 0 2 0 2  0 2 0 2 0 2 BLACK NOTHING_POINT 22=00_GenLabel 00lay22 20 1 7 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLACK MANNOT_POINT DB_NAME=db00 ORDERDRAW=11 8 9 6 14 13 12 7 5 4 3 1 2 0 19 20 10 21 22 18 17 16 15 POI_INDEX=8 [TABLES] ZIP_TABLE=0 0 [POI] POI_CONFIG=00poi.cfg  I thought that FPSpreadsheet fpsgrid example would be a good shell to start with. How do I load the numbered layer rows into the grid?How do I set the delimiter character to a space? ---------------------------------------------------------------------- wp · 2015-03-10 15:57 FPSpreadsheet is an overkill for this simple file parsing task. Just use an ini file, a simple string grid, and parse the lines by means of the Delimiter property of a stringlist: Code: [Select]var   list: TStringList;   ini: TCustomIniFile; begin   list := TStringList.Create;   list.Delimiter := ' ';   list.StrictDelimiter := true;   ini := TMemInifile.Create('sample.ini');   try     number := ini.ReadInteger('LAYERS', 'NUMBER', 0);     StringGrid1.RowCount := number + 1; // +1 for fixed row     StringGrid1.colCount := 42 + 1;  // you know the 42, +1 for fixed col     for i:=1 to number do begin       line := ini.ReadString('LAYERS', IntToStr(i), '');       list.DelimitedText := line;       StringGrid1.Cells[0, i] := IntToStr(i);       for j:=0 to list.Count-1 do         StringGrid1.Cells[j+1, i] := list[j];     end;     for j := 1 to StringGrid1.ColCount-1 do       StringGrid1.Cells[j, 0] := IntToStr(j);   finally     list.Free;     ini.Free;   end; end; ---------------------------------------------------------------------- flywire · 2015-03-11 01:40 Quote from: wp on March 10, 2015, 04:57:00 pmFPSpreadsheet is an overkill for this simple file parsing task. I totally agree if you only mean the csv loader but I wanted to use FPSpreadsheet as a grid to help the user edit the 42 columns, which are very confusing and need to be verified, plus add a few more controls. The main thing is that the digits on the RHS are a matrix of Detail[5] x Feature[3] x Visibility[2] (base 1). Grouping the Detail allowing some of them to be hidden,  hiding Features and pehaps Visibility then doing validation on input will make this a lot more understandable. Selection boxes to include or delete layers, Drop down boxes on colours and line types and that would be most of the editing functionality. Then save and remake the archive. I thought that FPSpreadsheet would be a better container for this than a grid of stringlist but any advice appreciated. I haven't got acccess to the compiler at the moment and I will have to see why it complained about not being able to find Grids. Top of file below: Code: [Select]program IniFileLoad; uses   Classes, IniFiles, sysutils, Grids; var   list: TStringList;   ini: TCustomIniFile;   StringGrid1: TStringGrid;   i, j, number: integer;   line: string; begin ... ---------------------------------------------------------------------- flywire · 2015-03-11 07:59 Quote from: flywire on March 11, 2015, 02:40:24 am ... any advice appreciated. ... ---------------------------------------------------------------------- wp · 2015-03-11 09:10 Set up a gui program properly and you will have all dependencies of a TStringGrid included correctly. Your code snippet indicates that you want a commandline program - TStringGrid requires a gui. ---------------------------------------------------------------------- flywire · 2015-03-11 09:28 You're right - I didn't understand that it needs a gui. ---------------------------------------------------------------------- wp · 2015-03-11 09:37 Have you ever written a gui program? If not, look at http://wiki.lazarus.freepascal.org/Form_Tutorial, and there are certainly a lot more introductory tutorials. In your case, add a TStringGrid to the form and, maybe, a TButton. Double-click on the button on the form to open the code editor for the OnClick event. Paste my code into that procedure skeleton. Compile & run. After clicking at the button, your program should open your ini file (called "sample.ini" here - just rename if it does not suit you) and show the Layers section in the grid. ---------------------------------------------------------------------- flywire · 2015-03-11 12:13 Thanks wp, I'm away with gui. Some minor changes for the benefit of anyone that tries this (Note that the layer number is stripped by the ini procedure.): Code: [Select]unit Unit1; // Create grid and load with ini file layer contents {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Grids,   StdCtrls, IniFiles; type   { TForm1 }   TForm1 = class(TForm)                                    // Form has     Button1: TButton;                                      // Button     StringGrid1: TStringGrid;                              // StringGrid     procedure Button1Click(Sender: TObject);   private     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);            // On button click                                                            // Load ini to Grid var   list: TStringList;   ini: TCustomIniFile;                                     // StringList type   i, j: integer;                                           // Ini type   number: longint;                                         // Row col counters   line: string;                                            // Ini line   begin     list := TStringList.Create;                            // Define StringList     list.Delimiter := ' ';                                 // Space delimited     list.StrictDelimiter := true;                          // Force delineation     ini := TMemInifile.Create('00map.ini');                // Ini filename     try       number := ini.ReadInteger('LAYERS', 'NUMBER', 0);    // Read ini Layers No       StringGrid1.RowCount := number + 1;                  // Rows +1 for fixed       StringGrid1.ColCount := 42 + 1;                      // Known cols +1       for i:=0 to number do begin                          // For each row         line := ini.ReadString('LAYERS', IntToStr(i), ''); // Read ini row         list.DelimitedText := line;                        // Reformat ini row         StringGrid1.Cells[0, i] := IntToStr(i);            // Load row label         for j:=0 to list.Count-1 do                        // For each column           StringGrid1.Cells[j+1, i+1] := list[j];          // Load cell       end;       for j := 1 to StringGrid1.ColCount-1 do              // For each row         StringGrid1.Cells[j, 0] := IntToStr(j);            // Load col label     finally                                                // Free memory       list.Free;                                           // For list       ini.Free;                                            // For ini     end;   end; end. Quote from: flywire on March 11, 2015, 02:40:24 am... I wanted to use FPSpreadsheet as a grid to help the user edit the 42 columns, which are very confusing and need to be verified, plus add a few more controls. The main thing is that the digits on the RHS are a matrix of Detail[5] x Feature[3] x Visibility[2] (base 1). Grouping the Detail allowing some of them to be hidden,  hiding Features and perhaps Visibility then doing validation on input will make this a lot more understandable. Selection boxes to include or delete layers, Drop down boxes on colours and line types and that would be most of the editing functionality. Then save and remake the archive. I thought that FPSpreadsheet would be a better container for this ... Can I get confirmation if StringGrid or FPSpreadsheet is the way to go as a container, or are they basically the same thing? I have noted a few issues with StringGrid and delphi and it would be good if the code could be compiled with both. Edit: Inserted code with comments ---------------------------------------------------------------------- wp · 2015-03-11 12:43 QuoteCan I get confirmation if StringGrid or FPSpreadsheet is the way to go as a container, or are they basically the same thing? I have noted a few issues with StringGrid and delphi and it would be good if the code could be compiled with both.FPSpreadsheet does not exist for Delphi... StringGrid and FPSpreadsheet are completely different things: StringGrid is some kind of table control displaying strings. FPSpreadsheet is a library to read/write spreadsheet files - you probably mean the TsWorksheetGrid which looks like a StringGrid, but does not store any data - it gets them from the underlying worksheet (StringGrid stores the strings in internal structures). So, if you don't need spreadsheet files, and if you want to use Delphi go with the StringGrid... ---------------------------------------------------------------------- flywire · 2015-03-14 10:55 Quote from: wp on March 10, 2015, 04:57:00 pm... Just use an ini file, ... and parse the lines by means of the Delimiter property of a stringlist: Code: [Select]list.DelimitedText := line; Well no. These files are more complicated than I imagined and the example that I provided is not necessarily the best one. (I incorrectly interpreted the double spaces as an empty field when they are actually double space field delimiters.) The best that I can explain is that the fields are delimited by 'white space' which could be one or a number of spaces, tabs etc. Libre Office Calc allows a number of Delimiters to be specified and a Merge Delimiters used to treat them as a single delimiter. As I see it the basic issue is that TStrings.DelimitedText breaks the fields on every occurrence of the delimiter and will not allow multiple delimiter types to be used at the same time. I know the basic philosophy is if you find that doesn't work you should fix it and make it available for everyone else. To be honest, I can't even describe what the code is but I suppose it is a method, so there's little chance of me changing it to 1..n delimiters rather than one. I'll go for the hack and dump the white space and insert a nice single delimiter. I suppose it is just a procedure to ParseDelimitedText(var TStrings.DelimitedText as TStringList; const line as TStringList; const Delimiters as string) and treat them as strings. Read all of the characters in the line and keep them if not instr of delimiters then add delimiter between each field. Running lazarus-1.4RC2-fpc-2.6.4-win32 under win7 ---------------------------------------------------------------------- engkin · 2015-03-14 15:08 If you post a better example we might be able to help. ---------------------------------------------------------------------- flywire · 2015-03-15 12:37 Quote from: engkin on March 14, 2015, 04:08:26 pmIf you post a better example ... Sorry, my post didn't save. I like the IniFiles unit but it does not parse the file as I require. I would like to parse the data and continue to use the IniFiles unit for other ini file processes. I have included sample data below but the detail of mixed tabs and spaces, sometimes multiple and mixed as a single delimiter, is in the 'white space'. (Ini file loaded as sample2.txt.) Code: [Select][LAYERS] NUMBER=23 0=00_Freeways 00lay0  20 1 0 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK US_INTERSTATE_HW_LINE 1=00_Highways 00lay1  20 1 0 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK US_STATE_HW_LINE 2=00_Major_Roads 00lay2  20 1 0 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLACK US_MAJOR_RD_LINE 3=00_Streets 00lay3  20 1 0 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLACK US_STREET_LINE 4=00_Trails_4WD 00lay4  20 1 0 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 RED MSOLID_LINE 5=00_Railroads 00lay5  20 1 1 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 DARK_GRAY US_RAIL_ROAD 6=00_Hydro_Polygons     00lay6  20 1 2 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLUE SOLID_FILL 7=00_Hydro_Lines 00lay7  20 1 2 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLUE MSOLID_LINE 8=00_Parks 00lay8 20 1 3 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 PAIL_GREEN SOLID_FILL 9=00_PLAZA 00lay9  20 1 4 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 WHITE SOLID_FILL 10=00_POWER_LINE 00lay10 20 1 4 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 LIGHT_GRAY MSOLID_LINE 11=00_URBAN         00lay11 20 1 4 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 LIGHT_GRAY SOLID_FILL 12=00_STREAM 00lay12 20 1 5 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLUE DASH_LINE 13=00_SWAMP 00lay13 20 1 5 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLUE SLANT_PATT 14=00_BEACH 00lay14 20 1 5 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 YELLOW SOLID_FILL 15=00_METROPOLE 00lay15 20 1 6 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK MAJOR_CITIES 16=00_CITIES 00lay16 20 1 6 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 BLACK LARGE_CITIES 17=00_TOWN 00lay17 20 1 6 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 BLACK MEDIUM_CITIES 18=00_HAMLET 00lay18 20 1 6 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLACK SMALL_CITIES 19=00_FERRY 00lay19 20 1 7 0 7 0 7 0 7  0 7 0 7 0 7  0 6 0 6 0 6  0 5 0 5 0 5  0 5 0 5 0 5 CYAN DASH_LINE 20=00_Depthline 00lay20 20 1 7 0 6 0 6 0 6  0 6 0 6 0 6  0 5 0 5 0 5  0 4 0 4 0 4  0 4 0 4 0 4 CYAN MSOLID_LINE 21=00_SpotSounding 00lay21 20 1 7 0 4 0 4 0 4  0 4 0 4 0 4  0 3 0 3 0 3  0 2 0 2 0 2  0 2 0 2 0 2 BLACK NOTHING_POINT 22=00_GenLabel 00lay22 20 1 7 0 5 0 5 0 5  0 5 0 5 0 5  0 4 0 4 0 4  0 3 0 3 0 3  0 3 0 3 0 3 BLACK MANNOT_POINT DB_NAME=db00 ORDERDRAW=11 8 9 6 14 13 12 7 5 4 3 1 2 0 19 20 10 21 22 18 17 16 15 POI_INDEX=8 This is a start to the approach I was working towards but I wont be back to it for a few days. Code: [Select]program ParseDelimitedTextDemo; var   line: string; Const   Delimiter: array[0..1] of char = ' '+ #9; procedure ParseDelimitedText(var line: string; Delimiter: array of char); var   TmpLine: string;   i, j: integer;   bNewFieldFlag: boolean;   begin     // Initialise     i := 1;     j := 1;     TmpLine := '';     try     for i := 1 to length(line) do begin         // For whole line       if pos(line[i],Delimiter)=0 then          // If not delimiter         begin           TmpLine[j] := line[i];                // Add character to temp string           inc(j);           bNewFieldFlag := true;                // Flag field new         end;       else if bNewFieldFlag then         begin           TmpLine[j] := Delimiter[0];           // Add 1st delimiter           inc(j);           bNewFieldFlag := false;               // Flag field not new         end;     end;   finally     line := TrimRight(TmpLine);                 // Trim any trailing delimiter   end; end; begin   line := 'Demo String     with spaces';   ParseDelimitedText (line, delimiter); end. ---------------------------------------------------------------------- engkin · 2015-03-15 14:50 How about this code: Code: [Select]program ParseDelimitedText_demo; uses   classes, sysutils; procedure PrintFields(const vLine: String); var   sl: TStringList;   s: String;   aLine: string; begin   sl := TStringList.Create;   try     aLine := StringReplace(vLine, #9, #32, [rfReplaceAll]);     sl.Delimiter := #32;     sl.StrictDelimiter := False;     sl.DelimitedText := aLine;     WriteLn('Number of fields is: ',sl.Count);     for s in sl do       writeLn(s);   finally     sl.Free;   end; end; var   line: string; begin   line := 'Demo String     with spaces';   PrintFields(line);   WriteLn('');   line := 'Demo String'#9#9#9#9'with tabs';   PrintFields(line);   WriteLn('');   line := #9#32'Demo String'#9#9#32#9'with  spaces    and '#9#9#9' tabs'#9;   PrintFields(line);   readln; end. ---------------------------------------------------------------------- flywire · 2015-03-16 02:11 Quote from: engkin on March 15, 2015, 03:50:14 pmHow about this code: ... Nice. It is simple and it works (although it only allows one additional delimiter other than the preferred one so if a comma or semicolon was also used the String Replace line would need to be duplicated). I sort of wish that I didn't post my ugly approach now.  What I would like to understand is how to use the IniFiles unit in the code from http://forum.lazarus.freepascal.org/index.php/topic,27663.ms g171301.html#msg171301 but overload the procedure so that it runs this code but everything else in the IniFiles unit still works. ---------------------------------------------------------------------- engkin · 2015-03-16 03:28 I just saw a comment in the source code: Quote    //Strings must be separated by Delimiter characters or spaces. which means that I did not need to use StringReplace, and a PrintFields is simply: Code: [Select]procedure PrintFields(const vLine: String); var   sl: TStringList;   s: String; begin   sl := TStringList.Create;   try     sl.Delimiter := #09;     sl.StrictDelimiter := False;     sl.DelimitedText := vLine;     WriteLn('Number of fields is: ',sl.Count);     for s in sl do       writeLn(s);   finally     sl.Free;   end; end; I believe you were using the same except you had StrictDelimiter = true. Quote from: flywire on March 16, 2015, 03:11:33 am[...] although it only allows one additional delimiter other than the preferred one so if a comma or semicolon was also used the String Replace line would need to be duplicated). For arbitrary number of delimiters I adjusted your code a bit: Code: [Select]procedure PrintFields(const vLine: String); var   sl: TStringList;   Delimiters: String;   vFrom, vTo: Integer;   s: String; begin   Delimiters := ' ,.'#9;   sl := TStringList.Create;   try     vFrom := 1;     repeat       { find a field }       vTo := vFrom;       while (vTo <= Length(vLine)) and not (Pos(vLine[vTo], Delimiters) <> 0) do         inc(vTo);       if vTo>vFrom then { add field }         sl.add(Copy(vLine, vFrom, vTo-vFrom));       { skip delimiters }       vFrom := vTo+1;       while (vFrom <= Length(vLine)) and (Pos(vLine[vFrom], Delimiters) <> 0) do         inc(vFrom);     until vFrom>Length(vLine);     WriteLn('Number of fields is: ',sl.Count);     for s in sl do       writeLn(s);   finally     sl.Free;   end; end; ---------------------------------------------------------------------- engkin · 2015-03-16 04:07 Quote from: flywire on March 16, 2015, 03:11:33 amWhat I would like to understand is how to use the IniFiles unit in the code from http://for um.lazarus.freepascal.org/index.php/topic,27663.msg171301.html#msg1713 01 but overload the procedure so that it runs this code but everything else in the IniFiles unit still works. Simply build a new class based on TStringList. I'll call it TDelimitedList: Code: [Select]  TDelimitedList = class(TStringList)   public     procedure AddFields(vLine: String; vDelimiters: String);   end; as you can see, it adds one procedure, AddFields: Code: [Select]  procedure TDelimitedList.AddFields(vLine: String; vDelimiters: String);   var     sl: TStringList;     vFrom, vTo: Integer;   begin     vFrom := 1;     repeat       { find a field }       vTo := vFrom;       while (vTo <= Length(vLine)) and not (Pos(vLine[vTo], vDelimiters) <> 0) do         inc(vTo);       if vTo>vFrom then { add field }         add(Copy(vLine, vFrom, vTo-vFrom));       { skip delimiters }       vFrom := vTo+1;       while (vFrom <= Length(vLine)) and (Pos(vLine[vFrom], vDelimiters) <> 0) do         inc(vFrom);     until vFrom>Length(vLine);   end; And make a few adjustments to the code you referred to in a link in your previous post. Instead of: Code: [Select]    list := TStringList.Create; use the new class: Code: [Select]    list := TDelimitedList.Create; And instead of: Code: [Select]        list.DelimitedText := line; use the new procedure: Code: [Select]        list.AddFields(line, ' ,.'#9); { add your delimiters here } Notice that you can add as much delimiters as you wish. If it works as you expect then you can get rid of these two lines, they are not needed anymore: Code: [Select]    list.Delimiter := ' ';     list.StrictDelimiter := true; Edit: It goes without saying that the type of variable list should be changed from TStringList to TDelimitedList. ---------------------------------------------------------------------- flywire · 2015-03-17 13:14 This class works with program ParseDelimitedText_demo; but it does not work with the attached program MagellanIniGrid; Can you explain how the class works in relation to the existing IniFiles unit? I thought that an overload procedure would have been used to just replace one of the IniFiles procedures (methods?) and allow the other procedures to be used (eg to write the ini file after it is edited). ---------------------------------------------------------------------- engkin · 2015-03-17 13:48 Quote from: flywire on March 17, 2015, 02:14:30 pmThis class works with program ParseDelimitedText_demo; but it does not work with the attached program MagellanIniGrid; The new list retains the previous values, which you don't need in this case, so just add list.Clear before list.AddFields: Code: [Select]        list.Clear;         list.AddFields(line, ' ,.'#9); { add your delimiters here } Quote from: flywire on March 17, 2015, 02:14:30 pmCan you explain how the class works in relation to the existing IniFiles unit? I thought that an overload procedure would have been used to just replace one of the IniFiles procedures (methods?) and allow the other procedures to be used (eg to write the ini file after it is edited). You can't override a method inside IniFiles if it's not virtual method. Edit: You don't need xx columns in the grid anymore. Just curious, any link to explain these fields? ---------------------------------------------------------------------- flywire · 2015-03-17 21:06 Quote from: engkin on March 17, 2015, 02:48:54 pmThe new list retains the previous values, which you don't need in this case, so just add list.Clear before list.AddFields: Yes that worked. (My next task is to study the IniFiles data structures more thoroughly.) QuoteYou don't need xx columns in the grid anymore. Just got to the bottom of your post - already gone. I will have to put the double spaces back in when I rewrite the ini file. QuoteJust curious, any link to explain these fields? No, just bits and pieces from everywhere. Good point - needs a wiki. Briefly to describe layer columns: layer name, layer extension aggregation, three fields of no interest, 5 x group of 6 columns, line type, symbology. Groups of 6 columns are zoom for - detail level: visible from, visible to; base map: visible from, visible to; labels visible from, visible to. By default detail zoom level is below 7 and above that there will be conflicts to resolve, especially with base map level. Note that there are a number of generations of hardware and software which do/don't support features to various extents. Disclaimer. This is unsupported. Always backup your maps before trying this and realise that it could break your GPS, possibly when you are lost in a remote area. ---------------------------------------------------------------------- flywire · 2015-03-22 13:05 I can't figure out writing the string back to the ini file. I need to write the space delineated line at line '// Write ini row ***'. The list is already space delineated but it is type TStringList and I need type string. Code: [Select]procedure TForm1.Button2Click(Sender: TObject); { Uses number: longint; number of columns } var   list: TStringList;                                       // StringList type   ini: TCustomIniFile;                                     // Ini type   i, j: integer;                                           // Row col counters   TargetF: string;   begin     TargetF := 'sample3.ini';     list := TStringList.Create;                            // Define StringList     ini := TMemInifile.Create(TargetF);                    // Ini filename     try       ini.WriteInteger ('LAYERS', 'NUMBER', number);       // Write ini Layers No       for i:=0 to number-1 do begin                        // For each row         for j:=0 to StringGrid1.ColCount-2 do              // For each column           list.add(StringGrid1.Cells[j+1, i+1]);           // Get list from cells         ini.WriteString ('LAYERS', IntToStr(i), list.text);// Write ini row ***       end;     finally                                                // Free memory       list.Free;                                           // For list       ini.Free;                                            // For ini //      TargetF.Free;                                        // For file     end; end; ---------------------------------------------------------------------- engkin · 2015-03-22 14:58 list.text uses #13#10 to separate the values, while space (#32) is needed. So build them one line at a time: Code: [Select]procedure TForm1.Button2Click(Sender: TObject); { Uses number: longint; number of columns } var   list: TStringList;                                       // StringList type   ini: TCustomIniFile;                                     // Ini type   i, j: integer;                                           // Row col counters   TargetF: string;   number: longint;   aLine: string; begin   TargetF := 'sample3.ini';   list    := TStringList.Create;                            // Define StringList   ini     := TMemInifile.Create(TargetF);                    // Ini filename   try     ini.WriteInteger('LAYERS', 'NUMBER', number);       // Write ini Layers No     for i := 0 to number - 1 do     begin                        // For each row       aLine := '';       for j := 0 to StringGrid1.ColCount - 2 do              // For each column       begin         if j = 0 then           aLine := aLine + StringGrid1.Cells[j + 1, i + 1]           // Get list from cells         else           aLine := aLine + ' ' + StringGrid1.Cells[j + 1, i + 1];       end;       ini.WriteString('LAYERS', IntToStr(i), aLine);// Write ini row ***     end;   finally                                                // Free memory     list.Free;                                           // For list     ini.Free;                                            // For ini     //      TargetF.Free;                                        // For file   end; end; ---------------------------------------------------------------------- flywire · 2015-03-23 08:50 I ran this and it only worked on the first line then I realised that it had not changed the invalid lines from the previous version. I deleted the ini file and the ini file created was fine.