CSV/ru
│
English (en) │
русский (ru) │
Обзор
CSV означает Comma-Separated Values (значения, разделенные запятыми) и является популярным форматом файлов, который, к сожалению, не полностью стандартизирован.
Это текстовый формат файла с
- полями данных, разделенными запятыми (или, в некоторых случаях, другими символами, такими как табуляции и точки с запятой)
- строкой заголовка, в которой перечислены имена полей (которой может и не быть)
- данными полей, содержащих разделители, которые могут быть запрещенными или быть заключенными в кавычки (чаще всего это знак двойной кавычки)
- окончаниями строк (#13 и/или #10), которые могут или не могут быть разрешены в данных полей
RFC4180 (см.здесь) пытается кодифицировать и стандартизировать существующую практику; имеет смысл соответствовать этому стандарту при записи данных CSV (и принимать все данные RFC4180 при чтении).
Другая, альтернативная, спецификация может быть найдена здесь
Образец фрагмента CSV:
FirstName,Surname,DOB,Remarks Jim,Weston,19560818,"Also known as ""The Butcher""" Alice,Cooper,19760312,""
Как видно, здесь используется строка заголовка, как и квотирование с использованием двойных кавычек.
Пакеты электронных таблиц, такие как 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
См. формат SDF
SDFDataset
FreePascal предлагает TSdfDataSet, который хранит данные в формате SDF. Примечание: FPC 2.6.x и более ранние версии хранят данные sdfdataset в формате, который не является полностью CSV или SDF-совместимым. Sdfdataset планируется переписать для хранения данных в формате CSV.
Как указано, SDF отличается от CSV. В зависимости от разновидности CSV этот формат может быть достаточно близок к тому, что ожидает от него читающее приложение.
Предупреждение: TSDFDataset, скорее всего, не будет работать, по крайней мере, на Windows CE/Windows Mobile на базе ARM, см. багрепорт
Файлы с примерами TSdfDataset и TFixedDataset
- Пример SDF-файла
- Ниже приведен пример базы данных для TSdfDataset. Обратите внимание, что в первой строке указаны имена полей и мы используем запятые в качестве разделителей:
ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE 1,resistor,resistor,1,1,1,LINE 2,capacitor,capacitor,1,1,1,LINE 3,transistor npn,transistor npn
- Пример файла TFixedDataset
- Каждая запись занимает фиксированное количество пробелов, и если поле меньше, пробелы должны использоваться для заполнения оставшегося размера.
Name = 15 chars; Surname = 15 chars; e_mail = 20 chars; Piet Pompies piet@pompies.net
Использование наборов данных: пример кода
Иногда полезно создать набор данных и работать с ним полностью в коде, и следующий код сделает это точно. Обратите внимание на некоторые особенности TSdfDataset/TFixedDataset:
- Максимальный размер строк в базе данных может составлять около 300. Проблема в процессе решения.
- Необходимо добавить определения полей. Некоторые наборы данных могут заполнить эту информацию самостоятельно из файла базы данных.
- Нужно установить
FirstLineAsSchema
вtrue
, чтобы указать, что первая строка содержит имена и позиции полей. - Свойство
Delimiter
содержит разделитель для полей. Будет невозможно использовать этот символ в строках в базе данных. Точно так же в настоящее время не будет возможности иметь символа окончания строк в базе данных, поскольку они отмечают изменение между записями. Это можно преодолеть, подставив нужную запятую или окончание строки с другим редко используемым символом, например #. При отображении данных на экране все символы # могут быть преобразованы в окончания строк и наоборот при сохранении данных в базе данных. Здесь будет полезна процедураReplaceString
.
uses sdfdata, db;
constructor TComponentsDatabase.Create;
var
FDataset: TSdfDataset;
begin
inherited Create;
FDataset := TSdfDataset.Create(nil);
FDataset.FileName := vConfigurations.ComponentsDBFile;
FDataset.FileMustExist := false; // существующий CSV-файл не требуется
// Не обязательно с TSdfDataset:
// FDataset.TableName := STR_DB_COMPONENTS_TABLE;
// FDataset.PrimaryKey := STR_DB_COMPONENTS_ID;
// Добавляем определения полей
FDataset.FieldDefs.Add('ID', ftString);
//необходимо добавить схему, чтобы sdfdataset правильно записал заголовок csv:
FDataset.Schema.Add('ID');
FDataset.FieldDefs.Add('NAMEEN', ftString);
FDataset.Schema.Add('NAMEEN');
FDataset.FieldDefs.Add('NAMEPT', ftString);
FDataset.Schema.Add('NAMEPT');
FDataset.FieldDefs.Add('HEIGHT', ftString);
FDataset.Schema.Add('HEIGHT');
FDataset.FieldDefs.Add('WIDTH', ftString);
FDataset.Schema.Add('WIDTH');
FDataset.FieldDefs.Add('PINS', ftString);
FDataset.Schema.Add('PINS');
FDataset.FieldDefs.Add('DRAWINGCODE', ftString);
FDataset.Schema.Add('DRAWINGCODE');
// Необходимо для TSdfDataset
FDataset.Delimiter := ',';
FDataset.FirstLineAsSchema := True;
FDataset.Active := True;
// Устанавливаем начальную запись
CurrentRecNo := 1;
FDataset.First;
end;
При использовании TSdfDataSet следует помнить, что RecNo
, хоть и реализован, но не работает как способ перемещения по набору данных при чтении или записи записей. Стандартные процедуры навигации, такие как First
, Next
, Prior
и Last
, работают как и положено, поэтому вам нужно использовать их, а не RecNo
.
Если вы привыкли использовать абсолютные номера записей для навигации по базе данных, вы можете реализовать свою собственную версию RecNo
. Объявите глобальную переменную longint
с именем CurrentRecNo
, которая будет содержать текущее значение RecNo
. Помните, что эта переменная будет иметь то же соглашение, что и RecNo
, поэтому первая запись имеет номер 1 (а не начинается с нуля). После активации базы данных инициализируйте базу данных для первой записи с помощью TSdfDataset.First
и установите CurrentRecNo:= 1
.
Пример кода:
{@@
Перемещение к нужной записи с использованием TDataset.Next и TDataset.Prior.
Это позволяет избегать использования TDataset.RecNo, который не обеспечивает надежную навигацию в любом наборе данных.
@param AID Указывает номер записи. Первая запись имеет номер 1
}
procedure TComponentsDatabase.GoToRec(AID: Integer);
begin
// Мы находимся перед искомой записью, двигаемся вперед
if CurrentRecNo < AID then
begin
while (not FDataset.EOF) and (CurrentRecNo < AID) do
begin
FDataset.Next;
FDataset.CursorPosChanged;
Inc(CurrentRecNo);
end;
end
// Мы находимся после искомой записи, двигаемся назад
else if CurrentRecNo > AID then
begin
while (CurrentRecNo >= 1) and (CurrentRecNo > AID) do
begin
FDataset.Prior;
FDataset.CursorPosChanged;
Dec(CurrentRecNo);
end;
end;
end;
Экспорт данных
Функциональность экспорта базы данных FreePascal/Lazarus (например, TCSVExporter на вкладке «Data Export») предлагает функцию экспорта CSV для наборов данных.
CSV компоненты Jan
См. JCSV (Jans CSV Components).
ZMSQL
ZMSQL хранит данные в файлах, разделенных точкой с запятой (используя SDF?).