CSV/ru: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | ## Обзор | |
| 2 | 2 | ||
| 3 | - | ||
| 3 | + | CSV означает Comma-Separated Values (значения, разделенные запятыми) и является популярным форматом файлов, который, к сожалению, не полностью стандартизирован. | |
| 4 | 4 | ||
| 5 | 5 | Это текстовый формат файла с | |
| 6 | 6 | ||
| 7 | 7 | - полями данных, разделенными запятыми (или, в некоторых случаях, другими символами, такими как табуляции и точки с запятой) | |
| 8 | 8 | - строкой заголовка, в которой перечислены имена полей (которой может и не быть) | |
| 9 | 9 | - данными полей, содержащих разделители, которые могут быть запрещенными или быть заключенными в кавычки (чаще всего это знак двойной кавычки) | |
| 10 | 10 | - окончаниями строк (#13 и/или \#10), которые могут или не могут быть разрешены в данных полей | |
| 11 | 11 | ||
| 12 | 12 | RFC4180 (см.[здесь](http://www.rfc-editor.org/rfc/rfc4180.txt)) пытается кодифицировать и стандартизировать существующую практику; имеет смысл соответствовать этому стандарту при записи данных CSV (и принимать все данные RFC4180 при чтении). | |
| 13 | 13 | ||
| 14 | 14 | Другая, альтернативная, спецификация может быть найдена [здесь](http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm#FileFormat) | |
| 15 | 15 | ||
| 16 | 16 | Образец фрагмента CSV: | |
| 17 | 17 | ||
| 18 | 18 | `FirstName,Surname,DOB,Remarks`\ | |
| 19 | 19 | `Jim,Weston,19560818,"Also known as ""The Butcher"""`\ | |
| 20 | 20 | `Alice,Cooper,19760312,""` | |
| 21 | 21 | ||
| 22 | 22 | Как видно, здесь используется строка заголовка, как и квотирование с использованием двойных кавычек. | |
| 23 | 23 | ||
| 24 | 24 | Пакеты электронных таблиц, такие как Microsoft Excel и OpenOffice/LibreOFfice Calc, могут экспортировать и импортировать из этого формата. Однако, поскольку Microsoft Excel может интерпретировать некоторые поля, такие как поля даты, по-разному в зависимости от языкового стандарта операционной системы пользователя, возможно целесообразнее найти альтернативные способы передачи данных (например, используя код [[FPSpreadsheet]]). | |
| 25 | 25 | ||
| 26 | 26 | ## CSV и SDF | |
| 27 | 27 | ||
| 28 | 28 | Delphi (и FreePascal) имеют очень похожий (но не идентичный) формат SDF. См. [[SDF]] для получения более подробной информации. | |
| 29 | 29 | ||
| 30 | 30 | ## Реализации | |
| 31 | 31 | ||
| 32 | 32 | ### CsvDocument | |
| 33 | 33 | ||
| 34 | 34 | [[CsvDocument/ru|CsvDocument]] является надежной реализацией как CSV-формата RFC 4180 ,так и альтернативного CSV-формата Creativyst/Excel. Он предлагает как линейный, так и документный доступ. Рекомендуется для использования с FPC/Lazarus. См. [[CsvDocument/ru|CsvDocument]]. | |
| 35 | 35 | ||
| 36 | 36 | ### DelimitedText | |
| 37 | 37 | ||
| 38 | 38 | TStringList предлагает свойство `DelimitedText`. Оно разбивает строку текста на отдельные поля. Обратите внимание, однако, что `DelimitedText` должен быть в SDF-формате, специфичном для Delphi, который очень похож на CSV, но не полностью соответствует RFC4180. | |
| 39 | 39 | ||
| 40 | 40 | Совет: при чтении данных CSV установите для свойства *StrictDelimiter* значение `true`. | |
| 41 | 41 | ||
| 42 | 42 | При записи CSV-данных установите для *StrictDelimiter* значение `false` и выведите свойство *DelimitedText*. Тут есть одна странность, заключающаяся в том, что, например, символы табуляции удаляются при записи данных с использованием `StrictDelimiter:=false` | |
| 43 | 43 | ||
| 44 | 44 | #### Формат SDF | |
| 45 | 45 | ||
| 46 | 46 | См. [[SDF|формат SDF]] | |
| 47 | 47 | ||
| 48 | 48 | ### SDFDataset | |
| 49 | 49 | ||
| 50 | - | ||
| 50 | + | FreePascal предлагает [[TSdfDataSet/ru|TSdfDataSet]], который хранит данные в формате SDF. Примечание: FPC 2.6.x и более ранние версии хранят данные sdfdataset в формате, который не является полностью CSV или SDF-совместимым. Sdfdataset планируется переписать для хранения данных в формате CSV. | |
| 51 | 51 | ||
| 52 | 52 | Как указано, [[SDF]] отличается от CSV. В зависимости от разновидности CSV этот формат может быть достаточно близок к тому, что ожидает от него читающее приложение. | |
| 53 | 53 | ||
| 54 | 54 | > **Warning:** | |
| 55 | 55 | ||
| 56 | 56 | > **Note:** | |
| 57 | 57 | ||
| 58 | 58 | #### Файлы с примерами TSdfDataset и TFixedDataset | |
| 59 | 59 | ||
| 60 | 60 | - Пример SDF-файла | |
| 61 | 61 | ||
| 62 | 62 | ||
| 63 | 63 | Ниже приведен пример базы данных для TSdfDataset. Обратите внимание, что в первой строке указаны имена полей и мы используем запятые в качестве разделителей: | |
| 64 | 64 | ||
| 65 | 65 | ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE | |
| 66 | 66 | 1,resistor,resistor,1,1,1,LINE | |
| 67 | 67 | 2,capacitor,capacitor,1,1,1,LINE | |
| 68 | 68 | 3,transistor npn,transistor npn | |
| 69 | 69 | ||
| 70 | 70 | - Пример файла TFixedDataset | |
| 71 | 71 | ||
| 72 | 72 | ||
| 73 | 73 | Каждая запись занимает фиксированное количество пробелов, и если поле меньше, пробелы должны использоваться для заполнения оставшегося размера. | |
| 74 | 74 | ||
| 75 | 75 | Name = 15 chars; Surname = 15 chars; e_mail = 20 chars; | |
| 76 | 76 | Piet Pompies piet@pompies.net | |
| 77 | 77 | ||
| 78 | 78 | #### Использование наборов данных: пример кода | |
| 79 | 79 | ||
| 80 | 80 | Иногда полезно создать набор данных и работать с ним полностью в коде, и следующий код сделает это точно. Обратите внимание на некоторые особенности TSdfDataset/TFixedDataset: | |
| 81 | 81 | ||
| 82 | 82 | - Максимальный размер строк в базе данных может составлять около 300. Проблема в процессе решения. | |
| 83 | 83 | - Необходимо добавить определения полей. Некоторые наборы данных могут заполнить эту информацию самостоятельно из файла базы данных. | |
| 84 | 84 | - Нужно установить `FirstLineAsSchema` в `true`, чтобы указать, что первая строка содержит имена и позиции полей. | |
| 85 | - | ||
| 85 | + | - Свойство `Delimiter` содержит разделитель для полей. Будет невозможно использовать этот символ в строках в базе данных. Точно так же в настоящее время не будет возможности иметь символа окончания строк в базе данных, поскольку они отмечают изменение между записями. Это можно преодолеть, подставив нужную запятую или окончание строки с другим редко используемым символом, например \#. При отображении данных на экране все символы \# могут быть преобразованы в окончания строк и наоборот при сохранении данных в базе данных. Здесь будет полезна процедура `ReplaceString`. | |
| 86 | 86 | ||
| 87 | 87 | ``` pascal | |
| 88 | 88 | uses sdfdata, db; | |
| 89 | 89 | ||
| 90 | 90 | constructor TComponentsDatabase.Create; | |
| 91 | 91 | var | |
| 92 | 92 | FDataset: TSdfDataset; | |
| 93 | 93 | begin | |
| 94 | 94 | inherited Create; | |
| 95 | 95 | ||
| 96 | 96 | FDataset := TSdfDataset.Create(nil); | |
| 97 | 97 | FDataset.FileName := vConfigurations.ComponentsDBFile; | |
| 98 | 98 | FDataset.FileMustExist := false; // существующий CSV-файл не требуется | |
| 99 | 99 | ||
| 100 | 100 | // Не обязательно с TSdfDataset: | |
| 101 | 101 | // FDataset.TableName := STR_DB_COMPONENTS_TABLE; | |
| 102 | 102 | // FDataset.PrimaryKey := STR_DB_COMPONENTS_ID; | |
| 103 | 103 | ||
| 104 | 104 | // Добавляем определения полей | |
| 105 | 105 | FDataset.FieldDefs.Add('ID', ftString); | |
| 106 | 106 | //необходимо добавить схему, чтобы sdfdataset правильно записал заголовок csv: | |
| 107 | 107 | FDataset.Schema.Add('ID'); | |
| 108 | 108 | FDataset.FieldDefs.Add('NAMEEN', ftString); | |
| 109 | 109 | FDataset.Schema.Add('NAMEEN'); | |
| 110 | 110 | FDataset.FieldDefs.Add('NAMEPT', ftString); | |
| 111 | 111 | FDataset.Schema.Add('NAMEPT'); | |
| 112 | 112 | FDataset.FieldDefs.Add('HEIGHT', ftString); | |
| 113 | 113 | FDataset.Schema.Add('HEIGHT'); | |
| 114 | 114 | FDataset.FieldDefs.Add('WIDTH', ftString); | |
| 115 | 115 | FDataset.Schema.Add('WIDTH'); | |
| 116 | 116 | FDataset.FieldDefs.Add('PINS', ftString); | |
| 117 | 117 | FDataset.Schema.Add('PINS'); | |
| 118 | 118 | FDataset.FieldDefs.Add('DRAWINGCODE', ftString); | |
| 119 | 119 | FDataset.Schema.Add('DRAWINGCODE'); | |
| 120 | 120 | ||
| 121 | 121 | // Необходимо для TSdfDataset | |
| 122 | 122 | FDataset.Delimiter := ','; | |
| 123 | 123 | FDataset.FirstLineAsSchema := True; | |
| 124 | 124 | ||
| 125 | 125 | FDataset.Active := True; | |
| 126 | 126 | ||
| 127 | 127 | // Устанавливаем начальную запись | |
| 128 | 128 | CurrentRecNo := 1; | |
| 129 | 129 | FDataset.First; | |
| 130 | 130 | end; | |
| 131 | 131 | ``` | |
| 132 | 132 | ||
| 133 | 133 | При использовании [[TSdfDataSet/ru|TSdfDataSet]] следует помнить, что `RecNo`, хоть и реализован, но не работает как способ перемещения по набору данных при чтении или записи записей. Стандартные процедуры навигации, такие как `First`, `Next`, `Prior` и `Last`, работают как и положено, поэтому вам нужно использовать их, а не `RecNo`. | |
| 134 | 134 | ||
| 135 | 135 | Если вы привыкли использовать абсолютные номера записей для навигации по базе данных, вы можете реализовать свою собственную версию `RecNo`. Объявите глобальную переменную `longint` с именем `CurrentRecNo`, которая будет содержать текущее значение `RecNo`. Помните, что эта переменная будет иметь то же соглашение, что и `RecNo`, поэтому первая запись имеет номер 1 (а не начинается с нуля). После активации базы данных инициализируйте базу данных для первой записи с помощью `TSdfDataset.First` и установите `CurrentRecNo:= 1`. | |
| 136 | 136 | ||
| 137 | 137 | Пример кода: | |
| 138 | 138 | ||
| 139 | 139 | ``` pascal | |
| 140 | 140 | {@@ | |
| 141 | 141 | Перемещение к нужной записи с использованием TDataset.Next и TDataset.Prior. | |
| 142 | 142 | Это позволяет избегать использования TDataset.RecNo, который не обеспечивает надежную навигацию в любом наборе данных. | |
| 143 | 143 | ||
| 144 | 144 | @param AID Указывает номер записи. Первая запись имеет номер 1 | |
| 145 | 145 | } | |
| 146 | 146 | procedure TComponentsDatabase.GoToRec(AID: Integer); | |
| 147 | 147 | begin | |
| 148 | 148 | // Мы находимся перед искомой записью, двигаемся вперед | |
| 149 | 149 | if CurrentRecNo < AID then | |
| 150 | 150 | begin | |
| 151 | 151 | while (not FDataset.EOF) and (CurrentRecNo < AID) do | |
| 152 | 152 | begin | |
| 153 | 153 | FDataset.Next; | |
| 154 | 154 | FDataset.CursorPosChanged; | |
| 155 | 155 | Inc(CurrentRecNo); | |
| 156 | 156 | end; | |
| 157 | 157 | end | |
| 158 | 158 | // Мы находимся после искомой записи, двигаемся назад | |
| 159 | 159 | else if CurrentRecNo > AID then | |
| 160 | 160 | begin | |
| 161 | 161 | while (CurrentRecNo >= 1) and (CurrentRecNo > AID) do | |
| 162 | 162 | begin | |
| 163 | 163 | FDataset.Prior; | |
| 164 | 164 | FDataset.CursorPosChanged; | |
| 165 | 165 | Dec(CurrentRecNo); | |
| 166 | 166 | end; | |
| 167 | 167 | end; | |
| 168 | 168 | end; | |
| 169 | 169 | ``` | |
| 170 | 170 | ||
| 171 | 171 | ### Экспорт данных | |
| 172 | 172 | ||
| 173 | 173 | Функциональность экспорта базы данных FreePascal/Lazarus (например, TCSVExporter на вкладке «Data Export») предлагает функцию экспорта CSV для наборов данных. | |
| 174 | 174 | ||
| 175 | 175 | ### CSV компоненты Jan | |
| 176 | 176 | ||
| 177 | 177 | См. [[JCSV (Jans CSV Components)]]. | |
| 178 | 178 | ||
| 179 | 179 | ### ZMSQL | |
| 180 | 180 | ||
| 181 | 181 | [[ZMSQL]] хранит данные в файлах, разделенных точкой с запятой (используя SDF?). | |
| 182 | 182 | ||
| 183 | 183 | --- | |
| 184 | 184 | *From the Free Pascal wiki, page "CSV/ru", as it stood on 2020-07-15 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
