CSV/ru

From Free Pascal wiki
Jump to navigationJump to search

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 этот формат может быть достаточно близок к тому, что ожидает от него читающее приложение.

Warning-icon.png

Предупреждение: TSDFDataset, скорее всего, не будет работать, по крайней мере, на Windows CE/Windows Mobile на базе ARM, см. багрепорт

Light bulb  Примечание: Ноябрь 2012: SDFDataset должен был быть (но еще не был) переопределен для использования формата RFC4180 CSV. См., например, багрепорт

Файлы с примерами 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;

Data Export

FreePascal/Lazarus database export functionality (e.g. TCSVExporter on the Data Export tab) offers CSV export functionality for datasets.

Jan's CSV components

See JCSV (Jans CSV Components).

ZMSQL

ZMSQL stores data in semicolon-delimited files (using SDF?).