2009-10-07 3 views
4

для моего приложения я должен разобрать CSV файл, используя Erlang.following код, который будет анализировать CSV, используя Erlang: -CSV парсер в Эрл

parse_file(Fn) -> 
{ok, Data} = file:read_file(Fn), 
parse(binary_to_list(Data)). 

parse(Data) -> lists:reverse(parse(Data, [])). 

parse([], Acc) -> Acc; 
parse(Data, Acc) -> 
{Line, Tail} = parse_line(Data), 
parse(Tail, [Line|Acc]). 

parse_line(Data) -> 
{Line, Tail} = parse_line(Data, []), 
{lists:reverse(Line), Tail}. 

parse_line([13,10|Data], Acc) -> {Acc, Data}; 
parse_line([10|Data], Acc) -> {Acc, Data}; 
parse_line([13|Data], Acc) -> {Acc, Data}; 
parse_line([], Acc) -> {Acc, []}; 
parse_line([$,,$,|Data], Acc) -> parse_line(Data, [""|Acc]); 
parse_line([$,|Data], Acc) -> parse_line(Data, Acc); 
parse_line(Data, Acc) -> 
{Fld, Tail} = parse_field(Data), 
parse_line(Tail, [Fld|Acc]). 

parse_field([34|Data]) -> 
{Fld, Tail} = parse_fieldq(Data, ""), 
{lists:reverse(Fld), Tail}; 
parse_field(Data) -> 
{Fld, Tail} = parse_field(Data, ""), 
{lists:reverse(Fld), Tail}. 

parse_field([$,|Tail], Acc) -> {Acc, [$,|Tail]}; 
parse_field([13|Tail], Acc) -> {Acc, [13|Tail]}; 
parse_field([10|Tail], Acc) -> {Acc, [10|Tail]}; 
parse_field([], Acc) -> {Acc, []}; 
parse_field([Ch|Tail], Acc) -> parse_field(Tail, [Ch|Acc]). 

parse_fieldq([34,34|Tail], Acc) -> parse_fieldq(Tail, [34|Acc]); 
parse_fieldq([34|Tail], Acc) -> {Acc, Tail}; 
parse_fieldq([Ch|Tail], Acc) -> parse_fieldq(Tail, [Ch|Acc]). 

этот код работает отлично, но имеющие два вопроса: - 1 - так как анализ кода использует двойную кавычку ("") и запятую (,) и разделяет каждое значение ... но в следующем примере, если первое имя состоит из двойного кавычки в нем, тогда синтаксический анализатор создаст еще одно поле.

"Type","First Name","Last Name","Email" 
"Contact","Ashwani Garg ------"All Pain Will End."","","[email protected]" 

result:- 
[["contact"],["Ashwani Garg ------"],["All Pain Will End."],[],["[email protected]"]] 

expected result:- 
[["contact"],["Ashwani Garg ------All Pain Will End."],[],["[email protected]"]] 

2-для следующего вида CSV его для значения, его усеченный некоторое значение: - Имя, Фамилия, Отчество, Имя, Ник, Адрес электронной почты, Улица, Город, Главная Почтовый индекс, Главная Государство, Главная Страна/регион, Домашний телефон, домашний факс, мобильный телефон, персональная веб-страница, бизнес-стрит, бизнес-город, бизнес-почтовый индекс, бизнес-статус, бизнес-страна/регион, веб-страница для бизнеса, бизнес-телефон, Бизнес-факс, пейджер, Компания, Должность, отдел, офис Адрес, Примечание

Affection,,,Affection,,,,,,,,+919845141544,,+919845141544,,,,,,,,,,,,,,, 
    result:- 
    [["Affection"],[],[],["Affection"],[],[],[],[],[],[],[],["+919845141544"],[],["+919845141544"],[],[],[],[],[],[],[]] 
    expected result:- 
    [["Affection"],[],[],["Affection"],[],[],[],[],[],[],[],["+919845141544"],[],["+919845141544"],[],[],[],[],[],[],[],[],[],[],[],[],[],[]] 

Пожалуйста, помогите мне ... для пожалуйста, используйте Другой крупный по следующей ссылке: - http://ppolv.wordpress.com/2008/02/25/parsing-csv-in-erlang/

ответ

6
parse(File) -> 
    {ok, F} = file:open(File, [read, raw]), 
    parse(F, file:read_line(F), []). 

parse(F, eof, Done) -> 
    file:close(F), 
    lists:reverse(Done);  

parse(F, Line, Done) -> 
    parse(F, file:read_line(F), [parse_line(Line)|Done]). 



parse_line(Line) -> parse_line(Line, []). 

parse_line([], Fields) -> lists:reverse(Fields); 
parse_line("," ++ Line, Fields) -> parse_field(Line, Fields); 
parse_line(Line, Fields) -> parse_field(Line, Fields). 

parse_field("\"" ++ Line, Fields) -> parse_field_q(Line, [], Fields); 
parse_field(Line, Fields) -> parse_field(Line, [], Fields). 

parse_field("," ++ _ = Line, Buf, Fields) -> parse_line(Line, [lists:reverse(Buf)|Fields]); 
parse_field([C|Line], Buf, Fields) -> parse_field(Line, [C|Buf], Fields); 
parse_field([], Buf, Fields) -> parse_line([], [lists:reverse(Buf)|Fields]). 

parse_field_q(Line, Fields) -> parse_field_q(Line, [], Fields). 
parse_field_q("\"\"" ++ Line, Buf, Fields) -> parse_field_q(Line, [$"|Buf], Fields); 
parse_field_q("\"" ++ Line, Buf, Fields) -> parse_line(Line, [lists:reverse(Buf)|Fields]); 
parse_field_q([C|Line], Buf, Fields) -> parse_field_q(Line, [C|Buf], Fields). 

без файла: read_line:

parse_file(File) -> 
    {ok, Data} = file:read_file(File), 
    parse(binary_to_list(Data), []). 

parse([], Done) -> 
    lists:reverse(Done); 

parse(Data, Done) -> 
    {Line, Rest} = case re:split(Data, "\r|\n|\r\n", [{return, list}, {parts, 2}]) of 
        [L,R] -> {L,R}; 
        [L] -> {L,[]} 
       end, 
    parse(Rest, [parse_line(Line)|Done]). 
+0

спасибо за ваши reply..i запустили вашего script..it работает хорошо и сортировка также, но проверить .. вывод [[ «Тип», «Имя», «Фамилия», "Электронная почта "], [[]], [" Контакт "," null ", []," [email protected] "], [[]] , но вы можете видеть, что есть одна [[]] коробка в каждой записи, которая не используется, мы можем избежать этого ... – Abhimanyu

+0

ОК, я изменил код, чтобы поместить строки в дополнительный список. – Zed

+0

** ошибка исключения: неопределенный файл функции: read_line/1 в функции csv_erl_parser: parse/1 – Abhimanyu

1

Чтение строк из файла также обсуждался в Trapexit. Она должна быть тривиальной адаптироваться, что к вашим потребностям:

http://www.trapexit.org/Reading_Lines_from_a_File

+0

Первый URL-адрес кажется таким же, как и в конце вопроса;) – Zed

+0

lol. Извини за это. Я этого не видел. –

2

Побочный вопрос:

Как вы создаете вход CSV? Он, похоже, не является достоверным CSV (но не потому, что существует особенно строгая спецификация для CSV).

Обычно использовать двойные кавычки внутри поля CSV они должны быть экранированы, как пара двойных кавычек, так что ваш пример будет:

"Type","First Name","Last Name","Email" 
"Contact","Ashwani Garg ------""All Pain Will End.""","","[email protected]" 

Это будет импортировать штраф в открытый офис таблицы, в то время как ваш оригинал пример нет.

0

Другое возможное решение. Может быть легко изменен на ленивую оценку, поэтому чтение всего файла сразу не требуется.

parse(Data) -> parse(Data, [], [], []). 

parse([$\r|Data], Field, Fields, Lines) -> parse_r(Data, Field, Fields, Lines); 
parse([$\n|Data], Field, Fields, Lines) -> parse(Data, [], [], [[Field|Fields]|Lines]); 
parse([$,|Data], Field, Fields, Lines) -> parse(Data, [], [Field|Fields], Lines); 
parse([$"|Data], [], Fields, Lines)  -> parse_q(Data, [], Fields, Lines); 
parse([C|Data], Field, Fields, Lines) -> parse(Data, [C|Field], Fields, Lines); 
parse([], Field, Fields, Lines)   -> 
    lists:reverse(
     [lists:reverse(
      [lists:reverse(F) || F <- L] 
     ) || L <- [[Field|Fields]|Lines]] 
    ). 

parse_r([$\n|_] = Data, Field, Fields, Lines) -> parse(Data, Field, Fields, Lines). 

parse_q([$"|Data], Field, Fields, Lines) -> parse_qq(Data, Field, Fields, Lines); 
parse_q([C|Data], Field, Fields, Lines) -> parse_q(Data, [C|Field], Fields, Lines). 

parse_qq([$"|Data], Field, Fields, Lines) -> parse_q(Data, [$"|Field], Fields, Lines); 
parse_qq([C|_] = Data, Field, Fields, Lines) 
    when C == $,; C == $\r; C == $\n  -> parse(Data, Field, Fields, Lines); 
parse_qq([], Field, Fields, Lines)  -> parse([], Field, Fields, Lines).
1

Моя реализация:

-module(csv). 

-export([ 
    parse/1 
]). 

parse(File) -> 
    try 
     {ok, Bin} = file:read_file(File), 
     {ok, parse(binary_to_list(Bin), [], [], [])} 
    catch 
     Class:Error -> 
      {Class, Error} 
    end. 

parse([], _FBuff, _RBuff, Result) -> 
    lists:reverse(Result); 
parse([$" | Rest], _FBuff, RBuff, Result) -> 
    {F, Rest1} = parse_q(Rest, []), 
    parse(Rest1, [], [F | RBuff], Result); 
parse([$,, $\s| Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [], [lists:reverse(FBuff) | RBuff], Result);  
parse([$, | Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [], [lists:reverse(FBuff) | RBuff], Result); 
parse([$\r, $\n | Rest], _FBuff, RBuff, Result) -> 
    parse(Rest, [], [], [lists:reverse(RBuff) | Result]); 
parse([$\n | Rest], _FBuff, RBuff, Result) -> 
    parse(Rest, [], [], [lists:reverse(RBuff) | Result]); 
parse([A | Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [A | FBuff], RBuff, Result). 

parse_q([$", $, | Rest], Result) -> 
    {lists:reverse(Result), Rest}; 
parse_q([A | Rest], Result) -> 
    parse_q(Rest, [A | Result]). 

Однако это решение не может иметь дело с вложенными кавычками ...

Например:

1, "Привет," Мир «», «Она говорит:« Это «решение», не так ли? », 2000 \ r \ n

1

Я столкнулся с вашей реализацией в другой день, и начал играть с ним.

Я также сделал вам парсер.

-module(csv_parser). 

-export([parse_file/1]). 

parse_file(File) -> 
    {ok, Data} = file:read_file(File), 
    parse(Data). 

parse(Data) -> 
    Lines = re:split(Data, "\r|\n|\r\n", []), 
    [ [begin 
      case re:split(Token, "\"", []) of 
       [_,T,_] -> T; 
       [] -> <<"">> 
      end 
     end || Token <- re:split(Line, ",", []) ] || Line <- Lines, Line =/= <<"">>]. 

Я даже написал небольшую BlogPost на этом csv parser

0

Я добавил несколько усовершенствований ответа Zed в.

-module (helper_csv_parser). 
-compile(export_all). 

% Taken from http://stackoverflow.com/questions/1532081/csv-parser-in-erlang, modified to fix errors. 
parse(File) -> 
    {ok, F} = file:open(File, [read, {encoding, utf8}]), 
    {ok, L} = file:read_line(F), 
    parse(F, string:strip(L, right, $\n), [], 1). 

parse(F, eof, Done, _) -> 
    file:close(F), 
    lists:reverse(Done); 

parse(F, Line, Done, Ctr) -> 
    Res = file:read_line(F), 

    case Res of 
     {error,collect_line} -> throw({error, "Might be unicode at line " ++ helper:i2s(Ctr)}); 
     {ok, L} -> parse(F, string:strip(L, right, $\n),[parse_line(Line)|Done], Ctr+1); 
     eof -> parse(F,eof,[parse_line(Line)|Done], Ctr+1) 
    end. 

parse_line("," ++ Line) -> parse_line(Line, [[]]); 
parse_line(Line) -> parse_line(Line, []). 

parse_line([], Fields) -> lists:reverse(Fields); 
parse_line("," ++ Line, Fields) -> parse_field(Line, Fields); 
parse_line(Line, Fields) -> parse_field(Line, Fields). 

parse_field("\"" ++ Line, Fields) -> parse_field_q(Line, [], Fields); 
parse_field(Line, Fields) -> parse_field(Line, [], Fields). 

parse_field("," ++ _ = Line, Buf, Fields) -> parse_line(Line, [string:strip(lists:reverse(Buf))|Fields]); 
parse_field([C|Line], Buf, Fields) -> parse_field(Line, [C|Buf], Fields); 
parse_field([], Buf, Fields) -> parse_line([], [lists:reverse(Buf)|Fields]). 

parse_field_q(Line, Fields) -> parse_field_q(Line, [], Fields). 
parse_field_q("\"\"" ++ Line, Buf, Fields) -> parse_field_q(Line, [$"|Buf], Fields); 
parse_field_q("\"" ++ Line, Buf, Fields) -> parse_line(Line, [string:strip(lists:reverse(Buf))|Fields]); 
parse_field_q([C|Line], Buf, Fields) -> parse_field_q(Line, [C|Buf], Fields). 
0

Ответ Wisher не плох, за исключением того, что он теряет последний элемент каждой линии csv. Вот за что. Тем не менее, он все еще не занимается встроенными кавычками.

-module(csv). 

-export([read/1]). 

read(File) -> 
    try 
     {ok, Bin} = file:read_file(File), 
     {ok, parse(binary_to_list(Bin), [], [], [])} 
    catch 
     Class:Error -> 
      {Class, Error} 
    end. 

parse([], _FBuff, _RBuff, Result) -> 
    lists:reverse(Result); 
parse([$" | Rest], _FBuff, RBuff, Result) -> 
    {F, Rest1} = parse_q(Rest, []), 
    parse(Rest1, [], [F | RBuff], Result); 
parse([$,, $\s| Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [], [lists:reverse(FBuff) | RBuff], Result); 
parse([$, | Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [], [lists:reverse(FBuff) | RBuff], Result); 
parse([$\r, $\n | Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [], [], [lists:reverse([lists:reverse(FBuff) | RBuff]) | Result]); 
parse([$\n | Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [], [], [lists:reverse([lists:reverse(FBuff) | RBuff]) | Result]); 
parse([A | Rest], FBuff, RBuff, Result) -> 
    parse(Rest, [A | FBuff], RBuff, Result). 

parse_q([$", $, | Rest], Result) -> 
    {lists:reverse(Result), Rest}; 
parse_q([A | Rest], Result) -> 
    parse_q(Rest, [A | Result]). 
Смежные вопросы