Difference between revisions of "CSV/ru"

From Free Pascal wiki
Jump to navigationJump to search
Line 49: Line 49:
 
=== SDFDataset ===
 
=== SDFDataset ===
  
FreePascal offers the [[TSdfDataSet]], which <s>stores data in SDF format.</s> Note: FPC 2.6.x and earlier store sdfdataset data in a format that is not completely CSV or completely SDF compatible. Sdfdataset is planned to be rewritten to store data in CSV format.
+
FreePascal предлагает [[TSdfDataSet/ru|TSdfDataSet]], который <s>хранит данные в формате SDF</s>. Примечание: FPC 2.6.x и более ранние версии хранят данные sdfdataset в формате, который не является полностью CSV или SDF-совместимым. Sdfdataset планируется переписать для хранения данных в формате CSV.
  
As indicated, [[SDF]] differs from CSV. Depending on the flavour of CSV, this format may be close enough to what a reading application expects to function.
+
Как указано, [[SDF]] отличается от CSV. В зависимости от разновидности CSV этот формат может быть достаточно близок к тому, что ожидает от него читающее приложение.
  
{{Warning|1=TSDFDataset will likely not work at least on ARM-based Windows CE/Windows mobile, see http://bugs.freepascal.org/view.php?id=17871 }}
+
{{Warning|1=TSDFDataset, скорее всего, не будет работать, по крайней мере, на Windows CE/Windows Mobile на базе ARM, см. [http://bugs.freepascal.org/view.php?id=17871 багрепорт]}}
  
{{Note|1=Nov 2012: SDFDataset is intended to be ''(but has not yet been)'' redefined to use RFC4180 CSV format. See e.g. http://bugs.freepascal.org/view.php?id=22980 }}
+
{{Note|1=Ноябрь 2012: SDFDataset должен был быть ''(но еще не был)'' переопределен для использования формата RFC4180 CSV. См., например, [http://bugs.freepascal.org/view.php?id=22980 багрепорт]}}
  
==== TSdfDataset and TFixedDataset sample files ====
+
==== Файлы с примерами TSdfDataset и TFixedDataset ====
Sample SDF file
+
* Пример SDF-файла
:Below is a sample database for TSdfDataset. Note that the first line has the names of the fields and that we are using commas as separators:
+
:Ниже приведен пример базы данных для TSdfDataset. Обратите внимание, что в первой строке указаны имена полей и мы используем запятые в качестве разделителей:
  
 
<pre>ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE
 
<pre>ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE
Line 66: Line 66:
 
3,transistor npn,transistor npn</pre>
 
3,transistor npn,transistor npn</pre>
  
* Sample TFixedDataset file
+
* Пример файла TFixedDataset
:Each record occupies a fixed amount of space, and if the field is smaller, spaces should be used to fill the remaining size.
+
:Каждая запись занимает фиксированное количество пробелов, и если поле меньше, пробелы должны использоваться для заполнения оставшегося размера.
  
 
<pre>Name = 15 chars; Surname = 15 chars; e_mail = 20 chars;
 
<pre>Name = 15 chars; Surname = 15 chars; e_mail = 20 chars;
 
Piet          Pompies                  piet@pompies.net</pre>
 
Piet          Pompies                  piet@pompies.net</pre>
  
==== Using the datasets: example code ====
+
==== Использование наборов данных: пример кода ====
  
Sometimes it is useful to create the dataset and work with it completely in code, and the following code will do exactly this. Note some peculiarities of TSdfDataset/TFixedDataset:
+
Иногда полезно создать набор данных и работать с ним полностью в коде, и следующий код сделает это точно. Обратите внимание на некоторые особенности TSdfDataset/TFixedDataset:
  
* The lines in the database can have a maximum size of about 300. A fix is being researched.
+
* Максимальный размер строк в базе данных может составлять около 300. Проблема в процессе решения.
* It is necessary to add the field definitions. Some datasets are able to fill this information alone from the database file.
+
* Необходимо добавить определения полей. Некоторые наборы данных могут заполнить эту информацию самостоятельно из файла базы данных.
* One should set FirstLineAsSchema to true, to indicate that the first line includes the field names and positions.
+
* Нужно установить <code>FirstLineAsSchema</code> в <code>true</code>, чтобы указать, что первая строка содержит имена и позиции полей.
* The Delimiter property holds the separator for the fields. It will not be possible to use this char in strings in the database. Similarly it will currently not be possible to have line endings in the database because they mark the change between records. It's possible to overcome this by substituting the needed comma or line ending with another rarely used char, like #. When showing the data on screen all # chars could be converted to line endings and the inverse when storing data back to the database. The ReplaceString routine is useful here.
+
* Свойство <code>Delimiter</code> содержит разделитель для полей. Будет невозможно использовать этот символ в строках в базе данных. Точно так же в настоящее время не будет возможности иметь символа окончания строк в базе данных, поскольку они отмечают изменение между записями. Это можно преодолеть, подставив нужную запятую или окончание строки с другим редко используемым символом, например <b>#</b>. При отображении данных на экране все символы <b>#</b> могут быть преобразованы в окончания строк и наоборот при сохранении данных в базе данных. Здесь будет полезна процедура <code>ReplaceString</code>.
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 92: Line 92:
 
   FDataset := TSdfDataset.Create(nil);
 
   FDataset := TSdfDataset.Create(nil);
 
   FDataset.FileName := vConfigurations.ComponentsDBFile;
 
   FDataset.FileName := vConfigurations.ComponentsDBFile;
   FDataset.FileMustExist := false; //don't require existing csv file
+
   FDataset.FileMustExist := false; // существующий CSV-файл не требуется
  
   // Not necessary with TSdfDataset:
+
   // Не обязательно с TSdfDataset:
 
   //  FDataset.TableName := STR_DB_COMPONENTS_TABLE;
 
   //  FDataset.TableName := STR_DB_COMPONENTS_TABLE;
 
   //  FDataset.PrimaryKey := STR_DB_COMPONENTS_ID;
 
   //  FDataset.PrimaryKey := STR_DB_COMPONENTS_ID;
  
   // Adds field definitions
+
   // Добавляем определения полей
 
   FDataset.FieldDefs.Add('ID', ftString);
 
   FDataset.FieldDefs.Add('ID', ftString);
   //schema addition is needed so that sdfdataset writes out the csv header correctly:
+
   //необходимо добавить схему, чтобы sdfdataset правильно записал заголовок csv:
 
   FDataset.Schema.Add('ID');  
 
   FDataset.Schema.Add('ID');  
 
   FDataset.FieldDefs.Add('NAMEEN', ftString);
 
   FDataset.FieldDefs.Add('NAMEEN', ftString);
Line 115: Line 115:
 
   FDataset.Schema.Add('DRAWINGCODE');  
 
   FDataset.Schema.Add('DRAWINGCODE');  
  
   // Necessary for TSdfDataset
+
   // Необходимо для TSdfDataset
 
   FDataset.Delimiter := ',';
 
   FDataset.Delimiter := ',';
 
   FDataset.FirstLineAsSchema := True;
 
   FDataset.FirstLineAsSchema := True;
Line 121: Line 121:
 
   FDataset.Active := True;
 
   FDataset.Active := True;
  
   // Sets the initial record
+
   // Устанавливаем начальную запись
 
   CurrentRecNo := 1;
 
   CurrentRecNo := 1;
 
   FDataset.First;
 
   FDataset.First;
Line 127: Line 127:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
When using [[TSdfDataSet]] directly be aware that RecNo, although it is implemented, does not work as a way to move through the dataset whether reading or writing records. The standard navigation routines like First, Next, Prior and Last work as expected, so you need to use them rather than RecNo.
+
При использовании [[TSdfDataSet/ru|TSdfDataSet]] следует помнить, что <code>RecNo</code>, хоть и реализован, но не работает как способ перемещения по набору данных при чтении или записи записей. Стандартные процедуры навигации, такие как <code>First</code>, <code>Next</code>, <code>Prior</code> и <code>Last</code>, работают как и положено, поэтому вам нужно использовать их, а не <code>RecNo</code>.
  
If you are used to using absolute record numbers to navigate around a database you can implement your own version of RecNo. Declare a global longint variable called CurrentRecNo which will hold the current RecNo value. Remember that this variable will have the same convention as RecNo, so the first record has number 1 (it is not zero-based). After activating the database initialize the database to the first record with TSdfDataset.First and set CurrentRecNo := 1.  
+
Если вы привыкли использовать абсолютные номера записей для навигации по базе данных, вы можете реализовать свою собственную версию <code>RecNo</code>. Объявите глобальную переменную <code>longint</code> с именем <code>CurrentRecNo</code>, которая будет содержать текущее значение <code>RecNo</code>. Помните, что эта переменная будет иметь то же соглашение, что и <code>RecNo</code>, поэтому первая запись имеет номер 1 (а не начинается с нуля). После активации базы данных инициализируйте базу данных для первой записи с помощью <code>TSdfDataset.First</code> и установите <code>CurrentRecNo:= 1</code>.  
  
Example code:
+
Пример кода:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
{@@
 
{@@
   Moves to the desired record using TDataset.Next and TDataset.Prior
+
   Перемещение к нужной записи с использованием TDataset.Next и TDataset.Prior.
   This avoids using TDataset.RecNo which doesn't navigate reliably in any dataset.
+
   Это позволяет избегать использования TDataset.RecNo, который не обеспечивает надежную навигацию в любом наборе данных.
  
   @param AID Indicates the record number. The first record has number 1
+
   @param AID Указывает номер записи. Первая запись имеет номер 1
 
}
 
}
 
procedure TComponentsDatabase.GoToRec(AID: Integer);
 
procedure TComponentsDatabase.GoToRec(AID: Integer);
 
begin
 
begin
   // We are before the desired record, move forward
+
   // Мы находимся перед искомой записью, двигаемся вперед
 
   if CurrentRecNo < AID then
 
   if CurrentRecNo < AID then
 
   begin
 
   begin
Line 152: Line 152:
 
     end;
 
     end;
 
   end
 
   end
   // We are after the desired record, move back
+
   // Мы находимся после искомой записи, двигаемся назад
 
   else if CurrentRecNo > AID  then
 
   else if CurrentRecNo > AID  then
 
   begin
 
   begin

Revision as of 22:47, 15 July 2020

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?).