I am in the middle of parsing in a large amount of csv data. The data is rather «dirty» in that I have inconsistent delimiters, spurious characters and format issues that cause problems for read_csv().
My problem here, however, is not the dirtiness of the data, but just trying to understand the parsing errors that read_csv() is giving me. If I can better understand the error messages, I can then do some janitorial work to fix the problem with scripts. The size of the data makes a manual approach intractable.
Here’s a minimal example. Suppose I have a csv file like this:
"col_a","col_b","col_c"
"1","a quick","10"
"2","a quick "brown" fox","20"
"3","quick, brown fox","30"
Note that there’s spurious quotes around «brown» in the 2nd row. This content goes into a file called «my_data.csv«.
When I try to read that file, I get some parsing failures.
> library(tidyverse)
> df <- read_csv("./my_data.csv", col_types = cols(.default = "c"))
Warning: 2 parsing failures.
row # A tibble: 2 x 5 col row col expected actual file expected <int> <chr> <chr> <chr> <chr> actual 1 2 col_b delimiter or quote b './my_data.csv' file 2 2 col_b delimiter or quote './my_data.csv'
As you can see, the parsing failure has not been «pretty printed». It is ONE LONG LINE of 271 characters.
I can’t figure out where to even put linebreaks in the failure message to see where the problem is and what the message is trying to tell me. Moreover, it refers to a «2×5 tibble». What tibble? My data frame is 3×3.
Can someone show me how to format or put linebreaks in the message from read_csv() so I can see how it is detecting the problem?
Yes, I know what the problem is in this particular minimal example. In my actual data I am dealing with large amounts of csv (~1M rows), peppered with inconsistencies that shower me with hundreds of parsing failures. I’d like to setup a workflow for categorizing these and dealing with them programmatically. The first step, I think, is just understanding how to «parse» the parsing failure message.
Есть большой csv файл. 50 столбцов на 60000 строк. Заполнен значениями о товаре, гарантии и тд. Проблема в том, что при парсинге выдает ошибку «java.io.IOException: (line 18476) invalid char between encapsulated token and delimiter». Я так понял где то проблема с разделителем. Как узнать в каком именно месте, учитывая что файл очень большой?
-
Вопрос заданболее трёх лет назад
-
544 просмотра
В районе 18476 строки плюс минус-несколько строк, ошибку не находите?
В парсинге можно добавить дебаг вывод, чтобы увидеть на какой строке он что успел вывести перед остановкой.
Пригласить эксперта
Варианты решений:
1. Если программа не Ваша, то поищете csv-linter
2. Если программа Ваша, то примените log.debug(….)
3. Если программа не Ваша, то на пишите скрипт lint-ер сами.
Про нумеровано в порядке предпринимаемых действий.
Это мое мнение, но думаю есть решение по-лучше!
-
Показать ещё
Загружается…
14 июн. 2023, в 03:49
3000 руб./за проект
14 июн. 2023, в 03:45
4000 руб./за проект
14 июн. 2023, в 01:02
5000 руб./за проект
Минуточку внимания
Я использую Filebeat для отправки файла CSV в Logstash, а затем в Kibana, однако я получаю сообщение об ошибке синтаксического анализа, когда файл CSV выбирается Logstash.
Это содержимое файла CSV:
time version id score type
May 6, 2020 @ 11:29:59.863 1 2 PPy_6XEBuZH417wO9uVe _doc
Файл logstash.conf:
input {
beats {
port => 5044
}
}
filter {
csv {
separator => ","
columns =>["time","version","id","index","score","type"]
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
}
}
Filebeat.yml:
filebeat.inputs:
# Each - is an input. Most options can be set at the input level, so
# you can use different inputs for various configurations.
# Below are the input specific configurations.
- type: log
# Change to true to enable this input configuration.
enabled: true
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /etc/test/*.csv
#- c:programdataelasticsearchlogs*
И ошибка в Logstash:
[2020-05-27T12:28:14,585][WARN ][logstash.filters.csv ][main] Error parsing csv {:field=>"message", :source=>"time,version,id,score,type,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,", :exception=>#<TypeError: wrong argument type String (expected LogStash::Timestamp)>}
[2020-05-27T12:28:14,586][WARN ][logstash.filters.csv ][main] Error parsing csv {:field=>"message", :source=>""May 6, 2020 @ 11:29:59.863",1,2,PPy_6XEBuZH417wO9uVe,_doc,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,", :exception=>#<TypeError: wrong argument type String (expected LogStash::Timestamp)>}
Я получаю некоторые данные в Кибане, но не то, что хочу видеть.
Мне удалось заставить его работать локально. ошибки, которые я заметил до сих пор, были:
- Использование зарезервированных полей ES, например
@timestamp
,@version
и других. - Отметка времени была не в формате ISO8601. В центре был знак
@
. - Ваш фильтр установил разделитель на
,
, но ваш реальный разделитель CSV —"t"
. - В соответствии с ошибкой, которую вы видите, он также пытается работать с вашей строкой заголовков, я предлагаю вам удалить его из CSV или использовать вариант
skip_header
.
Ниже приведен файл logstash.conf, который я использовал:
input {
file {
path => "C:/work/elastic/logstash-6.5.0/config/test.csv"
start_position => "beginning"
}
}
filter {
csv {
separator => ","
columns =>["time","version","id","score","type"]
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "csv-test"
}
}
CSV-файл, который я использовал:
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
Из моей Кибаны:
2
the Tin Man
28 Май 2020 в 00:02
-
Dreadnought
New Member
Пользователи- Регистрация:
- 7 май 2013
- Сообщения:
- 31
Добрый день
При парсинге штрихкодов в CSV, штрихкоды приобретают вид 4,72E+12 (получаются значения с большим количеством нулей 4720000000000, хотя при предварительном просмотре получалось 4720048010521).
Скажите пожалуйста, как этого избежать?
Заранее благодарю.Последнее редактирование: 27 фев 2015
-
kagorec
Администратор
Команда форума
Администраторвид приобретают когда просматриваете через Microsoft excel? Смотрите через текстовой редактор или через OpenOffice
-
Root
Администратор
Администратор- Регистрация:
- 10 мар 2010
- Сообщения:
- 14.818
- Город:
- Барнаул
Здравствуйте.
Откройте ваш CSV в блокноте и вы увидите там «нормальные данные» (4720000000000). MS Excel сам их «искажает» при открытии CSV-файлов. Но файл CSV ПО СУТИ В СЕБЕ СОДЕРЖИТ НОРМАЛЬНЫЕ ДАННЫЕ.
Вывод: Не переживать по поводу отображения данных в Excel.
Рекомендация: Пользоваться встроенным в программу Content Downloader CSV-редактором (соответствующая кнопка на панели инструментов).
С уважением к вам, Сергей…
-
Dreadnought
New Member
Пользователи- Регистрация:
- 7 май 2013
- Сообщения:
- 31
Проблема в том, что парсится 4720048010521, а значение в csv преобразуется и отображается как 4,72E+12 (что означает 4,72 с 12 нулями).
Вот я и спрашиваю, как этого избежать? У меня такая же проблема была с парсингом цен (некоторые цены начинали отображаться как дата в excel),помог макрос <TOPRICE>.Последнее редактирование: 27 фев 2015
-
Root
Администратор
Администратор- Регистрация:
- 10 мар 2010
- Сообщения:
- 14.818
- Город:
- Барнаул
Лично я Excel не использую и у меня его даже нет. Поэтому я не могу вам помочь с этим вопросом.
Повторяю: В CSV-файле (куда вы парсите) данные в нужном формате! Вся проблема только в том, что Excel некорректно эти данные выводит вам на экран.
-
kagorec
Администратор
Команда форума
АдминистраторЕсли чудные цифры вместо цен получаете то естественно надо править границу которая цепляет цену с сайта.
-
Dreadnought
New Member
Пользователи- Регистрация:
- 7 май 2013
- Сообщения:
- 31
Выполнил парсин еще раз и все стало нормально. Цифры также сокращаются как астрономические, однако значения не теряются.
UPD. Значения теряются при пересохранении файла в excel’e. Сперва нужна перевести ячийки с большими цифрами в другой формат.
Спасибо за помощь.Последнее редактирование: 27 фев 2015
Поделиться этой страницей
I am in the middle of parsing in a large amount of csv data. The data is rather «dirty» in that I have inconsistent delimiters, spurious characters and format issues that cause problems for read_csv().
My problem here, however, is not the dirtiness of the data, but just trying to understand the parsing errors that read_csv() is giving me. If I can better understand the error messages, I can then do some janitorial work to fix the problem with scripts. The size of the data makes a manual approach intractable.
Here’s a minimal example. Suppose I have a csv file like this:
"col_a","col_b","col_c"
"1","a quick","10"
"2","a quick "brown" fox","20"
"3","quick, brown fox","30"
Note that there’s spurious quotes around «brown» in the 2nd row. This content goes into a file called «my_data.csv«.
When I try to read that file, I get some parsing failures.
> library(tidyverse)
> df <- read_csv("./my_data.csv", col_types = cols(.default = "c"))
Warning: 2 parsing failures.
row # A tibble: 2 x 5 col row col expected actual file expected <int> <chr> <chr> <chr> <chr> actual 1 2 col_b delimiter or quote b './my_data.csv' file 2 2 col_b delimiter or quote './my_data.csv'
As you can see, the parsing failure has not been «pretty printed». It is ONE LONG LINE of 271 characters.
I can’t figure out where to even put linebreaks in the failure message to see where the problem is and what the message is trying to tell me. Moreover, it refers to a «2×5 tibble». What tibble? My data frame is 3×3.
Can someone show me how to format or put linebreaks in the message from read_csv() so I can see how it is detecting the problem?
Yes, I know what the problem is in this particular minimal example. In my actual data I am dealing with large amounts of csv (~1M rows), peppered with inconsistencies that shower me with hundreds of parsing failures. I’d like to setup a workflow for categorizing these and dealing with them programmatically. The first step, I think, is just understanding how to «parse» the parsing failure message.
Я использую Filebeat для отправки файла CSV в Logstash, а затем в Kibana, однако я получаю сообщение об ошибке синтаксического анализа, когда файл CSV выбирается Logstash.
Это содержимое файла CSV:
time version id score type
May 6, 2020 @ 11:29:59.863 1 2 PPy_6XEBuZH417wO9uVe _doc
Файл logstash.conf:
input {
beats {
port => 5044
}
}
filter {
csv {
separator => ","
columns =>["time","version","id","index","score","type"]
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
}
}
Filebeat.yml:
filebeat.inputs:
# Each - is an input. Most options can be set at the input level, so
# you can use different inputs for various configurations.
# Below are the input specific configurations.
- type: log
# Change to true to enable this input configuration.
enabled: true
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /etc/test/*.csv
#- c:programdataelasticsearchlogs*
И ошибка в Logstash:
[2020-05-27T12:28:14,585][WARN ][logstash.filters.csv ][main] Error parsing csv {:field=>"message", :source=>"time,version,id,score,type,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,", :exception=>#<TypeError: wrong argument type String (expected LogStash::Timestamp)>}
[2020-05-27T12:28:14,586][WARN ][logstash.filters.csv ][main] Error parsing csv {:field=>"message", :source=>""May 6, 2020 @ 11:29:59.863",1,2,PPy_6XEBuZH417wO9uVe,_doc,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,", :exception=>#<TypeError: wrong argument type String (expected LogStash::Timestamp)>}
Я получаю некоторые данные в Кибане, но не то, что хочу видеть.
Мне удалось заставить его работать локально. ошибки, которые я заметил до сих пор, были:
- Использование зарезервированных полей ES, например
@timestamp
,@version
и других. - Отметка времени была не в формате ISO8601. В центре был знак
@
. - Ваш фильтр установил разделитель на
,
, но ваш реальный разделитель CSV —"t"
. - В соответствии с ошибкой, которую вы видите, он также пытается работать с вашей строкой заголовков, я предлагаю вам удалить его из CSV или использовать вариант
skip_header
.
Ниже приведен файл logstash.conf, который я использовал:
input {
file {
path => "C:/work/elastic/logstash-6.5.0/config/test.csv"
start_position => "beginning"
}
}
filter {
csv {
separator => ","
columns =>["time","version","id","score","type"]
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "csv-test"
}
}
CSV-файл, который я использовал:
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
May 6 2020 11:29:59.863,1,PPy_6XEBuZH417wO9uVe,_doc
Из моей Кибаны:
2
the Tin Man
28 Май 2020 в 00:02
unit mfr;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, TAGraph, TASeries, TATransformations, TATools,
Forms, Controls, Graphics, Dialogs, StdCtrls, DBGrids, Grids, ComCtrls,
ExtCtrls, FileCtrl, EditBtn, Buttons, Arrow, csvdocument, TAChartAxis,
Ipfilebroker, RTTICtrls, laz_synapse, ftpsend;
type
{ TForm1 }
TForm1 = class(TForm)
Arrow1: TArrow;
btDrawAll: TButton;
btLoad: TButton;
btCompare: TButton;
btShift: TButton;
CATransformLeftAutoScaleAxisTransform1: TAutoScaleAxisTransform;
CATransformRifgtAutoScaleAxisTransform1: TAutoScaleAxisTransform;
Chart1: TChart;
CGChecker: TCheckGroup;
CATransformLeft: TChartAxisTransformations;
CATransformRifgt: TChartAxisTransformations;
CATransformBar2: TChartAxisTransformations;
CATransformBar2AutoScaleAxisTransform1: TAutoScaleAxisTransform;
CATransformBar1: TChartAxisTransformations;
CATransformBar1AutoScaleAxisTransform1: TAutoScaleAxisTransform;
CheckBox1: TCheckBox;
FileListBox1: TFileListBox;
Label1: TLabel;
ListBox1: TListBox;
ListView1: TListView;
Memo1: TMemo;
StringGrid1: TStringGrid;
TrackBar1: TTrackBar;
procedure btCompareClick(Sender: TObject);
procedure btDrawAllClick(Sender: TObject);
procedure btLoadClick(Sender: TObject);
procedure CheckBox1Change(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ListView1DblClick(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
procedure UpdateFl;
private
{ private declarations }
public
FTPSend: TFTPSend;
{ public declarations }
end;
type csvLog=class(TCSVDocument)
public
Title,Date,Sample,Data:string;
Combination: boolean;
Units,Combi:array[0..16] of string;
ur:integer;
//загрузка данных и обработка шапки файла
procedure FTPReadInfo(filename:string);
//поиск № строки по тексту в первой колонке
function RowSearch(text:string):Integer;
//поиск № колонки по тексту и номеру строки
function ColSearch(text:string;CRow:Integer):Integer;
end;
var
Form1: TForm1;
csv:csvLog;
test,s0: string;
syn1,syn2,syn9:integer;
autoshift,combination:boolean;
limits:array[1..10,1..2] of real;
axisind:array[1..6] of Integer;
const
COLORS: array [0..15] of Integer =
($000080,$008000,$008080,$800000,$800080,$808000,$808080,$C0C0C0,$0000FF,$00FF00,$00FFFF,$FF0000,$FF00FF,$FFFF00,$000000,$F0CAA6);
implementation
{$R *.lfm}
{ TForm1 }
procedure csvLog.FTPReadInfo(filename:string);
var i,dr,cr: integer;
begin
csv.Clear;
//чтение из файла в режиме возможной докачки
if Form1.FTPSend.RetrieveFile(filename,true) then Self.LoadFromStream(Form1.FTPSend.DataStream) else exit;
//заголовок файла
Title:='TITLE: '+Cells[1,RowSearch('TITLE')];
//формирование даты
dr:=RowSearch('TRG_TIME');
Date:='TRG_TIME: '+Cells[3,dr]+'/'+Cells[2,dr]+'/20'+Cells[1,dr]+' '+Cells[4,dr]+':'+Cells[5,dr]+':'+Cells[6,dr];
//интервал замеров
Sample:='TRG_SAMPLE: '+Cells[1,RowSearch('TRG_SAMPLE') ]+' ms';
//массив заголовков данных
ur:=RowSearch('UNIT');
Combination:=false;
Units[0]:='UNIT: ';
for i:=1 to ColCount[ur] do
begin
Units[i]:=Cells[i,ur];
end;
//подсчет кол-ва строк с данными
Data:='DATA: '+IntToStr(RowCount-ur-2)+' lines';
//массив заголовков данных для комбинированного файла
cr:=RowSearch('COMBI');
if Cells[0,cr]='COMBI' then
begin
Combination:=true;
Combi[0]:='COMBI: ';
for i:=1 to ColCount[cr-1] do
begin
Combi[i]:=Cells[i,cr]+',';
end;
end;
end;
function csvLog.RowSearch(text:string):Integer;
var i:integer;
begin
i:=0;
while (Self.Cells[0,i]<>text) and (i<Self.RowCount-1) do inc(i);
if i=Self.RowCount-2 then result:=-1;
result:=i;
end;
function csvLog.ColSearch(text:string;CRow:Integer):Integer;
var i:integer;
begin
i:=0;
while (Self.Cells[i,CRow]<>text) and (i<Self.ColCount[CRow]-1) do inc(i);
if i=(Self.ColCount[CRow]-2) then result:=-1;
result:=i;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//создаем соединение с FTP
FTPSend:=TFTPSend.Create;
FTPSend.TargetHost:='172.16.170.11';
FTPSend.UserName:='anonymous';
FTPSend.Password:='';
FTPSend.PassiveMode:=True;
if FTPSend.Login then
begin
//получаем информацию об объектах в директории
FTPSend.List(FTPSend.GetCurrentDir, false);
//обновляем список
UpdateFL;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
csv.Free;
FTPSend.Logout;
FTPSend.Free;
end;
//обновление списка файлов
procedure TForm1.UpdateFl;
var LI: TlistItem;
I: Integer;
begin
ListView1.Items.BeginUpdate;
try
ListView1.Items.Clear;
//создаем элемент для перехода на один уровень вверх
LI:=ListView1.Items.Add;
LI.Caption:='[...]';
//заполняем список информацией об объектах в текущей директории
for I := 0 to FTPSend.FtpList.Count-1 do
begin
LI:=ListView1.Items.Add;
LI.Caption:=FTPSend.FtpList[i].FileName;
if FTPSend.FtpList[i].Directory then
Li.SubItems.Add('Папка')
else
Li.SubItems.Add('Файл');
LI.SubItems.Add(IntToStr(FTPSend.FtpList[i].FileSize));
LI.SubItems.Add((DateToStr(FTPSend.FtpList[i].FileTime)));
end;
finally
ListView1.Items.EndUpdate;
end;
end;
//обработка двойного клика в списке файлов
procedure TForm1.ListView1DblClick(Sender: TObject);
var b:boolean;
s,line:string;
i:integer;
begin
btLoad.Enabled:=false;
if not Assigned(ListView1.Selected) then Exit;
//переход на уровень вверх
if ListView1.ItemIndex=0 then
b:=FTPSend.ChangeToParentDir
else
//смена директории на выбранную
b:=FTPSend.ChangeWorkingDir(ListView1.Selected.Caption);
if b then
begin
//получаем данные об объектах в текущей директории
FTPSend.List(EmptyStr,False);
//обновляем список
UpdateFL;
end
else
begin
s:=FTPSend.FtpList[ListView1.Selected.Index-1].FileName;
//если файл нужного формата (csv), выводим информацию
if Pos('.csv',s)>0 then
begin
//активация кнопки "Загрузить"
btLoad.Enabled:=true;
csv:=csvLog.Create;
csv.FTPReadInfo(s);
Memo1.Clear;
Memo1.Lines.Add(csv.Title);
Memo1.Lines.Add(csv.Date);
Memo1.Lines.Add(csv.Sample);
//создание списка заголовков в зависимости от типа файла
if Combination then
for i:=0 to 16 do line:=line+csv.Combi[i]
else
for i:=0 to 16 do line:=line+csv.Units[i];
Memo1.Lines.Add(line);
Memo1.Lines.Add(csv.Data);
end;
end;
end;
//загрузка данных в таблицу
procedure LoadGrid(Grid:TStringGrid);
var col,row:integer;
begin
Grid.BeginUpdate;
Grid.Clear;
Grid.RowCount:=csv.RowCount;
Grid.ColCount:=csv.ColCount[csv.ur+1];
for col:=0 to Grid.ColCount-1 do
// "row+csv.ur" задает сдвиг по строкам для вывода только данных с первой строки таблицы
for row:=0 to Grid.RowCount-1 do Grid.Cells[col,row]:=csv.Cells[col,row+csv.ur];
Grid.EndUpdate;
end;
//замена точки на запятую, вычисление максимумов и точки синхронизации
procedure ReplGrid(Grid: TStringGrid);
var i,y,c,x:integer;
curr: real;
begin
syn1:=0; syn2:=0;
for x:=1 to Grid.ColCount-1 do
for y:=1 to 2 do limits[x,y]:=0.0; //обнуление массива пределов
Grid.BeginUpdate;
for i:=1 to Grid.RowCount-1 do begin
for c:=1 to Grid.ColCount-1 do
begin
if Grid.Cells[c,i]='' then break;
Grid.Cells[c,i]:=StringReplace(Grid.Cells[c,i],'.',',',[rfReplaceAll, rfIgnoreCase]); //замена
curr:=StrToFloat(Grid.Cells[c,i]);
if curr>limits[c,1] then limits[c,1]:=curr; //максимум
if curr<limits[c,2] then limits[c,2]:=curr; //минимум
if (Grid.Col=8) and (curr<>0) and (syn1<>0) then syn1:=Grid.Row; //точка синхронизации
end;
end;
Form1.Memo1.Lines.Add(FloatToStr(limits[2,1])+'/'+FloatToStr(limits[2,2])+', '+FloatToStr(limits[3,1])+'/'+FloatToStr(limits[3,2]));
Grid.EndUpdate;
end;
//отображение позиции трекбара
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
Label1.Caption:=IntToStr(TrackBar1.Position);
end;
//отрисовка графика по таблице
procedure TForm1.btDrawAllClick(Sender: TObject);
var i,j:integer;
ls: TLineSeries;
trl,trr: TChartAxisTransformations;
begin
Chart1.ClearSeries; //все серии удаляются
Chart1.AxisList[2].Visible:=false;
Chart1.AxisList[0].Transformations.Destroy;
trl:= TChartAxisTransformations.Create(Self);
TAutoScaleAxisTransform.Create(Self).Transformations:=trl;//трансформация для левой оси
Chart1.AxisList[0].Transformations:=trl;
Chart1.AxisList[2].Transformations.Destroy;
trr:= TChartAxisTransformations.Create(Self);
TAutoScaleAxisTransform.Create(Self).Transformations:=trr;//трансформация для правой оси
Chart1.AxisList[2].Transformations:=trr;
for i := 1 to 6 do //создание новых серий
begin
ls := TLineSeries.Create(Self);
Chart1.AddSeries(ls);
if CGChecker.Checked[i-1] then //проверяем, отмечена ли серия для отрисовки
begin
ls.SeriesColor := COLORS[i];
ls.LinePen.Width := 2;
ls.Legend.Visible:=true;
ls.Title:=StringGrid1.Cells[i+1,0];
//серии с большими максимумами привязываются к правой оси
if (limits[i+1,1]>100) or (limits[i+1,2]<-100) then
begin
ls.AxisIndexY:=2;
Chart1.AxisList[2].Visible:=true;
end
else ls.AxisIndexY:=0;
//флаговые серии привязываются к 2-м нижним осям
// if i=7 then ls.AxisIndexY:=3;
// if i=8 then ls.AxisIndexY:=4;
axisind[i]:=ls.AxisIndexY; //запись в массив индексов осей
for j := 1 to StringGrid1.RowCount-1 do
begin
if (StringGrid1.Cells[1,j]='') or (StringGrid1.Cells[i+1,j]='') then break;
ls.AddXY(StrToFloat(StringGrid1.Cells[1,j]),StrToFloat(StringGrid1.Cells[i+1,j]));
end;
end;
end;
end;
//отрисовка вторичных серий и синхронизация
procedure TForm1.btCompareClick(Sender: TObject);
var csv2:csvLog;
ccol,crow,i,c,x,urow,shift:Integer;
ls2: TLineSeries;
begin
shift:=0;
x:=Chart1.SeriesCount;
while x>6 do
begin
Chart1.DeleteSeries(Chart1.Series.Items[x-1]); //удаление старых вторичных серий если есть
x:=Chart1.SeriesCount;
end;
for c:=1 to 6 do
begin
ls2:=TLineSeries.Create(Self); //создание новых серий
Chart1.AddSeries(ls2);
if CGChecker.Checked[c-1] then //проверка на сравнение отмеченных серий
begin
ls2.SeriesColor:=COLORS[c];
ls2.Legend.Visible:=false;
ls2.LinePen.Width:=1;
ls2.AxisIndexY:=axisind[c];
if not(FileExists(FileListBox1.FileName)) then exit; //TODO: FTP
csv2:=csvLog.Create;
csv2.LoadFromFile(FileListBox1.FileName);
urow:=csv2.RowSearch('UNIT');
//поиск нужной колонки по заголовку в соответствующем чекере
ccol:=csv2.ColSearch(CGChecker.Items.Strings[c-1],urow);
crow:=csv2.RowSearch('DATA 1');
//если отмечена автосинхронизация, сравниваются точки синхронизации
if autoshift then
begin
//поиск совпадающих меток
for i:=crow to csv2.RowCount-1 do
begin
if (StrToInt(csv2.Cells[8,i])<>0) and (syn2<>0) then syn2:=i;
end;
if syn1<>syn2 then shift:=abs(syn1-syn2);
end
//при ручной синхронизации задается сдвиг по позиции трекбара
else if abs(TrackBar1.Position)<(csv2.RowCount-urow+1) then shift:=abs(TrackBar1.Position);
for i:=crow to StringGrid1.RowCount-1-shift do
begin
csv2.Cells[1,i]:=StringReplace(csv2.Cells[1,i],'.',',',[rfReplaceAll, rfIgnoreCase]);
csv2.Cells[ccol,i+shift]:=StringReplace(csv2.Cells[ccol,i+shift],'.',',',[rfReplaceAll, rfIgnoreCase]);
//обнуление пустых ячеек, если встречаются
if csv2.Cells[1,i]='' then csv2.Cells[1,i]:='0';
//сдвиг идет только по колонке с данными
if csv2.Cells[ccol,i+shift]='' then csv2.Cells[ccol,i+shift]:='0';
ls2.AddXY(StrToFloat(csv2.Cells[1,i]),StrToFloat(csv2.Cells[ccol,i+shift]));
end;
end;
end;
csv2.Free;
end;
//основная функция: чтение из файла, загрузка в таблицу, очистка, замена знака
//TODO: FTP
procedure TForm1.btLoadClick(Sender: TObject);
var i:integer;
begin
btCompare.Enabled:=true;
LoadGrid(StringGrid1);
ReplGrid(StringGrid1);
CGChecker.Caption:=s0;
btDrawAll.Enabled:=true;
CGChecker.Items.Clear;
for i:=1 to 6 do
begin
CGChecker.Items.Add(StringGrid1.Cells[i+1,0]);
if not Combination then CGChecker.Checked[i-1]:=true;
end;
end;
//интерфейсная часть синхронизации
procedure TForm1.CheckBox1Change(Sender: TObject);
begin
if CheckBox1.Checked then
begin
autoshift:=true;
btShift.Enabled:=false;
TrackBar1.Position:=0;
TrackBar1.Enabled:=false;
Label1.Caption:='';
end
else
begin
autoshift:=false;
btShift.Enabled:=true;
TrackBar1.Enabled:=true;
end;
end;
end.
You don’t add to filesData
the parsed data but actually result returned by new File('').withReader{}
which is probably stream or anything else. Instead try:
listOfFiles.each { file ->
def csvData = file.withReader {
filesData.add(new CsvParser().parse( it , separator: CSV_SEPARATOR ))
}
}
UPDATE
I also doubt if You iterate over filesData
correctly. In every loop myIt
isn’t a data row but an instance of Iterable
returned from parse
method. Can’t experiment right now but maybe it should be.
for (myIt in filesData) {
myIt.each { rowIt ->
println rowIt.ID + " " + rowIt.NAME
}
}
UPDATE 2
Working example for groovy console
@Grab( 'com.xlson.groovycsv:groovycsv:1.0' )
import com.xlson.groovycsv.CsvParser
def csv1 = '''ID,NAME,ADDRESS
1,n1,a1
2,n2,a2
3,n3,a3'''
def csv2 = '''ID,NAME,ADDRESS
4,n4,a4
5,n5,a5
6,n6,a6
'''
def listOfFiles = [csv1, csv2]
def filesData = []
listOfFiles.each { filesData.add(new CsvParser().parse( it , separator: ',' )) }
filesData.each { d ->
d.each { r ->
println "$r.ID $r.NAME"
}
}
What You need to do is to check if all file operations run smoothly in the example You provided.
UPDATE 3
Here’s the sample code with a reproduced error:
@Grab( 'com.xlson.groovycsv:groovycsv:1.0' )
import com.xlson.groovycsv.CsvParser
def f1
def f2
try {
f1 = File.createTempFile("temp1",".csv")
f2 = File.createTempFile("temp2",".csv")
f1.text = '''ID,NAME,ADDRESS
1,n1,a1
2,n2,a2
3,n3,a3
'''
f2.text = '''ID,NAME,ADDRESS
4,n4,a4
5,n5,a5
6,n6,a6
'''
def listOfFiles = [f1, f2]
def filesData = []
listOfFiles.each { f ->
f.withReader { r ->
def data = new CsvParser().parse( r , separator: ',' )
filesData.add(data)
}
}
filesData.each { d ->
d.each { r ->
println "$r.ID $r.NAME"
}
}
} finally {
f1?.delete()
f2?.delete()
}
Now.. what’s going on here. In withReader
closure parse
method of CsvParser
is invoked with r
(reader) object passed. But withReader
closure isn’t evaluated eagerly but lazily. Then in the place where parsed data are printed the r
is read and while groovy detects the EOF
closes the stream automatically (it can be easily verified with changing 3,n3,a3n'''
to 3,n3,a3'''
. But the class au.com.bytecode.opencsv.CSVReader
(getNextLine()
method) doesn’t check if the stream is closed and try to read it which results in the Stream closed
exception. Actually this is a bug in CSVReader
class.
To correct it You need to change lazy reading to eager. This can be done with changing this line of code:
filesData.add(data)
to this:
filesData.add(data.toList())
which causes data to be read in the closure and not left for reading later on.
You don’t add to filesData
the parsed data but actually result returned by new File('').withReader{}
which is probably stream or anything else. Instead try:
listOfFiles.each { file ->
def csvData = file.withReader {
filesData.add(new CsvParser().parse( it , separator: CSV_SEPARATOR ))
}
}
UPDATE
I also doubt if You iterate over filesData
correctly. In every loop myIt
isn’t a data row but an instance of Iterable
returned from parse
method. Can’t experiment right now but maybe it should be.
for (myIt in filesData) {
myIt.each { rowIt ->
println rowIt.ID + " " + rowIt.NAME
}
}
UPDATE 2
Working example for groovy console
@Grab( 'com.xlson.groovycsv:groovycsv:1.0' )
import com.xlson.groovycsv.CsvParser
def csv1 = '''ID,NAME,ADDRESS
1,n1,a1
2,n2,a2
3,n3,a3'''
def csv2 = '''ID,NAME,ADDRESS
4,n4,a4
5,n5,a5
6,n6,a6
'''
def listOfFiles = [csv1, csv2]
def filesData = []
listOfFiles.each { filesData.add(new CsvParser().parse( it , separator: ',' )) }
filesData.each { d ->
d.each { r ->
println "$r.ID $r.NAME"
}
}
What You need to do is to check if all file operations run smoothly in the example You provided.
UPDATE 3
Here’s the sample code with a reproduced error:
@Grab( 'com.xlson.groovycsv:groovycsv:1.0' )
import com.xlson.groovycsv.CsvParser
def f1
def f2
try {
f1 = File.createTempFile("temp1",".csv")
f2 = File.createTempFile("temp2",".csv")
f1.text = '''ID,NAME,ADDRESS
1,n1,a1
2,n2,a2
3,n3,a3
'''
f2.text = '''ID,NAME,ADDRESS
4,n4,a4
5,n5,a5
6,n6,a6
'''
def listOfFiles = [f1, f2]
def filesData = []
listOfFiles.each { f ->
f.withReader { r ->
def data = new CsvParser().parse( r , separator: ',' )
filesData.add(data)
}
}
filesData.each { d ->
d.each { r ->
println "$r.ID $r.NAME"
}
}
} finally {
f1?.delete()
f2?.delete()
}
Now.. what’s going on here. In withReader
closure parse
method of CsvParser
is invoked with r
(reader) object passed. But withReader
closure isn’t evaluated eagerly but lazily. Then in the place where parsed data are printed the r
is read and while groovy detects the EOF
closes the stream automatically (it can be easily verified with changing 3,n3,a3n'''
to 3,n3,a3'''
. But the class au.com.bytecode.opencsv.CSVReader
(getNextLine()
method) doesn’t check if the stream is closed and try to read it which results in the Stream closed
exception. Actually this is a bug in CSVReader
class.
To correct it You need to change lazy reading to eager. This can be done with changing this line of code:
filesData.add(data)
to this:
filesData.add(data.toList())
which causes data to be read in the closure and not left for reading later on.