Ошибка при парсинге csv файла

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)>}

Я получаю некоторые данные в Кибане, но не то, что хочу видеть.

enter image description here

Мне удалось заставить его работать локально. ошибки, которые я заметил до сих пор, были:

  1. Использование зарезервированных полей ES, например @timestamp, @version и других.
  2. Отметка времени была не в формате ISO8601. В центре был знак @.
  3. Ваш фильтр установил разделитель на ,, но ваш реальный разделитель CSV — "t".
  4. В соответствии с ошибкой, которую вы видите, он также пытается работать с вашей строкой заголовков, я предлагаю вам удалить его из 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

Из моей Кибаны:

enter image description here


2

the Tin Man
28 Май 2020 в 00:02


  1. Dreadnought

    Dreadnought
    New Member
    Пользователи

    Регистрация:
    7 май 2013
    Сообщения:
    31

    Добрый день
    При парсинге штрихкодов в CSV, штрихкоды приобретают вид 4,72E+12 (получаются значения с большим количеством нулей 4720000000000, хотя при предварительном просмотре получалось 4720048010521).
    Скажите пожалуйста, как этого избежать?
    Заранее благодарю.

    Последнее редактирование: 27 фев 2015


  2. kagorec

    kagorec
    Администратор
    Команда форума
    Администратор

    вид приобретают когда просматриваете через Microsoft excel? Смотрите через текстовой редактор или через OpenOffice


  3. Root

    Root
    Администратор
    Администратор

    Регистрация:
    10 мар 2010
    Сообщения:
    14.818
    Город:
    Барнаул

    Здравствуйте.

    Откройте ваш CSV в блокноте и вы увидите там «нормальные данные» (4720000000000). MS Excel сам их «искажает» при открытии CSV-файлов. Но файл CSV ПО СУТИ В СЕБЕ СОДЕРЖИТ НОРМАЛЬНЫЕ ДАННЫЕ.

    Вывод: Не переживать по поводу отображения данных в Excel.

    Рекомендация: Пользоваться встроенным в программу Content Downloader CSV-редактором (соответствующая кнопка на панели инструментов).

    С уважением к вам, Сергей…


  4. Dreadnought

    Dreadnought
    New Member
    Пользователи

    Регистрация:
    7 май 2013
    Сообщения:
    31

    Проблема в том, что парсится 4720048010521, а значение в csv преобразуется и отображается как 4,72E+12 (что означает 4,72 с 12 нулями).
    Вот я и спрашиваю, как этого избежать? У меня такая же проблема была с парсингом цен (некоторые цены начинали отображаться как дата в excel),помог макрос <TOPRICE>.

    Последнее редактирование: 27 фев 2015


  5. Root

    Root
    Администратор
    Администратор

    Регистрация:
    10 мар 2010
    Сообщения:
    14.818
    Город:
    Барнаул

    Лично я Excel не использую и у меня его даже нет. Поэтому я не могу вам помочь с этим вопросом.

    Повторяю: В CSV-файле (куда вы парсите) данные в нужном формате! Вся проблема только в том, что Excel некорректно эти данные выводит вам на экран.


  6. kagorec

    kagorec
    Администратор
    Команда форума
    Администратор

    Если чудные цифры вместо цен получаете то естественно надо править границу которая цепляет цену с сайта.


  7. Dreadnought

    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)>}

Я получаю некоторые данные в Кибане, но не то, что хочу видеть.

enter image description here

Мне удалось заставить его работать локально. ошибки, которые я заметил до сих пор, были:

  1. Использование зарезервированных полей ES, например @timestamp, @version и других.
  2. Отметка времени была не в формате ISO8601. В центре был знак @.
  3. Ваш фильтр установил разделитель на ,, но ваш реальный разделитель CSV — "t".
  4. В соответствии с ошибкой, которую вы видите, он также пытается работать с вашей строкой заголовков, я предлагаю вам удалить его из 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

Из моей Кибаны:

enter image description here


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 :D

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 :D

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.

Понравилась статья? Поделить с друзьями:
  • Ошибка при очистке фио почта россии
  • Ошибка при очистке адреса почта россии
  • Ошибка при очистке windows 10
  • Ошибка при очистке dns кэша ошибка
  • Ошибка при очистке dns кеша