
Читая иностранные блоги, наткнулся на пост, в котором поднимается проблема парсинга и отображения в TStringGrid tsv файлов (tab-separated values – значения, разделенные символом табуляции, англ.). В таких файлах элементы в строках разделены знаком табуляции (девятым символом). Также не хочется забывать про не менее популярный формат csv – comma-separated values – в нём элементы разделены запятой. Стоит сказать, что программы для работы с таблицами просто обязаны читать эти два формата.
[Матчасть]
Как оказалось, delphi нам уже всё подготовил. Мы будем использовать два объекта TStringList и, очевидно, TStringGrid.
У объекта TStringList есть свойство Delimiter (разделитель – англ.). А сам TStringList по сути своей – массив строк.
DelimitedText – string. Строка, присвоенная DelimitedText, разобьётся, согласно Delimiter, и у нас становится TStringList с элементами строки. Впрочем, тот же explode на php.
QuoteChar – это свойство нам сегодня не пригодится, но упомянуть стоит. Например, у нас есть строка «delimiter»;»delimitedtext»;»quotechar»;»tstringlist». Элементы в ней не только разделены точкой с запятой, но еще и заключены в кавычки. В этом случае Delimiter ставим как ;, а QuoteChar как «. И легким движением руки мы получаем список из ключевых слов данного поста ![]()
Хотел сначала написать, что функция explode на php так не может (ха-ха), а потом прочитал, что там есть готовая функция fgetcsv, Нагайченко Максим написал об этом в посте на своём блоге.
Готовим TStringGrid
Для будущей загрузки таблицы, в Object Inspector я изначально поставил ColCount=1, а FixedCols и FixedRows обратил в ноль.
Парсим csv
Допустим, у нас есть таблица c:\table.csv, которую нам предстоит загрузить в TStringGrid.
Наши действия: каждую строку построчно разбиваем на элементы с помощью вышеописанных свойств и вставляем в TStringGrid.
В примере я использовал второй TStringList для открытия файла. Для больших таблиц также неплохо будет использование стандартного построчного получения файла, перешедшего из паскаля (AssignFile, Reset, ReadLn), но сегодня акцент не на этом.
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
sdata, srow: TStrings;
i:integer;
begin
sdata:=TStringList.Create;
srow:=TStringList.Create;
sdata.Delimiter:=',';
sdata.LoadFromFile('c:\table.csv');
StringGrid1.RowCount:=sdata.Count;
for i:=0 to sdata.Count-1 do begin
srow.DelimitedText:=sdata[i];
if srow.Count>StringGrid1.ColCount then
StringGrid1.ColCount:=srow.Count;
StringGrid1.Rows[i].Assign(srow);
end;
srow.Free;
sdata.Free;
end;
Небольшие пояснения: в sdata загружаем файл, строки из sdata по очереди парсим в srow, который каждый раз присваиваем следующей строке TStringGrid.
Про tsv и прочее
Для парсинга tsv свойству Delimiter нужно присвоить значение табуляции. Это девятый символ, #9, chr(9) – так можно записать. Согласно википедии, csv и tsv объединяет формат dsv – delimiter-separated values, собственно его мы сегодня и отпарсили.
(c) crystalbit, http://parsers.info
RSS поток – будь в курсе!
Ну во первых, TStringList не самый быстрый способ распарсить строку Во вторых readln тоже не самый быстрый способ считывания csv файлов.
По вашему примеру алгоритм примерно таким будет:
1. readln считывает из файла по байтно ища разделитель строк
2. найденная строка передаётся в stringlist, который снова проверяет всю строку посимвольно и разделяет строку.
3. Каждый элемент stringlist копируется в ячейку grid
Тут однозначно можно провести небольшой апгрейд алгоритма, тобишь оптимизацию:
1. Считываем например 1000 байт из файла в память.
2. Посимвольно проверяем каждый байт ища указанный разделитель или разделитель строк.
3. Если найден csv-разделитель, то копируем найденную часть строки в ячейку grid
4. Если разделитель строк, то создаем новую строку в grid
5. Таким образом считываем весь файл.
Алгоритм конечно сложнее по реализации, но скорость парсинга dsv файлов вырастает на порядок (проверенно опытным путём).
Соглашусь, TStringList – не самый оптимальный вариант, зато всё уже сделано за нас. А с небольшими таблицами скорость существенной роли не играет.
Ваш алгоритм не столь сложен в реализации простых таблиц, немного придётся потрудиться в случае, когда каждый элемент заключен в кавычки (QuoteChar). В принципе, повод, чтобы написать модуль для работы с dsv на winapi
Сам вначале долго программировал на 1Cv77. Как же я потом матерился в дельфи, когда искал аналог функции «ИзСтрокиСразделителем», а потом пришлось ручками писать свой аналог. Зато теперь любой разделитель в качестве параметра задается и ву-аля
Тоже программировал на 1Cv77 ,
но теперь все очень круто!