#delphi #csv #tquery
#delphi #csv #tquery
Вопрос:
Я хочу экспортировать содержимое TQuery в файл CSV без использования компонента 3D-детали (Delphi 7). Насколько мне известно, это невозможно выполнить с помощью стандартных компонентов Delphi.
Моим решением было сохранить содержимое в StringList в формате CSV и сохранить его в файл.
Есть ли какое-либо удобное решение?
PS: Я не хочу использовать JvCsvDataSet или какой-либо другой компонент. Вопрос в том, может ли это быть выполнено только с помощью стандартных компонентов Delphi 7 или выше?
Заранее благодарю вас!
Комментарии:
1. Довольно просто написать отправитель файла CSV. Поскольку вы не можете найти встроенную функциональность и поскольку вам не нужны сторонние решения, то вам, вероятно, придется пойти по этому пути.
2. поскольку мы говорим о CSV, есть хорошая статья о шаблонах проектирования, использующих синтаксический анализатор CSV в качестве примера — conferences.embarcadero.com/article/32129
Ответ №1:
Конечно, это возможно.
Вам просто нужно выполнить работу по правильному выводу содержимого CSV (правильное цитирование, обработка встроенных кавычек и запятых и т.д.). Вы можете легко записать выходные данные, используя TFileStream
, и получить данные, правильно используя TQuery.Fields
и TQuery.FieldCount
.
Я оставлю вам причудливое цитирование CSV и специальную обработку. Это позаботится о самой простой части:
var
Stream: TFileStream;
i: Integer;
OutLine: string;
sTemp: string;
begin
Stream := TFileStream.Create('C:DataYourFile.csv', fmCreate);
try
while not Query1.Eof do
begin
// You'll need to add your special handling here where OutLine is built
OutLine := '';
for i := 0 to Query.FieldCount - 1 do
begin
sTemp := Query.Fields[i].AsString;
// Special handling to sTemp here
OutLine := OutLine sTemp ',';
end;
// Remove final unnecessary ','
SetLength(OutLine, Length(OutLine) - 1);
// Write line to file
Stream.Write(OutLine[1], Length(OutLine) * SizeOf(Char));
// Write line ending
Stream.Write(sLineBreak, Length(sLineBreak));
Query1.Next;
end;
finally
Stream.Free; // Saves the file
end;
end;
Комментарии:
1. Некоторая информация о том, как избежать sTemp: csvreader.com/csv_format.php
2. @Radu Barbu: помните о форматировании даты при импорте csv в электронную таблицу (я предполагаю, что вы хотите это сделать). F.i. Excel пытается угадать дату на основе входных данных, поэтому, пожалуйста, убедитесь, что ваши даты обрабатываются правильно (если они присутствуют, конечно). Попробуйте явно преобразовать их в формат ‘дд-ммм-гггг’, если у вас возникнут проблемы.
Ответ №2:
В исходном вопросе предлагалось решение с использованием списка строк. Так что это было бы что-то более похожее на это. Это будет работать с любым TDataSet, а не только с TQuery.
procedure WriteDataSetToCSV(DataSet: TDataSet, FileName: String);
var
List: TStringList;
S: String;
I: Integer;
begin
List := TStringList.Create;
try
DataSet.First;
while not DataSet.Eof do
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do
begin
if S > '' then
S := S ',';
S := S '"' DataSet.Fields[I].AsString '"';
end;
List.Add(S);
DataSet.Next;
end;
finally
List.SaveToFile(FileName);
List.Free;
end;
end;
Вы можете добавить параметры для изменения типа разделителя или чего-либо еще.
Комментарии:
1. Вопрос не запрашивал решение с использованием TStringList. Он просто попросил решение, которое не использовало сторонние библиотеки.
2. Исправлено: после TDataSet требуется точка с запятой, а не запятая. «процедура WriteDataSetToCSV(набор данных: TDataSet; имя файла: Строка);»
Ответ №3:
Это похоже на решение Роба Макдонелла, но с некоторыми улучшениями: заголовок, управляющие символы, вложение только при необходимости и разделитель «;». Вы можете легко отключить это улучшение, если оно не требуется.
procedure SaveToCSV(DataSet: TDataSet; FileName: String);
const
Delimiter: Char = ';'; // In order to be automatically recognized in Microsoft Excel use ";", not ","
Enclosure: Char = '"';
var
List: TStringList;
S: String;
I: Integer;
function EscapeString(s: string): string;
var
i: Integer;
begin
Result := StringReplace(s,Enclosure,Enclosure Enclosure,[rfReplaceAll]);
if (Pos(Delimiter,s) > 0) OR (Pos(Enclosure,s) > 0) then // Comment this line for enclosure in every fields
Result := Enclosure Result Enclosure;
end;
procedure AddHeader;
var
I: Integer;
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do begin
if S > '' then
S := S Delimiter;
S := S EscapeString(DataSet.Fields[I].FieldName);
end;
List.Add(S);
end;
procedure AddRecord;
var
I: Integer;
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do begin
if S > '' then
S := S Delimiter;
S := S EscapeString(DataSet.Fields[I].AsString);
end;
List.Add(S);
end;
begin
List := TStringList.Create;
try
DataSet.DisableControls;
DataSet.First;
AddHeader; // Comment if header not required
while not DataSet.Eof do begin
AddRecord;
DataSet.Next;
end;
finally
List.SaveToFile(FileName);
DataSet.First;
DataSet.EnableControls;
List.Free;
end;
end;
Ответ №4:
Delphi не предоставляет никакого встроенного доступа к данным .csv. Однако, следуя парадигме VCL TXMLTransform, я написал вспомогательный класс TCsvTransform, который будет преобразовывать структуру .csv в / из TClientDataSet. Что касается первоначального вопроса, который заключался в экспорте TQuery в .csv, простой TDataSetProvider установит связь между TQuery и TClientDataSet. Для получения более подробной информации о TCsvTransform см.http://didier.cabale.free.fr/delphi.htm#uCsvTransform
Ответ №5:
Извините, что отвечаю на вопрос семилетней давности, но если кто-нибудь наткнется на него (как это сделал я) и не захочет внедрять и поддерживать свой собственный CSV writer, следующий код демонстрирует, как использовать компоненты FireDAC (которые поставляются с Delphi начиная с XE5) для сохранения набора данных в CSV всего за несколько основных шагов:
uses
FireDAC.Comp.BatchMove,
FireDAC.Comp.BatchMove.Text,
FireDAC.Comp.BatchMove.DataSet;
procedure SaveDatasetToCSV(IncludeFieldNames : Boolean; Dataset : TDataset; Filename : String);
var
TextWriter : TFDBatchMoveTextWriter;
DataSetReader : TFDBatchMoveDataSetReader;
BatchMove : TFDBatchMove;
begin
BatchMove := nil;
try
BatchMove := TFDBatchMove.Create(nil);
TextWriter := TFDBatchMoveTextWriter.Create(BatchMove);
DataSetReader := TFDBatchMoveDataSetReader.Create(BatchMove);
DataSetReader.DataSet := Dataset;
TextWriter.FileName := Filename;
TextWriter.DataDef.WithFieldNames := IncludeFieldNames;
BatchMove.Reader := DataSetReader;
BatchMove.Writer := TextWriter;
BatchMove.Options := BatchMove.Options [poClearDest]; //Overrides file if it already exists
BatchMove.Execute;
finally
BatchMove.Free;
end;
end;