Адъ CSV

Nov. 9th, 2009 08:28 pm
metaclass: (Default)
[personal profile] metaclass
А напишите кто-нибудь табличку состояний CSV парсера? Т.е. последовательность символов, разделенных запятыми, в строках, разделенных \r или \r\n разбить на список списков строк. Если в строке должны быть служебные символы (т.е. запятая или \r \n) - строка обрамляется в кавычки, если внутри такой строки нужна кавычка - ставится две кавычки подряд.
На самом деле там немного сложнее, типа допустимо незначимые пробелы возле запятых, а значимыми их делают тоже через кавычки, управляющие символы <32 вроде тоже обязательно в кавычки, но это пофиг.
Вроде блин простая задачка, а каждый раз когда ее приходится делать - получаются на пару страниц конечные автоматы.

Date: 2009-11-09 08:58 pm (UTC)
From: [identity profile] kkirsanov.livejournal.com
Доктор!
Экспортирую из 2007 экселя в CSV. Он, зараза, и кавычки не ставит и служебные символы не эскейпит. С горя в XML начал экспортировать, а он угловые скобки html срезает.

Есть там кнопка "сделать чтоб работало" ?

Date: 2009-11-09 09:08 pm (UTC)
From: [identity profile] vp.livejournal.com
Что-то ты усложняешь.
Есть делиметер, допустим, запятая.
Поиск делиметера, по позициям - нарезка строки в список строк.
все, строка распаршена. Дальше по каждой подстроке уже убирание кавычек краевых, если они присутствуют, ну и байт стаффинг кавычек. Хотя на самом деле меня плющит, что вот делиметер внутри никаким байстаффингом не кодируется. То есть его просто тупо не может быть. Надо документ почитать про CSV, я не помню, как там по науке. Также и не помню как с \r\n быть по науке.

Date: 2009-11-09 09:47 pm (UTC)
From: [identity profile] arush-damage.livejournal.com
Как-то так :
quoted=0;
prev_char=0;
start_field();
while(c=getch()) {
  if (quoted) {
    if ('"' == c)) quoted = 0; 
    else field_append_char(ch);
  } else {
    if ('"' == ch) {
      if ('"' == prev_char) {
        field_append_char(ch);
        quoted = 1;
      } else if (',' == prev_char) {
        quoted = 1;
      } else {
        report_error();
        return;
      }
    } else if (',' == ch) {
      end_field();
      start_field()
    } else {
      if ('"' == prev_char) {
        report_error();
        return;
      } else {
        field_append_char(ch);
      }
    }
  }
  prev_char = ch;
}
if (quoted) {
  report_error();
  return;
}
end_field()

Date: 2009-11-09 09:58 pm (UTC)
From: [identity profile] lionet.livejournal.com
Как-то так (парсинг "строки" CSV в точном соответствии с RFC4180):

extract_line(Data) -> extract_line(Data, []).
extract_line(Data, Accum) ->
        case extract_value(Data) of
                {Value, Rest} -> extract_line(Rest, [r(Value)|Accum]);
                {Rest} -> {r(Accum), Rest};
                novalue when Accum =/= [] -> {r(Accum), []};
                novalue -> noline
        end.

extract_value([]) -> novalue;
extract_value([$\r,$\n|Rest]) -> {Rest};
extract_value([$\n|Rest]) -> {Rest};
extract_value([$"|Rest]) -> extract_quoted(Rest, []);
extract_value(Rest) -> extract_nonquoted(Rest, []).

extract_nonquoted([$\n|_]=Rest, V) -> {V, Rest};
extract_nonquoted([$\r,$\n|_]=Rest, V) -> {V, Rest};
extract_nonquoted([$,|Rest], V) -> {V, Rest};
extract_nonquoted([C|Rest], V) -> extract_nonquoted(Rest, [C|V]);
extract_nonquoted([], V) -> {V, []}.

extract_quoted([$",$,|Rest], V) -> {V, Rest};
extract_quoted([$",$\r,$\n|Rest], V) -> {V, [$\n|Rest]};
extract_quoted([$",$\n|Rest], V) -> {V, [$\n|Rest]};
extract_quoted([$",$"|Rest], V) -> extract_quoted(Rest, [$"|V]);
extract_quoted([$"], V) -> {V, []};
extract_quoted([$\r,$\n|Rest], V) -> extract_quoted(Rest, [$\n|V]);
extract_quoted([C|Rest], V) -> extract_quoted(Rest, [C|V]);
extract_quoted([], V) -> {V, []}.

Date: 2009-11-09 10:19 pm (UTC)
From: [identity profile] 184467440737095.livejournal.com
пусть A - терминалы, {'"'}, {','}, {'eol'} - подмножества A.
тогда для значения столбца и для текста целиком получаются следующие регулярные языки
V = (A\{',', 'eol', '"'})* + "(A\{'"'}+"")*"
S =((V,)*Veol)* = (V(, + eol))*

регулярный язык сводится к автомату. этот, если не ошибаюсь, к такому:
  A\{',', 'eol', '"'}   {'"'}   {'eol', ','}
1         1               2           1
2         2               3           2
3        err              2           1

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

Date: 2009-11-09 10:22 pm (UTC)
From: [identity profile] enternet.livejournal.com
На заметку, про CSV и Excel.

Разделитель не всегда запятая. Иногда - точка с запятой. Зависит от настройки системы "разделитель элементов списка".

Date: 2009-11-10 06:19 am (UTC)
From: [identity profile] zamotivator.livejournal.com
СимволТекущее состояникНовое состояниеГенерируемое событие
"StartFirst-QuoteNone
"First-QuoteDouble-QuotedNone
not "First-QuoteSingle-QuotedNone
"Single-QuoteStartSingle-Quoted String
"Double-QuotedDouble-Quoted-EndNone
not "Double-QuotedDouble-QuotedNone
"Double-Quoted-EndStartDouble-Quoted String
,StartStartField complete
\nStartStartString complete

Profile

metaclass: (Default)
metaclass

April 2017

S M T W T F S
      1
2345678
9101112 131415
16171819202122
23242526272829
30      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jul. 1st, 2025 02:51 am
Powered by Dreamwidth Studios