1

I am looking for advice on how to best handle a programming task using Delphi 7.

I have the need to be able to quickly identify a value from a CSV file (less than 15kb in size). The CSV file exists in format of:

Chapter number, Paragraph number, Total number of words in the paragraph

I want to be able to retrieve this last value, i.e., the number of words, by providing a function the first two values (i.e., the chapter and paragraph number).

The CSV file is sorted numerically, that is:

1,1,30    // first paragraph of first chapter  (line # 1)
1,2,56    // second paragraph of first chapter  (line # 2)
1,3,101
1,4,56
...
2,1,78
2,2,51
...
100,1,87
100,2,101
...
100,23,78    // last paragraph of last chapter (line # 1500)

So in the example above, I'd like to pass 2,2 to a function and have it return "51" (integer)

I'd like to avoid using a database table because: 1) the amount of data isn't that large (1500 lines in the CSV file, i.e., 1500 paragraphs), 2) the extra overhead of a database engine (I only need to read data, not write data), 3) the frequency that this function will be called from within the program.

What would you recommend, and why?

4

5 回答 5

3
TYPE
  TTwoDimIntArr = ARRAY OF ARRAY OF Cardinal;

PROCEDURE SetValue(VAR ARR : TTwoDimIntArr ; Chapter,Paragraph,Value : Cardinal);
  BEGIN
    IF Chapter>=LENGTH(ARR) THEN SetLength(ARR,SUCC(Chapter));
    IF Paragraph>=LENGTH(ARR[Chapter]) THEN SetLength(ARR[Chapter],SUCC(Paragraph));
    ARR[Chapter,Paragraph]:=Value
  END;

FUNCTION GetValue(CONST ARR : TTwoDimIntArr ; Chapter,Paragraph : Cardinal) : Cardinal;
  BEGIN
    IF Chapter>=LENGTH(ARR) THEN
      Result:=0
    ELSE IF Paragraph>=LENGTH(ARR[Chapter]) THEN
      Result:=0
    ELSE
      Result:=ARR[Chapter,Paragraph]
  END;

FUNCTION ParseFile(CONST FileName : STRING) : TTwoDimIntArr;
  VAR
    SL  : TStrings;
    S   : STRING;
    P,Q : Cardinal;
    {$IFDEF DELPHI7 }
      I : Cardinal;
    {$ENDIF }

  BEGIN
    SL:=TStringList.Create;
    TRY
      SL.LoadFromFile(FileName);
      {$IFDEF DELPHI7 }
      FOR I:=1 TO SL.Count DO BEGIN
        S:=SL[PRED(I)];
      {$ELSE }
      FOR S IN SL DO BEGIN
      {$ENDIF }
        P:=POS(',',S);
        Q:=PosEx(',',S,SUCC(P));
        SetValue(Result,StrToInt(COPY(S,1,PRED(P))),StrToInt(COPY(S,SUCC(P),PRED(Q-P))),StrToInt(COPY(S,SUCC(Q),255)))
      END
    FINALLY
      SL.Free
    END
  END;

ParseFile parses the file and returns it in a two-dimensional dynamic array. If you are 100% sure that you don't exceed the boundaries of the array you can access it directly. Otherwise the GetValue function is a safe wrapper to access the contents of the array.

Use it as:

USES ... StrUtils ...;

.
.<My Code>
.
VAR ARR : TTwoDimIntArr;

BEGIN
  ARR:=ParseFile(<FileName>);
  .
  .
  .
  Words:=GetValue(ARR, <Chapter>, <Paragraph>);
  .
  .
END.

If Delphi 7 doesn't have the PosEx function in StrUtils, you can code it as follows:

FUNCTION PosEx(CONST SearchFor,SearchIn : STRING ; StartPos : Cardinal = 1) : Cardinal;
  BEGIN
    Result:=POS(SearchFor,COPY(SearchIn,StartPos,$7FFFFFFF));
    IF Result>0 THEN INC(Result,PRED(StartPos))
  END;
于 2013-09-02T14:53:25.423 回答
3

Instead of writing your own code, why not use the, possibly little known, CommaText property of TStrings?

An example of using CommaText is shown below. CODE

var
  s: TStringList;
begin
  s := TStringList.Create;
  try
    s.CommaText := 'a, b, "c,d"';
    ShowMessage( s[0] );
    ShowMessage( s[1] );
    ShowMessage( s[2] );
  finally
    s.Free;
  end;
end;

Will Display Quote: a b c,d

as the output of the three ShowMessage calls.

Andrew

Hampshire, UK

于 2014-05-03T03:15:59.917 回答
1

If file is not that big (~few kilobytes), I'd recommend to read it to memory, and search for #13#10',,'. The next number after this string will be number you are searching for (of course, if there are no extra spaces in your file).

于 2013-09-02T10:44:35.077 回答
1

I think I would create two-dimensional array

Book[Chapter, Paragraph]

and fill it manually by reading the file.

And the function will be trivial:

GetNumberOfPages(Chapter: integer; Paragraph: integer): integer;
begin
  Result := Book[Chapter, Paragraph];
end;

If you are looking for 3-rd party tools, JEDi could an option

http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvCsvDataSet

于 2013-09-02T13:19:40.950 回答
1

With a 2dim array you need a stringlist helper as temp:

BEGIN
    SL:=TStringList.Create;
    sltemp:= TStringList.Create;
   TRY
      SL.LoadFromFile(FileName);
      writeln('Scikit Boston Samples: '+itoa(sl.count-1))
       FOR I:= 0 TO SL.Count-1 DO BEGIN
        S:=SL[(I)];
        SLtemp.CommaText:= S;
        for sll:= start-1 to col-1 do   
          SetValue(Result,I,Sll,StrToFloat(sltemp[sll]))
       END;
   FINALLY
      sltemp.free;
      SL.Free
   END
于 2018-03-26T13:55:15.553 回答