Delphi работа с csv файлами как с базой данных
CSV означает Comma-Separated Values (значения, разделенные запятыми) и является популярным форматом файлов, который, к сожалению, не полностью стандартизирован.
Это текстовый формат файла с
RFC4180 (см.здесь) пытается кодифицировать и стандартизировать существующую практику; имеет смысл соответствовать этому стандарту при записи данных CSV (и принимать все данные RFC4180 при чтении).
Другая, альтернативная, спецификация может быть найдена здесь
Образец фрагмента CSV:
Как видно, здесь используется строка заголовка, как и квотирование с использованием двойных кавычек.
Пакеты электронных таблиц, такие как Microsoft Excel и OpenOffice/LibreOFfice Calc, могут экспортировать и импортировать из этого формата. Однако, поскольку Microsoft Excel может интерпретировать некоторые поля, такие как поля даты, по-разному в зависимости от языкового стандарта операционной системы пользователя, возможно целесообразнее найти альтернативные способы передачи данных (например, используя код FPSpreadsheet).
CSV и SDF
Delphi (и FreePascal) имеют очень похожий (но не идентичный) формат SDF. См. SDF для получения более подробной информации.
Реализации
CsvDocument
CsvDocument является надежной реализацией как CSV-формата RFC 4180 ,так и альтернативного CSV-формата Creativyst/Excel. Он предлагает как линейный, так и документный доступ. Рекомендуется для использования с FPC/Lazarus. См. CsvDocument.
DelimitedText
TStringList предлагает свойство DelimitedText . Оно разбивает строку текста на отдельные поля. Обратите внимание, однако, что DelimitedText должен быть в SDF-формате, специфичном для Delphi, который очень похож на CSV, но не полностью соответствует RFC4180.
Совет: при чтении данных CSV установите для свойства StrictDelimiter значение true .
При записи CSV-данных установите для StrictDelimiter значение false и выведите свойство DelimitedText. Тут есть одна странность, заключающаяся в том, что, например, символы табуляции удаляются при записи данных с использованием StrictDelimiter:=false
Формат SDF
SDFDataset
FreePascal предлагает TSdfDataSet, который хранит данные в формате SDF. Примечание: FPC 2.6.x и более ранние версии хранят данные sdfdataset в формате, который не является полностью CSV или SDF-совместимым. Sdfdataset планируется переписать для хранения данных в формате CSV.
Как указано, SDF отличается от CSV. В зависимости от разновидности CSV этот формат может быть достаточно близок к тому, что ожидает от него читающее приложение.
Предупреждение: TSDFDataset, скорее всего, не будет работать, по крайней мере, на Windows CE/Windows Mobile на базе ARM, см. багрепорт
Примечание: Ноябрь 2012: SDFDataset должен был быть (но еще не был) переопределен для использования формата RFC4180 CSV. См., например, багрепорт
Файлы с примерами TSdfDataset и TFixedDataset
- Пример SDF-файла
- Пример файла TFixedDataset
Использование наборов данных: пример кода
Иногда полезно создать набор данных и работать с ним полностью в коде, и следующий код сделает это точно. Обратите внимание на некоторые особенности TSdfDataset/TFixedDataset:
При использовании TSdfDataSet следует помнить, что RecNo , хоть и реализован, но не работает как способ перемещения по набору данных при чтении или записи записей. Стандартные процедуры навигации, такие как First , Next , Prior и Last , работают как и положено, поэтому вам нужно использовать их, а не RecNo .
Если вы привыкли использовать абсолютные номера записей для навигации по базе данных, вы можете реализовать свою собственную версию RecNo . Объявите глобальную переменную longint с именем CurrentRecNo , которая будет содержать текущее значение RecNo . Помните, что эта переменная будет иметь то же соглашение, что и RecNo , поэтому первая запись имеет номер 1 (а не начинается с нуля). После активации базы данных инициализируйте базу данных для первой записи с помощью TSdfDataset.First и установите CurrentRecNo:= 1 .
Экспорт данных
Функциональность экспорта базы данных FreePascal/Lazarus (например, TCSVExporter на вкладке «Data Export») предлагает функцию экспорта CSV для наборов данных.
CSV компоненты Jan
ZMSQL
ZMSQL хранит данные в файлах, разделенных точкой с запятой (используя SDF?).
CSV-файл – простейший по организации файл-таблица, который понимает Microsoft Excel. CSV – Como Separated Value – данные, разделенные запятой. Это обычный текстовый файл, в котором каждая строка олицетворяет ряд таблицы, а разделение этого ряда на колонки осуществляется путем разделения значений специальным разделителем. Обычно роль этого разделителя, судя из названия, играет запятая. Однако, встречается и другой разделитель – точка с запятой. Даже в Microsoft не определились, с каким разделителем должен работать Excel. При сохранении какой-либо таблицы в формате CSV, в качестве разделителя используется точка с запятой. Но, при открытии такого файла с помощью Microsoft Excel, нужно быть очень внимательным, так как, не знаю, шутка это или нет, существует, как Вы знаете два способа открытия файла:
- В главном меню программы выбрать пункт «Открыть» из меню «файл».
- Найти на жестком диске нужный файл и два раза кликнуть на нем мышкой.
Так вот, при открытии CSV файла первым способом, Excel использует точку с запятой в качестве разделителя, а при открытии вторым способом Excel использует запятую в качестве разделителя. Правда в Microsoft Excel XP эта проблема(?) решена, и используется только точка с запятой, не смотря на название файла.
Работа с CSV файлами в Delphi
Для работы с CSV-файлами в Delphi Вам не понадобятся различные сторонние компоненты. Как я уже говорил, CSV-файл – это обычный текстовый файл. Следовательно, для работы с ним мы будем использовать стандартный тип TextFile определенный в модуле system.
Давайте напишем процедуру для загрузки CSV файла в таблицу TStringGrid. Это стандартный компонент Delphi для работы со строковыми таблицами. Находится он на странице Additional в палитре компонентов Delphi.
Вот код этой процедуры:
Теперь разберем этот код.
Это заголовок нашей процедуры. В качестве параметров мы передаем ей имя файла и разделитель данных.
Здесь мы ассоциируем с файловой переменной f имя файла и открываем его для чтения.
Пока не достигнут конец файла, читаем из него очередную строку.
Далее нам остается только разделить строку на много строк, используя в качестве разделителя символ separator, и записать эти строки в таблицу, учитывая то, что нужно увеличить число рядов на 1. Это делается следующим кодом:
Ну вот, пожалуй, и все что я хотел написать.
Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре
Уже говорил, что разбирал древний хлам на очень старом компьютере и нашёл папку с исходниками, набросками кода. Такая, знаете ли, библиотечка под рукой. Была и папочка с примером -StringGrid в CSV в Delphi. Прошли годы, но, как вижу, Rad Studio еще трепыхается. Для тех, кто пользуется, выкладываю тот кусочек кода для импорта/экспорта данных в CSV файл из Delphi.
Код, как вижу, где-то скопипастил. Короче, на форму ставим компоненты: StringGrid1, Button1 (сохранить), Button2 (загрузить), Label1, Edit1.
Вот полный код модуля импорта-экспорта. Просто копируем и вставляем.
Код примера импорта-экспорта StringGrid в csv в Delphi
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Edit1Change(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;
procedure SaveBase(StringGrid: TStringGrid; const FileName: TFileName);
var
l: TStrings;
i: word;
s: string;
begin
l:= TStringList.Create;
for i:= 0 to Pred(StringGrid.RowCount) do
begin
StringGrid.Rows[i].StrictDelimiter:= true;
StringGrid.Rows[i].Delimiter:= TabT;
s:= StringGrid.Rows[i].DelimitedText;
l.Add(s);
end;
l.SaveToFile(FileName);
l.Free;
end;
procedure LoadBase(StringGrid: TStringGrid; const FileName: TFileName);
var
sdata, srow: TStrings;
i:integer;
begin
sdata:=TStringList.Create;
srow:=TStringList.Create;
srow.StrictDelimiter:= true;
srow.Delimiter:=TabT;
sdata.LoadFromFile(FileName);
StringGrid.RowCount:=sdata.Count;
for i:=0 to sdata.Count-1 do begin
srow.DelimitedText:=sdata[i];
if srow.Count>StringGrid.ColCount then
StringGrid.ColCount:=srow.Count;
StringGrid.Rows[i].Assign(srow);
end;
srow.Free;
sdata.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SaveBase(StringGrid1,D);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
LoadBase(StringGrid1,D);
end;
procedure TForm1.Edit1Change(Sender: TObject);
begin
TabT:=Char(Edit1.Text[1]);
end;
Файл CSV представляет собой простой текст с разделителями для табличного представления в ячейках. Поддерживается Excel и другими табличными редакторами.
В директории с программой создайте текстовый файл, переименуйте в test.csv и заполните его, например, этим:
Заголовок 1;Заголовок 2;Заголовок 3;Заголовок 4
1111 1;11111;111 111 ;11111 1111
2 test;22 test;222 test;2222 test
3 test;33 test;333 test;3333 test
Ну, а дальше запускаете свой проект и можете импортировать или экспортировать данные меняя разделитель.
У меня было предварительное интервью, которое я завершил, и решение работает, однако я был отмечен и не получил интервью из-за использования TADODataset. Я в основном импортировал CSV-файл, который заполнял набор данных, данные должны были обрабатываться определенным образом, поэтому я использовал фильтрацию и сортировку набора данных, чтобы убедиться, что данные были упорядочены так, как я хотел, а затем я сделал логическую обработку в цикле while. Обратная связь, которая была получена сказал, что это было плохо, так как это было бы очень медленно для больших файлов.
мой главный вопрос здесь, Если использование набора данных в памяти медленно для обработки больших файлов, что было бы лучшим способом доступа к информации из файла csv. Должен ли я использовать списки строк или что-то в этом роде?
это действительно зависит от того, насколько "большие" и доступные ресурсы(в данном случае ОЗУ) для задачи.
" обратная связь, которая была получена, сказала, что это плохо, так как это будет очень медленно для больших файлов."
CSV-файлы обычно используются для перемещения данных (в большинстве случаев, с которыми я столкнулся, файлы составляют
10MB, но это не значит, что другие не будут сбрасывать больше данных в формате CSV), не беспокоясь слишком много(если вообще) об импорте/экспорте, так как это очень упрощенно.
Предположим, у вас есть файл CSV 80MB, теперь это файл, который вы хотите обрабатывать кусками, иначе(в зависимости от вашей обработки) вы можете съесть сотни МБ ОЗУ, в этом случае я бы сделал следующее:
в приведенном выше случае вы загружаете не 80 МБ данных в ОЗУ, а всего несколько сотен КБ, а остальное вы используете для обработки, т. е. связанные списки, динамические запросы вставки(пакетная вставка) и т. д.
". однако я был отмечен и сделал не получить интервью из-за использования TADODataset."
Я не удивлен, они, вероятно, искали, можете ли вы создать алгоритм(Ы) и предоставить простые решения на месте, но без использования "готовых" решений.
они, вероятно, думали увидеть, как вы используете динамические массивы и создаете один(или несколько) алгоритмов сортировки.
" должен ли я использовать списки строк или что-то в этом роде?"
ответ может быть было то же самое, опять же, я думаю, что они хотели посмотреть, как вы "работаете".
правильное, масштабируемое и быстрое решение для любого среднего файла вверх-использовать "внешнюю сортировку".
"внешняя сортировка" - это двухэтапный процесс, первый этап-разбить каждый файл на управляемые и отсортированные файлы меньшего размера. Второй этап-объединить эти файлы обратно в один отсортированный файл, который затем может быть обработан строка за строкой.
Он чрезвычайно эффективен в любом CSV-файле с более чем 200,000 русло. Объем памяти, в котором выполняется процесс, можно контролировать, и, таким образом, опасность исчерпания памяти может быть устранена.
я реализовал много таких процессов сортировки и в Delphi рекомендовал бы сочетание классов TStringList, TList и TQueue.
Читайте также: