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