fpspreadsheet: Introduce string encoding when reading/writing csv files. Automatic detection of currency working now (€ symbol is detected correctly now). Use UTF8 encoded format settings.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3687 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2014-10-24 20:44:37 +00:00
parent 391cd0a647
commit c4d1e08656
7 changed files with 249 additions and 220 deletions

View File

@ -10,11 +10,11 @@ object CSVParamsForm: TCSVParamsForm
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
Position = poMainFormCenter
LCLVersion = '1.2.6.0'
LCLVersion = '1.3'
object ButtonPanel: TButtonPanel
Left = 6
Height = 38
Top = 511
Height = 34
Top = 515
Width = 458
OKButton.Name = 'OKButton'
OKButton.DefaultCaption = True
@ -29,19 +29,19 @@ object CSVParamsForm: TCSVParamsForm
end
object PageControl: TPageControl
Left = 8
Height = 495
Height = 499
Top = 8
Width = 454
ActivePage = PgDateTimeParams
ActivePage = PgGeneralParams
Align = alClient
BorderSpacing.Around = 8
MultiLine = True
TabIndex = 3
TabIndex = 0
TabOrder = 1
Options = [nboMultiLine]
object PgGeneralParams: TTabSheet
Caption = 'General'
ClientHeight = 410
ClientHeight = 471
ClientWidth = 446
object LblQuoteChar: TLabel
Left = 16
@ -54,10 +54,10 @@ object CSVParamsForm: TCSVParamsForm
end
object CbQuoteChar: TComboBox
Left = 156
Height = 28
Height = 23
Top = 80
Width = 155
ItemHeight = 20
Width = 275
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'none'
@ -70,10 +70,10 @@ object CSVParamsForm: TCSVParamsForm
end
object CbDelimiter: TComboBox
Left = 156
Height = 28
Height = 23
Top = 16
Width = 155
ItemHeight = 20
Width = 275
ItemHeight = 15
ItemIndex = 4
Items.Strings = (
'Comma ( , )'
@ -106,10 +106,10 @@ object CSVParamsForm: TCSVParamsForm
end
object CbLineEnding: TComboBox
Left = 156
Height = 28
Height = 23
Top = 48
Width = 155
ItemHeight = 20
Width = 275
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'System'
@ -122,10 +122,10 @@ object CSVParamsForm: TCSVParamsForm
Text = 'System'
end
object RgDetectContentType: TRadioGroup
Left = 19
Left = 16
Height = 80
Top = 128
Width = 292
Top = 156
Width = 415
AutoFill = True
Caption = 'Conversion of strings after reading'
ChildSizing.LeftRightSpacing = 6
@ -135,8 +135,8 @@ object CSVParamsForm: TCSVParamsForm
ChildSizing.ShrinkVertical = crsScaleChilds
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1
ClientHeight = 58
ClientWidth = 288
ClientHeight = 60
ClientWidth = 411
ItemIndex = 1
Items.Strings = (
'Do not convert, strings are sufficient'
@ -144,16 +144,35 @@ object CSVParamsForm: TCSVParamsForm
)
TabOrder = 3
end
object LbEncoding: TLabel
Left = 16
Height = 15
Top = 116
Width = 87
Caption = 'String encoding:'
FocusControl = CbEncoding
ParentColor = False
end
object CbEncoding: TComboBox
Left = 156
Height = 23
Top = 112
Width = 275
DropDownCount = 32
ItemHeight = 15
Style = csDropDownList
TabOrder = 4
end
end
object PgNumberParams: TTabSheet
Caption = 'Number cells'
ClientHeight = 410
ClientHeight = 471
ClientWidth = 446
object CbAutoDetectNumberFormat: TCheckBox
Left = 16
Height = 24
Height = 19
Top = 16
Width = 248
Width = 200
Caption = 'Try to auto-detect number format'
Checked = True
State = cbChecked
@ -161,16 +180,16 @@ object CSVParamsForm: TCSVParamsForm
end
object EdNumFormat: TEdit
Left = 232
Height = 28
Height = 23
Top = 140
Width = 194
TabOrder = 3
end
object LblNumFormat: TLabel
Left = 17
Height = 20
Height = 15
Top = 144
Width = 225
Width = 182
Caption = 'Format string for writing numbers:'
FocusControl = EdNumFormat
ParentColor = False
@ -191,23 +210,23 @@ object CSVParamsForm: TCSVParamsForm
end
object LblDecimalSeparator: TLabel
Left = 16
Height = 20
Height = 15
Top = 59
Width = 125
Width = 98
Caption = 'Decimal separator:'
FocusControl = CbDecimalSeparator
ParentColor = False
end
object CbDecimalSeparator: TComboBox
Left = 232
Height = 28
Height = 23
Top = 56
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
'Dot ( . )'
'Period ( . )'
'Comma ( , )'
)
TabOrder = 1
@ -215,23 +234,23 @@ object CSVParamsForm: TCSVParamsForm
end
object LblThousandSeparator: TLabel
Left = 16
Height = 20
Height = 15
Top = 91
Width = 134
Width = 108
Caption = 'Thousand separator:'
FocusControl = CbThousandSeparator
ParentColor = False
end
object CbThousandSeparator: TComboBox
Left = 232
Height = 28
Height = 23
Top = 88
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
'Dot ( . )'
'Period ( . )'
'Comma ( , )'
'Space ( )'
)
@ -241,20 +260,20 @@ object CSVParamsForm: TCSVParamsForm
end
object PgCurrency: TTabSheet
Caption = 'Currency cells'
ClientHeight = 410
ClientHeight = 471
ClientWidth = 446
object LblCurrencySymbol: TLabel
Left = 16
Height = 20
Height = 15
Top = 20
Width = 112
Width = 93
Caption = 'Currency symbol:'
FocusControl = EdCurrencySymbol
ParentColor = False
end
object EdCurrencySymbol: TEdit
Left = 232
Height = 28
Height = 23
Top = 16
Width = 194
OnEnter = DateTimeFormatChange
@ -264,39 +283,39 @@ object CSVParamsForm: TCSVParamsForm
end
object PgDateTimeParams: TTabSheet
Caption = 'Date/time cells'
ClientHeight = 437
ClientHeight = 471
ClientWidth = 446
object LblNumFormat1: TLabel
Left = 16
Height = 20
Height = 15
Top = 20
Width = 160
Width = 128
Caption = 'Long date format string:'
ParentColor = False
end
object LblNumFormat2: TLabel
Left = 16
Height = 20
Height = 15
Top = 52
Width = 162
Width = 129
Caption = 'Short date format string:'
ParentColor = False
end
object LblDecimalSeparator1: TLabel
Left = 16
Height = 20
Height = 15
Top = 83
Width = 102
Width = 79
Caption = 'Date separator:'
FocusControl = CbDateSeparator
ParentColor = False
end
object CbDateSeparator: TComboBox
Left = 232
Height = 28
Height = 23
Top = 80
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
@ -311,35 +330,35 @@ object CSVParamsForm: TCSVParamsForm
end
object LblNumFormat3: TLabel
Left = 16
Height = 20
Height = 15
Top = 268
Width = 160
Width = 129
Caption = 'Long time format string:'
ParentColor = False
end
object LblNumFormat4: TLabel
Left = 16
Height = 20
Height = 15
Top = 300
Width = 162
Width = 130
Caption = 'Short time format string:'
ParentColor = False
end
object LblDecimalSeparator2: TLabel
Left = 16
Height = 20
Height = 15
Top = 331
Width = 103
Width = 82
Caption = 'Time separator:'
FocusControl = CbTimeSeparator
ParentColor = False
end
object CbTimeSeparator: TComboBox
Left = 232
Height = 28
Height = 23
Top = 328
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
@ -355,42 +374,42 @@ object CSVParamsForm: TCSVParamsForm
end
object LblLongMonthNames: TLabel
Left = 16
Height = 20
Height = 15
Top = 116
Width = 130
Width = 107
Caption = 'Long month names:'
ParentColor = False
end
object LblShortMonthNames: TLabel
Left = 16
Height = 20
Height = 15
Top = 148
Width = 132
Width = 108
Caption = 'Short month names:'
ParentColor = False
end
object LblLongDayNames: TLabel
Left = 16
Height = 20
Height = 15
Top = 180
Width = 111
Width = 90
Caption = 'Long day names:'
ParentColor = False
end
object LblShortDayNames: TLabel
Left = 16
Height = 20
Height = 15
Top = 212
Width = 113
Width = 91
Caption = 'Short day names:'
ParentColor = False
end
object CbLongDateFormat: TComboBox
Left = 232
Height = 28
Height = 23
Top = 16
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
@ -416,10 +435,10 @@ object CSVParamsForm: TCSVParamsForm
end
object CbShortDateFormat: TComboBox
Left = 232
Height = 28
Height = 23
Top = 48
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
@ -440,10 +459,10 @@ object CSVParamsForm: TCSVParamsForm
end
object CbLongTimeFormat: TComboBox
Left = 232
Height = 28
Height = 23
Top = 264
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
@ -458,10 +477,10 @@ object CSVParamsForm: TCSVParamsForm
end
object CbShortTimeFormat: TComboBox
Left = 232
Height = 28
Height = 23
Top = 296
Width = 194
ItemHeight = 20
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'like spreadsheet'
@ -480,7 +499,7 @@ object CSVParamsForm: TCSVParamsForm
Top = 366
Width = 409
Caption = 'Sample'
ClientHeight = 36
ClientHeight = 38
ClientWidth = 405
TabOrder = 6
object LblDateTimeSample: TLabel
@ -498,7 +517,7 @@ object CSVParamsForm: TCSVParamsForm
end
object PgBoolParams: TTabSheet
Caption = 'Boolean cells'
ClientHeight = 410
ClientHeight = 471
ClientWidth = 446
object EdTRUE: TEdit
Left = 16

View File

@ -19,6 +19,7 @@ type
CbAutoDetectNumberFormat: TCheckBox;
CbLongDateFormat: TComboBox;
CbLongTimeFormat: TComboBox;
CbEncoding: TComboBox;
EdCurrencySymbol: TEdit;
CbShortTimeFormat: TComboBox;
CbShortDateFormat: TComboBox;
@ -42,6 +43,7 @@ type
LblDecimalSeparator1: TLabel;
LblDecimalSeparator2: TLabel;
LblCurrencySymbol: TLabel;
LbEncoding: TLabel;
LblShortMonthNames: TLabel;
LblLongDayNames: TLabel;
LblShortDayNames: TLabel;
@ -90,16 +92,11 @@ var
implementation
uses
fpsUtils;
LConvEncoding, fpsUtils;
resourcestring
rsLikeSpreadsheet = 'like spreadsheet';
{
const
CURR_VALUE = 100.0;
}
var
CSVParamsPageIndex: Integer = 0;
@ -135,7 +132,7 @@ var
arr: Array[1..12] of String;
i: Integer;
begin
fs := DefaultFormatSettings;
fs := UTF8FormatSettings;
if CbLongDateFormat.ItemIndex <> 0 then
fs.LongDateFormat := CbLongDateFormat.Text;
if CbShortDateFormat.ItemIndex <> 0 then
@ -214,40 +211,44 @@ begin
CSVParamsPageIndex := PageControl.ActivePageIndex;
end;
(*
procedure TCSVParamsForm.EdCurrencySymbolChange(Sender: TObject);
var
sel: Integer;
begin
sel := CbPosCurrencyFormat.ItemIndex;
CbPosCurrencyFormat.Items.BeginUpdate;
try
CbPosCurrencyFormat.Items.Clear;
BuildCurrencyFormatList(CbPosCurrencyFormat.Items, true, CURR_VALUE, GetCurrencySymbol);
CbPosCurrencyFormat.Items.Insert(0, rsLikeSpreadsheet);
CbPosCurrencyFormat.ItemIndex := sel;
finally
CbPosCurrencyFormat.Items.EndUpdate;
end;
sel := CbNegCurrencyFormat.ItemIndex;
CbNegCurrencyFormat.Items.BeginUpdate;
try
CbNegCurrencyFormat.Items.Clear;
BuildCurrencyFormatList(CbNegCurrencyFormat.Items, false, CURR_VALUE, GetCurrencySymbol);
CbNegCurrencyFormat.Items.Insert(0, rsLikeSpreadsheet);
CbNegCurrencyFormat.ItemIndex := sel;
finally
CbNegCurrencyFormat.Items.EndUpdate;
end;
end;
*)
procedure TCSVParamsForm.FormCreate(Sender: TObject);
begin
PageControl.ActivePageIndex := CSVParamsPageIndex;
// CbNegCurrencyFormat.DropdownCount := 32;
// Populate encoding combobox. Done in code because of the conditional define.
with CbEncoding.Items do begin
Add('automatic / UTF8');
Add('UTF8');
Add('UTF8 with BOM');
Add('ISO_8859_1 (Central Europe)');
Add('ISO_8859_15 (Western European languages)');
Add('ISO_8859_2 (Eastern Europe)');
Add('CP1250 (Central Europe)');
Add('CP1251 (Cyrillic)');
Add('CP1252 (Latin 1)');
Add('CP1253 (Greek)');
Add('CP1254 (Turkish)');
Add('CP1255 (Hebrew)');
Add('CP1256 (Arabic)');
Add('CP1257 (Baltic)');
Add('CP1258 (Vietnam)');
Add('CP437 (DOS central Europe)');
Add('CP850 (DOS western Europe)');
Add('CP852 (DOS central Europe)');
Add('CP866 (DOS and Windows console''s cyrillic)');
Add('CP874 (Thai)');
{$IFNDEF DisableAsianCodePages}
// Asian encodings
Add('CP932 (Japanese)');
Add('CP936 (Chinese)');
Add('CP949 (Korea)');
Add('CP950 (Chinese Complex)');
{$ENDIF}
Add('KOI8 (Russian cyrillic)');
Add('UCS2LE (UCS2-LE 2byte little endian)');
Add('UCS2BE (UCS2-BE 2byte big endian)');
end;
CbEncoding.ItemIndex := 0;
FEdLongMonthNames := TMonthDayNamesEdit.Create(self);
with FEdLongMonthNames do
@ -305,12 +306,14 @@ begin
end;
LblShortDayNames.FocusControl := FEdShortDayNames;
FDateFormatSample := DefaultFormatSettings.LongDateFormat;
FTimeFormatSample := DefaultFormatSettings.LongTimeFormat;
FDateFormatSample := UTF8FormatSettings.LongDateFormat;
FTimeFormatSample := UTF8FormatSettings.LongTimeFormat;
FSampleDateTime := now();
end;
procedure TCSVParamsForm.GetParams(var AParams: TsCSVParams);
var
s: String;
begin
// Line endings
case CbLineEnding.ItemIndex of
@ -336,6 +339,17 @@ begin
2: AParams.QuoteChar := '''';
end;
// Encoding
if CbEncoding.ItemIndex = 0 then
AParams.Encoding := ''
else if CbEncoding.ItemIndex = 1 then
AParams.Encoding := EncodingUTF8BOM
else
begin
s := CbEncoding.Items[CbEncoding.ItemIndex];
AParams.Encoding := Copy(s, 1, Pos(' ', s)-1);
end;
// Detect content type and convert
AParams.DetectContentType := RgDetectContentType.ItemIndex <> 0;
@ -355,7 +369,7 @@ begin
if (EdCurrencySymbol.Text = '') or (EdCurrencySymbol.Text = rsLikeSpreadsheet) then
AParams.FormatSettings.CurrencyString := ''
else
AParams.FormatSettings.CurrencyString := UTF8ToAnsi(EdCurrencySymbol.Text);
AParams.FormatSettings.CurrencyString := EdCurrencySymbol.Text;
// Long date format string
if (CbLongDateFormat.ItemIndex = 0) or (CbLongDateFormat.Text = '') then
@ -407,6 +421,9 @@ begin
end;
procedure TCSVParamsForm.SetParams(const AParams: TsCSVParams);
var
s: String;
i: Integer;
begin
// Line endings
case AParams.LineEnding of
@ -432,6 +449,22 @@ begin
'''' : CbQuoteChar.ItemIndex := 2;
end;
// String encoding
if AParams.Encoding = '' then
CbEncoding.ItemIndex := 0
else if AParams.Encoding = EncodingUTF8BOM then
CbEncoding.ItemIndex := 1
else
for i:=1 to CbEncoding.Items.Count-1 do
begin
s := CbEncoding.Items[i];
if SameText(AParams.Encoding, Copy(s, 1, Pos(' ', s)-1)) then
begin
CbEncoding.ItemIndex := i;
break;
end;
end;
// Detect content type
RgDetectContentType.ItemIndex := ord(AParams.DetectContentType);
@ -462,7 +495,7 @@ begin
if AParams.FormatSettings.CurrencyString = '' then
EdCurrencySymbol.Text := rsLikeSpreadsheet
else
EdCurrencySymbol.Text := AnsiToUTF8(AParams.FormatSettings.CurrencyString);
EdCurrencySymbol.Text := AParams.FormatSettings.CurrencyString;
// Long date format
if AParams.FormatSettings.LongDateFormat = '' then

View File

@ -171,7 +171,7 @@ begin
L.DelimitedText := Text;
for i:=0 to L.Count-1 do
if i < L.Count then
TMonthNameArray(ANamesArray)[i+1] := UTF8ToAnsi(L[i]);
TMonthNameArray(ANamesArray)[i+1] := L[i];
finally
L.Free;
end;
@ -189,8 +189,8 @@ begin
FShortNames := IsShortNames;
case FCount of
7 : Text := AnsiToUTF8(DayNamesToString(TWeekNameArray(ANamesArray), AEmptyString));
12: Text := AnsiToUTF8(MonthNamesToString(TMonthNameArray(ANamesArray), AEmptyString));
7: Text := DayNamesToString(TWeekNameArray(ANamesArray), AEmptyString);
12: Text := MonthNamesToString(TMonthNameArray(ANamesArray), AEmptyString);
else raise Exception.Create('[TMonthDayNameEdit] Array length can only be 7 or 12.');
end;
end;

View File

@ -106,39 +106,39 @@ begin
if (ctrl = CbLongDateFormat) then
begin
FDateFormatSample := fs.LongDateFormat;
s := AnsiToUTF8(FormatDateTime(FDateFormatSample, dt, fs));
s := FormatDateTime(FDateFormatSample, dt, fs);
LblDateTimeSample.Caption := 'Sample date:'#13 + s;
end
else
if (ctrl = CbShortDateFormat) then
begin
FDateFormatSample := fs.ShortDateFormat;
s := AnsiToUTF8(FormatDateTime(FDateFormatSample, dt, fs));
s := FormatDateTime(FDateFormatSample, dt, fs);
LblDateTimeSample.Caption := 'Sample date:'#13 + s;
end
else
if (ctrl = FCbDateSeparator) then begin
s := AnsiToUTF8(FormatDateTime(FDateFormatSample, dt, fs));
s := FormatDateTime(FDateFormatSample, dt, fs);
LblDateTimeSample.Caption := 'Sample date:'#13 + s;
end
else
if (ctrl = CbLongTimeFormat) then
begin
FTimeFormatSample := fs.LongTimeFormat;
s := AnsiToUTF8(FormatDateTime(FTimeFormatSample, dt, fs));
s := FormatDateTime(FTimeFormatSample, dt, fs);
LblDateTimeSample.Caption := 'Sample time:'#13 + s;
end
else
if (ctrl = CbShortTimeFormat) then
begin
FTimeFormatSample := fs.ShortTimeFormat;
s := AnsiToUTF8(FormatDateTime(FTimeFormatSample, dt, fs));
s := FormatDateTime(FTimeFormatSample, dt, fs);
LblDateTimeSample.Caption := 'Sample time:'#13 + s;
end
else
if (ctrl = FCbTimeSeparator) then
begin
s := AnsiToUTF8(FormatDateTime(FTimeFormatSample, dt, fs));
s := FormatDateTime(FTimeFormatSample, dt, fs);
LblDateTimeSample.Caption := 'Sample time:'#13 + s;
{
end
@ -337,7 +337,7 @@ begin
// --- Currency format parameters ---
// Currency symbol
Result.CurrencyString := UTF8ToAnsi(EdCurrencySymbol.Text);
Result.CurrencyString := EdCurrencySymbol.Text;
// Currency decimal places
Result.CurrencyDecimals := EdCurrencyDecimals.Value;
// Positive currency format
@ -380,7 +380,7 @@ begin
// --- Currency format parameters ---
// Currency symbol
EdCurrencySymbol.Text := AnsiToUTF8(AValue.CurrencyString);
EdCurrencySymbol.Text := AValue.CurrencyString;
// Currency decimal places
EdCurrencyDecimals.Value := AValue.CurrencyDecimals;
// Positive currency format

View File

@ -33,7 +33,7 @@ type
TsCSVWriter = class(TsCustomSpreadWriter)
private
FCSVBuilder: TCSVBuilder;
FLineEnding: String;
FEncoding: String;
protected
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override;
@ -62,6 +62,7 @@ type
LineEnding: TsCSVLineEnding; // W: Specification for line ending to be written
Delimiter: Char; // RW: Column delimiter
QuoteChar: Char; // RW: Character for quoting texts
Encoding: String; // RW: Encoding of file
DetectContentType: Boolean; // R: try to convert strings to content types
NumberFormat: String; // W: if empty write numbers like in sheet, otherwise use this format
AutoDetectNumberFormat: Boolean; // R: automatically detects decimal/thousand separator used in numbers
@ -76,6 +77,7 @@ var
LineEnding: leSystem;
Delimiter: ';';
QuoteChar: '"';
Encoding: ''; // '' = auto-detect when reading, UTF8 when writing
DetectContentType: true;
NumberFormat: '';
AutoDetectNumberFormat: true;
@ -89,7 +91,7 @@ function LineEndingAsString(ALineEnding: TsCSVLineEnding): String;
implementation
uses
StrUtils, DateUtils, fpsutils;
StrUtils, DateUtils, LConvEncoding, Math, fpsutils;
{ Initializes the FormatSettings of the CSVParams to default values which
can be replaced by the FormatSettings of the workbook's FormatSettings }
@ -370,12 +372,12 @@ begin
// No idea how to apply the date/time formatsettings here...
if IsDateTime(AText, dtValue) then
begin
if dtValue < 1.0 then // this is a time alone
if dtValue < 1.0 then // this is a time alone
nf := nfLongTime
else
if frac(dtValue) = 0.0 then // this is a date alone
nf := nfShortDate
else // this is date + time
else // this is date + time
nf := nfShortDateTime;
FWorksheet.WriteDateTime(ARow, ACol, dtValue, nf);
exit;
@ -406,10 +408,23 @@ end;
procedure TsCSVReader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var
Parser: TCSVParser;
s: String;
i: Integer;
encoding: String;
begin
// Try to determine encoding of the input file
SetLength(s, Min(1000, AStream.Size));
AStream.ReadBuffer(s[1], Length(s));
if CSVParams.Encoding = '' then
encoding := GuessEncoding(s)
else
encoding := CSVParams.Encoding;
// Store workbook for internal use, and create worksheet
FWorkbook := AData;
FWorksheet := AData.AddWorksheet(FWorksheetName);
// Create csv parser, read file and store in worksheet
Parser := TCSVParser.Create;
try
Parser.Delimiter := CSVParams.Delimiter;
@ -418,64 +433,17 @@ begin
// Indicate column counts between rows may differ:
Parser.EqualColCountPerRow := false;
Parser.SetSource(AStream);
while Parser.ParseNextCell do
ReadCellValue(Parser.CurrentRow, Parser.CurrentCol, Parser.CurrentCellText);
while Parser.ParseNextCell do begin
// Convert string to UTF8
s := Parser.CurrentCellText;
s := ConvertEncoding(s, encoding, EncodingUTF8);
ReadCellValue(Parser.CurrentRow, Parser.CurrentCol, s);
end;
finally
Parser.Free;
end;
end;
{
procedure TsCSVReader.ReadFromStream(AStream: TStream; AData: TsWorkbook);
var
n: Int64;
ch: Char;
nextch: Char;
cellValue: String;
r, c: Cardinal;
begin
FWorkbook := AData;
FWorksheet := AData.AddWorksheet(FWorksheetName);
n := AStream.Size;
cellValue := '';
r := 0;
c := 0;
while AStream.Position < n do begin
ch := char(AStream.ReadByte);
if (CSVParams.QuoteChar <> #0) and (ch = CSVParams.QuoteChar) then
begin
// Begin of quoted string --> read until next quote; this allows line breaks
// and column separators in quoted string!
cellValue := cellValue + ch;
repeat
ch := char(AStream.ReadByte);
cellValue := cellValue + ch;
until (AStream.Position = n) or (ch = CSVParams.QuoteChar);
end else
if ch = CSVParams.Delimiter then begin
// End of column reached
ReadCellValue(r, c, cellValue);
inc(c);
cellValue := '';
end else
if (ch = #13) or (ch = #10) then begin
// End of row reached
ReadCellValue(r, c, cellValue);
inc(r);
c := 0;
cellValue := '';
// look for CR+LF: if true, skip next byte
if AStream.Position+1 < n then begin
nextch := char(AStream.ReadByte);
if ((ch = #13) and (nextch <> #10)) then
AStream.Position := AStream.Position - 1; // re-read nextchar in next loop
end;
end else
cellValue := cellValue + ch;
end;
end;
}
procedure TsCSVReader.ReadFromStrings(AStrings: TStrings; AData: TsWorkbook);
var
Stream: TStringStream;
@ -507,12 +475,10 @@ constructor TsCSVWriter.Create(AWorkbook: TsWorkbook);
begin
inherited Create(AWorkbook);
ReplaceFormatSettings(CSVParams.FormatSettings, FWorkbook.FormatSettings);
case CSVParams.LineEnding of
leSystem : FLineEnding := LineEnding;
leCRLF : FLineEnding := #13#10;
leCR : FLineEnding := #13;
leLF : FLineEnding := #10;
end;
if CSVParams.Encoding = '' then
FEncoding := 'utf8'
else
FEncoding := CSVParams.Encoding;
end;
procedure TsCSVWriter.WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
@ -526,29 +492,30 @@ end;
{ Write boolean cell to stream formatted as string }
procedure TsCSVWriter.WriteBool(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: Boolean; ACell: PCell);
var
s: String;
begin
Unused(AStream);
Unused(ARow, ACol, ACell);
if AValue then
FCSVBuilder.AppendCell(CSVParams.TrueText)
s := CSVParams.TrueText
else
FCSVBuilder.AppendCell(CSVParams.FalseText);
{
if AValue then
AppendToStream(AStream, CSVParams.TrueText)
else
AppendToStream(AStream, CSVParams.FalseText);
}
s := CSVParams.FalseText;
s := ConvertEncoding(s, EncodingUTF8, FEncoding);
FCSVBuilder.AppendCell(s);
end;
{ Write date/time values in the same way they are displayed in the sheet }
procedure TsCSVWriter.WriteDateTime(AStream: TStream; const ARow, ACol: Cardinal;
const AValue: TDateTime; ACell: PCell);
var
s: String;
begin
Unused(AStream);
Unused(ARow, ACol, AValue);
FCSVBuilder.AppendCell(FWorksheet.ReadAsUTF8Text(ACell));
// AppendToStream(AStream, FWorksheet.ReadAsUTF8Text(ACell));
s := FWorksheet.ReadAsUTF8Text(ACell);
s := ConvertEncoding(s, EncodingUTF8, FEncoding);
FCSVBuilder.AppendCell(s);
end;
{ CSV does not support formulas, but we can write the formula results to
@ -579,9 +546,9 @@ begin
if ACell = nil then
exit;
s := ACell^.UTF8StringValue;
s := ConvertEncoding(s, EncodingUTF8, FEncoding);
// No need to quote; csvdocument will do that for us...
FCSVBuilder.AppendCell(s);
// AppendToStream(AStream, s);
end;
{ Writes a number cell to the stream. }
@ -598,8 +565,8 @@ begin
s := Format(CSVParams.NumberFormat, [AValue], CSVParams.FormatSettings)
else
s := FWorksheet.ReadAsUTF8Text(ACell, CSVParams.FormatSettings);
s := ConvertEncoding(s, EncodingUTF8, FEncoding);
FCSVBuilder.AppendCell(s);
// AppendToStream(AStream, s);
end;
procedure TsCSVWriter.WriteSheet(AStream: TStream; AWorksheet: TsWorksheet);
@ -631,18 +598,6 @@ begin
finally
FreeAndNil(FCSVBuilder);
end;
{
for r := 0 to LastRow do
for c := 0 to LastCol do begin
Cell := FWorksheet.FindCell(r, c);
if Cell <> nil then
WriteCellCallback(Cell, AStream);
if c = LastCol then
AppendToStream(AStream, FLineEnding)
else
AppendToStream(AStream, CSVParams.Delimiter);
end;
}
end;
procedure TsCSVWriter.WriteToStream(AStream: TStream);

View File

@ -908,9 +908,9 @@ type
procedure UpdateCaches;
public
{@@ A copy of SysUtil's DefaultFormatSettings to provide some kind of
localization to some formatting strings. Can be modified before
loading/writing files }
{@@ A copy of SysUtil's DefaultFormatSettings (converted to UTF8) to provide
some kind of localization to some formatting strings.
Can be modified before loading/writing files }
FormatSettings: TFormatSettings;
{ Base methods }
@ -5999,7 +5999,7 @@ begin
FWorksheets := TFPList.Create;
FLog := TStringList.Create;
FFormat := sfExcel8;
FormatSettings := DefaultFormatSettings;
FormatSettings := UTF8FormatSettings;
FormatSettings.ShortDateFormat := MakeShortDateFormat(FormatSettings.ShortDateFormat);
FormatSettings.LongDateFormat := MakeLongDateFormat(FormatSettings.ShortDateFormat);
UseDefaultPalette;

View File

@ -4,6 +4,8 @@
// to do: Remove the patched FormatDateTime when the feature of square brackets
// in time format codes is in the rtl
// to do: Remove the declaration UTF8FormatSettings and InitUTF8FormatSettings
// when this same modification is in LazUtils of Laz stable
unit fpsutils;
@ -170,6 +172,7 @@ procedure DumpFontsToFile(AWorkbook: TsWorkbook; AFileName: String);
var
ScreenPixelsPerInch: Integer = 96;
UTF8FormatSettings: TFormatSettings;
implementation
@ -1013,7 +1016,7 @@ begin
if (ADecimals < 0) then
ADecimals := AFormatSettings.CurrencyDecimals;
if ACurrencySymbol = '?' then
ACurrencySymbol := AnsiToUTF8(AFormatSettings.CurrencyString); // is this correct? fpspreadsheet should be kept clean of string conversions!
ACurrencySymbol := AFormatSettings.CurrencyString;
if ACurrencySymbol <> '' then
ACurrencySymbol := '"' + ACurrencySymbol + '"';
decs := DupeString('0', ADecimals);
@ -2641,6 +2644,25 @@ begin
end;
end;
procedure InitUTF8FormatSettings;
// remove when available in LazUtils
var
i: Integer;
begin
UTF8FormatSettings := DefaultFormatSettings;
UTF8FormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString);
for i:=1 to 12 do begin
UTF8FormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]);
UTF8FormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]);
end;
for i:=1 to 7 do begin
UTF8FormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]);
UTF8FormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]);
end;
end;
initialization
InitUTF8FormatSettings;
end.