Martin Viljoen
Martin Viljoen

Reputation: 23

How to parse delimited TStringlist to dynamic multidimensional array of integer?

I have been searching high and low for a Delphi function or any info that can parse a space delimited TStringList(Loaded from *.asc file) to a dynamic 2D array of integer.

What I have is an ASCII grid file with elevations in a grid format. Firstly I read the file into a TStringlist, where I extract the header data to variables, then delete the header data from the TStringlist. What I'm left with is a TStringlist with only elevation data in a grid of 3601 x 3601, with each value separated by a space. With this Stringlist still in memory, I'd like to fill a 2D array of integer with the elevation data, then free the TStringList.

From my searches I stumbled upon a piece of code here that kind of worked, but not completely.

What I have is:

var sourceData, tmpGrid, tmpRow : TStringList;
    val : String;
    Pos, i, j, k, l : Integer;
    nCols, nRows, noData : Integer;
    xllCorner, yllCorner, cellSize : Extended;
    Dem : array of array of Integer;
begin
 val := '';
 sourceData := TStringList.Create;
 tmpGrid := TStringList.Create;
 tmpRow := TStringList.Create;
 tmpRow.StrictDelimiter := True;
 tmpRow.Delimiter := ' ';
 sourceData.LoadFromFile('D:\ZA_DEM_DATA\Bulk_Order_466486\ARCASCII\s30_e026_1arc_v3.asc');

nCols := StrToInt(StripNonConforming(sourceData.Strings[0], ['0'..'9', '+', '-', '.']));
nRows := StrToInt(StripNonConforming(sourceData.Strings[1], ['0'..'9', '+', '-', '.']));
xllCorner := StrToFloat(StripNonConforming(sourceData.Strings[2], ['0'..'9', '+', '-', '.']));
yllCorner := StrToFloat(StripNonConforming(sourceData.Strings[3], ['0'..'9', '+', '-', '.']));
cellSize := StrToFloat(StripNonConforming(sourceData.Strings[4], ['0'..'9', '+', '-', '.']));
noData := StrToInt(StripNonConforming(sourceData.Strings[5], ['0'..'9', '+', '-', '.']));

Edit1.Text := IntToStr(nCols);
Edit2.Text := IntToStr(nRows);
Edit3.Text := FloatToStr(xllCorner);
Edit4.Text := FloatToStr(yllCorner);
Edit5.Text := FloatToStr(cellSize);
Edit6.Text := IntToStr(noData);

for I := 0 to 5 do
 begin
  sourceData.Delete(0);
 end;
//This is where I start parsing my TStrinList
tmpGrid.DelimitedText := sourceData.Text;
SetLength(Dem, nCols, nRows);

Edit7.Text := IntToStr(sourceData.Count);

try
 for i := 0 to sourceData.Count -1 do
   for j := 0 to sourceData.Count -1 do
    begin
     Dem[i][j] := StrToInt(tmpGrid.Strings[i]);
    end;
finally
 sourceData.Free;
end;

StringGrid1.ColCount := nCols;
StringGrid1.RowCount := nRows;
//This is mainly to visualise the data while testing the code
for i := 0 to nCols -1 do
  for j := 0 to nRows -1 do
   begin
    StringGrid1.Cells[i, j] := IntToStr(dem[i, j]);
   end;
 end;

The result I get is the first "row" of data as displayed in the original file, but copied to all rows of the array. For example:

Original Data:

1. 1245 1268 1232 1258
2. 1354 1321 1578 1689
3. 1461 1423 1475 1427
4. 1598 1541 1562 1550

What I get is:

1. 1245 1268 1232 1258
2. 1245 1268 1232 1258
3. 1245 1268 1232 1258
4. 1245 1268 1232 1258

Any and all help will be greatly appreciated.

Upvotes: 0

Views: 1640

Answers (2)

Tom Brunberg
Tom Brunberg

Reputation: 21045

This seems to be the cause of the wrong results

 for i := 0 to sourceData.Count -1 do
   for j := 0 to sourceData.Count -1 do
    begin
     Dem[i][j] := StrToInt(tmpGrid.Strings[i]);
    end;

You have moved the actual data from SourceData to tmpGrid, which is not needed. Instead, assign one line at a time from SourceData to tmpRow, then read the values one at a time from tmpRow, and assign to the value array:

for i := 0 to SourceData.Count-1 do
begin
  tmpRow.DelimitedText := SourceData[i];
  for j := 0 to tmpRow.Count-1 do
    Dem[i,j] := StrToInt(tmpRow[j]);
end;

Upvotes: 2

Sir Rufo
Sir Rufo

Reputation: 19106

Just after this code line

//This is where I start parsing my TStrinList
tmpGrid.DelimitedText := sourceData.Text;

your sourceData contains 4 lines of text

1245 1268 1232 1258
1354 1321 1578 1689
1461 1423 1475 1427
1598 1541 1562 1550

and tmpGrid contains 16 lines of text

1245
1268
1232
1258
1354
1321
1578
1689
1461
1423
1475
1427
1598
1541
1562
1550

But you only access the tmpGrid lines from 0 to 3 for each row

 for i := 0 to sourceData.Count -1 do
   for j := 0 to sourceData.Count -1 do
    begin
     Dem[i][j] := StrToInt(tmpGrid.Strings[i]);
    end;

You should change your code to use the values from nCols and nRows and then calculate the line inside tmpGrid

 for i := 0 to nCols -1 do
   for j := 0 to nRows -1 do
    begin
     Dem[i][j] := StrToInt(tmpGrid.Strings[i + j*nCols]);
    end;
i = 0 // first column
j = 1 // second row
i + j * nCols => 0 + 4 => fifth line of tmpGrid (zero indexed)

Upvotes: 2

Related Questions