fpspreadsheet: Add test case for switched decimal and thousand separators in csv file. Fix bugs related to that.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3674 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-10-20 23:17:07 +00:00
parent e8351807cc
commit 8891b0a90b
4 changed files with 59 additions and 32 deletions

View File

@ -561,7 +561,7 @@ begin
if CSVParams.NumberFormat <> '' then if CSVParams.NumberFormat <> '' then
s := Format(CSVParams.NumberFormat, [AValue], CSVParams.FormatSettings) s := Format(CSVParams.NumberFormat, [AValue], CSVParams.FormatSettings)
else else
s := FWorksheet.ReadAsUTF8Text(ACell); s := FWorksheet.ReadAsUTF8Text(ACell, CSVParams.FormatSettings);
AppendToStream(AStream, s); AppendToStream(AStream, s);
end; end;

View File

@ -523,6 +523,7 @@ type
{ Reading of values } { Reading of values }
function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload; function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; overload;
function ReadAsUTF8Text(ACell: PCell): ansistring; overload; function ReadAsUTF8Text(ACell: PCell): ansistring; overload;
function ReadAsUTF8Text(ACell: PCell; AFormatSettings: TFormatSettings): ansistring; overload;
function ReadAsNumber(ARow, ACol: Cardinal): Double; overload; function ReadAsNumber(ARow, ACol: Cardinal): Double; overload;
function ReadAsNumber(ACell: PCell): Double; overload; function ReadAsNumber(ACell: PCell): Double; overload;
function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload; function ReadAsDateTime(ARow, ACol: Cardinal; out AResult: TDateTime): Boolean; overload;
@ -2384,23 +2385,26 @@ end;
@return The text representation of the cell @return The text representation of the cell
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
begin
Result := ReadAsUTF8Text(ACell, FWorkbook.FormatSettings);
end;
function FloatToStrNoNaN(const Value: Double; function TsWorksheet.ReadAsUTF8Text(ACell: PCell;
AFormatSettings: TFormatSettings): ansistring;
function FloatToStrNoNaN(const AValue: Double;
ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring; ANumberFormat: TsNumberFormat; ANumberFormatStr: string): ansistring;
var
fs: TFormatSettings;
begin begin
fs := FWorkbook.FormatSettings; if IsNan(AValue) then
if IsNan(Value) then
Result := '' Result := ''
else else
if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then
Result := FloatToStr(Value, fs) Result := FloatToStr(AValue, AFormatSettings)
else else
if (ANumberFormat = nfPercentage) then if (ANumberFormat = nfPercentage) then
Result := FormatFloat(ANumberFormatStr, Value*100, fs) Result := FormatFloat(ANumberFormatStr, AValue*100, AFormatSettings)
else else
Result := FormatFloat(ANumberFormatStr, Value, fs) Result := FormatFloat(ANumberFormatStr, AValue, AFormatSettings)
end; end;
function DateTimeToStrNoNaN(const Value: Double; function DateTimeToStrNoNaN(const Value: Double;
@ -2414,15 +2418,15 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring;
if (ANumberFormat = nfGeneral) then if (ANumberFormat = nfGeneral) then
begin begin
if frac(Value) = 0 then // date only if frac(Value) = 0 then // date only
ANumberFormatStr := Workbook.FormatSettings.ShortDateFormat ANumberFormatStr := AFormatSettings.ShortDateFormat
else if trunc(Value) = 0 then // time only else if trunc(Value) = 0 then // time only
ANumberFormatStr := Workbook.FormatSettings.LongTimeFormat ANumberFormatStr := AFormatSettings.LongTimeFormat
else else
ANumberFormatStr := 'cc' ANumberFormatStr := 'cc'
end else end else
if ANumberFormatStr = '' then if ANumberFormatStr = '' then
ANumberFormatStr := BuildDateTimeFormatString(ANumberFormat, ANumberFormatStr := BuildDateTimeFormatString(ANumberFormat,
Workbook.FormatSettings, ANumberFormatStr); AFormatSettings, ANumberFormatStr);
// Saw strange cases in ods where date/time formats contained pos/neg/zero parts. // Saw strange cases in ods where date/time formats contained pos/neg/zero parts.
// Split to be on the safe side. // Split to be on the safe side.

View File

@ -119,8 +119,6 @@ function SpecialDateTimeFormat(ACode: String;
procedure SplitFormatString(const AFormatString: String; out APositivePart, procedure SplitFormatString(const AFormatString: String; out APositivePart,
ANegativePart, AZeroPart: String); ANegativePart, AZeroPart: String);
procedure MakeTimeIntervalMask(Src: String; var Dest: String); procedure MakeTimeIntervalMask(Src: String; var Dest: String);
// These two functions are copies of fpc trunk until they are available in stable fpc. // These two functions are copies of fpc trunk until they are available in stable fpc.
@ -1558,10 +1556,11 @@ begin
dec(i); dec(i);
while i >= 1 do while i >= 1 do
begin begin
if not (AText[i] in ['0'..'9', '+', '-']) then if not (AText[i] in ['0'..'9', '+', '-', '.', ',']) then
exit; exit;
// If we find the testSep character again it must be a thousand separator. // If we find the testSep character again it must be a thousand separator,
// and there are no decimals.
if (AText[i] = testSep) then if (AText[i] = testSep) then
begin begin
// ... but only if there are 3 numerical digits in between // ... but only if there are 3 numerical digits in between
@ -1573,8 +1572,8 @@ begin
fs.DecimalSeparator := ',' fs.DecimalSeparator := ','
else else
fs.DecimalSeparator := '.'; fs.DecimalSeparator := '.';
ADecimalSeparator := fs.DecimalSeparator;
AThousandSeparator := fs.ThousandSeparator; AThousandSeparator := fs.ThousandSeparator;
ADecimalSeparator := #0; // this indicates that there are no decimals
done := true; done := true;
i := 0; i := 0;
end else end else

View File

@ -65,7 +65,8 @@ type
// Test word wrapping // Test word wrapping
procedure TestWriteRead_WordWrap(AFormat: TsSpreadsheetFormat); procedure TestWriteRead_WordWrap(AFormat: TsSpreadsheetFormat);
// Test number formats // Test number formats
procedure TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat); procedure TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat;
AVariant: Integer = 0);
// Repeat with date/times // Repeat with date/times
procedure TestWriteRead_DateTimeFormats(AFormat: TsSpreadsheetFormat); procedure TestWriteRead_DateTimeFormats(AFormat: TsSpreadsheetFormat);
// Test merged cells // Test merged cells
@ -137,13 +138,14 @@ type
procedure TestWriteRead_OOXML_WordWrap; procedure TestWriteRead_OOXML_WordWrap;
{ CSV Tests } { CSV Tests }
procedure TestWriteRead_CSV_NumberFormats; procedure TestWriteRead_CSV_NumberFormats_0;
procedure TestWriteRead_CSV_NumberFormats_1;
end; end;
implementation implementation
uses uses
TypInfo, fpsutils; TypInfo, fpsutils, fpscsv;
const const
FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet FmtNumbersSheet = 'NumbersFormat'; //let's distinguish it from the regular numbers sheet
@ -165,14 +167,18 @@ var
i: Integer; i: Integer;
fs: TFormatSettings; fs: TFormatSettings;
myworkbook: TsWorkbook; myworkbook: TsWorkbook;
ch: Char;
begin begin
// Set up norm - MUST match spreadsheet cells exactly // Set up norm - MUST match spreadsheet cells exactly
// The workbook uses a slightly modified copy of the DefaultFormatSettings // The workbook uses a slightly modified copy of the DefaultFormatSettings
// We create a copy here in order to better define the predicted strings. // We create a copy here in order to better define the predicted strings.
myWorkbook := TsWorkbook.Create; myWorkbook := TsWorkbook.Create;
fs := MyWorkbook.FormatSettings; try
myWorkbook.Free; fs := MyWorkbook.FormatSettings;
finally
myWorkbook.Free;
end;
// Numbers // Numbers
SollNumbers[0] := 0.0; SollNumbers[0] := 0.0;
@ -299,21 +305,33 @@ end;
{ --- Number format tests --- } { --- Number format tests --- }
procedure TSpreadWriteReadFormatTests.TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat); procedure TSpreadWriteReadFormatTests.TestWriteRead_NumberFormats(AFormat: TsSpreadsheetFormat;
AVariant: Integer = 0);
var var
MyWorksheet: TsWorksheet; MyWorksheet: TsWorksheet;
MyWorkbook: TsWorkbook; MyWorkbook: TsWorkbook;
ActualString: String; ActualString: String;
ExpectedString: String;
Row, Col: Integer; Row, Col: Integer;
TempFile: string; //write xls/xml to this file and read back from it TempFile: string; //write xls/xml to this file and read back from it
begin begin
{// Not needed: use workbook.writetofile with overwrite=true
if fileexists(TempFile) then
DeleteFile(TempFile);
}
// Write out all test values // Write out all test values
MyWorkbook := TsWorkbook.Create; MyWorkbook := TsWorkbook.Create;
try try
if (AFormat = sfCSV) then
begin
case AVariant of
0: begin
CSVParams.FormatSettings.DecimalSeparator := MyWorkbook.FormatSettings.DecimalSeparator;
CSVParams.FormatSettings.ThousandSeparator := MyWorkbook.FormatSettings.ThousandSeparator;
end;
1: begin // interchanged decimal and thousand separators
CSVParams.FormatSettings.ThousandSeparator := MyWorkbook.FormatSettings.DecimalSeparator;
CSVParams.FormatSettings.DecimalSeparator := MyWorkbook.FormatSettings.ThousandSeparator;
end;
end;
end;
MyWorkSheet:= MyWorkBook.AddWorksheet(FmtNumbersSheet); MyWorkSheet:= MyWorkBook.AddWorksheet(FmtNumbersSheet);
for Row := Low(SollNumbers) to High(SollNumbers) do for Row := Low(SollNumbers) to High(SollNumbers) do
for Col := ord(Low(SollNumberFormats)) to ord(High(SollNumberFormats)) do for Col := ord(Low(SollNumberFormats)) to ord(High(SollNumberFormats)) do
@ -343,17 +361,18 @@ begin
for Col := Low(SollNumberFormats) to High(SollNumberFormats) do for Col := Low(SollNumberFormats) to High(SollNumberFormats) do
begin begin
ActualString := MyWorkSheet.ReadAsUTF8Text(Row,Col); ActualString := MyWorkSheet.ReadAsUTF8Text(Row,Col);
if (SollNumberStrings[Row,Col] <> ActualString) then ExpectedString := SollNumberStrings[Row, Col];
if (ExpectedString <> ActualString) then
begin begin
if (AFormat = sfCSV) and (Row=5) and (Col=0) then if (AFormat = sfCSV) and (Row=5) and (Col=0) then
// CSV has an insignificant difference of tiny numbers in // CSV has an insignificant difference of tiny numbers in
// general format // general format
ignore('Ignoring insignificant saved string mismatch, cell ' + ignore('Ignoring insignificant saved string mismatch, cell ' +
CellNotation(MyWorksheet,Row,Col) + CellNotation(MyWorksheet,Row,Col) +
', expected: <' + SollNumberStrings[Row,Col] + ', expected: <' + ExpectedString +
'> but was: <' + ActualString + '>') '> but was: <' + ActualString + '>')
else else
CheckEquals(SollNumberStrings[Row,Col], ActualString, CheckEquals(ExpectedString, ActualString,
'Test saved string mismatch, cell '+CellNotation(MyWorkSheet,Row,Col)); 'Test saved string mismatch, cell '+CellNotation(MyWorkSheet,Row,Col));
end; end;
end; end;
@ -388,9 +407,14 @@ begin
TestWriteRead_NumberFormats(sfOOXML); TestWriteRead_NumberFormats(sfOOXML);
end; end;
procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_NumberFormats; procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_NumberFormats_0;
begin begin
TestWriteRead_NumberFormats(sfCSV); TestWriteRead_NumberFormats(sfCSV, 0);
end;
procedure TSpreadWriteReadFormatTests.TestWriteRead_CSV_NumberFormats_1;
begin
TestWriteRead_NumberFormats(sfCSV, 1);
end; end;