Как стать автором
Обновить

Комментарии 80

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


"aaa","b CRLF
bb","ccc"

а когда он еще прилетает от разных ОС...

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

Я очень редко встречалась с таким форматом. Кажется, вот сейчас был только второй раз в жизни, а первый раз был как-то связан с детским питоном - что-то там на майнкрафт через mcpi переносилось, рельеф стены что-ли. Там csv хранил просто массив из строчек "какой камень ставить на это место". Оттуда же смутно помнила, что это - comma separated values.

В сфере Data Science, Machine Learning и связанных CSV это один из основных форматов для хранения датасетов.

О, мне однажды при парсинге csv готовой либой в джаве попался символ вертикальной табуляции. До сих пор не знаю, что он делает, но теперь знаюо его существовании.

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

В эпоху телетайпов этот символ двигал лист бумаги, чтобы печатать следующую строку таблицы. Так же, как обычная табуляция длиннее, чем пробел, это могло быть большее расстояние, чем одна строка телетайпа.

Поэтому, в каком-то смысле, вертикальные табы для разделения строк в csv - это почти логично. Хотя я, если честно, мечтаю, чтобы более широко использовались Unit Separator и Record Separator для разделения полей и строк соответственно. Тогда, к примеру, и не пришлось бы экранировать запятые для csv и обычные пробелы для tsv.

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

А это другая моя печаль - не все редакторы поддерживают control pictures.

В php есть стандартные функции/объекты для чтения csv (fgetcsv, SplFileObject).Не надо было делать str_replace вообще, они читают строку и возвращают массив данных из полей.

Так может тестовое задание было как раз на свой парсинг csv, мы же не знаем

Нет-нет. Это был просто формат подачи данных. Надо было их переработать и выложить в виде json. Так что реально я для подачи и записи данных сделала интерфейсы, в предположении, что форматы могут как-нибудь измениться.

Это тоже засада, собственно. Когда задача достаточно хитрая для тебя, от просто формы подачи данных не ждёшь никаких сюрпризов. Вроде ну да, на входе - строковый файл. На выходе - файл с джейсоном. Могли бы оба файла одновременно быть в json. Ну или первый бы текстовый был. Или из базы читали бы, тоже строчку за строчкой.

В тестовом у меня был как раз примитивный csv, с очень чёткой структурой (в каждой строке 3 или 4 элемента, и это в кавычках короткие буквенно-цифровые значения с добавками вроде подчёркивания или дефиса, и разделители - одинаковые "точка с запятой"). Так что я просто свой мелкий класс написала, для построчного чтения, с парсингом по точке с запятой и вот этими всеми заменами.

От примитивности данных мысль поискать готовую либу забивается в угол.

При этом ещё вопрос о готовых либах. Я тут недавно на хабре нашла несколько статей про затраты памяти на объекты. Статьи правда древние, 2010+ годов. Вопрос, когда у вас csv примитивный, что лучше: взять SplFileObject (ещё с ним не сталкивалась, разумеется, но видела аналогичные инструменты для xml), который может вообще во всё (тот же SimpleXML парсит многоэтажные файлы с вложенными тегами и параметрами внутри тегов), или сделать свой мелкий кастомный класс.

ЗЫ посмотрела про fgetcsv - отличный выбор. Я-то использовала fgets + парсинг и эти самые замены через str_replace. И fgetcsv всё это делает сразу при чтении.

Подумала было вчера вам в ответ написать что-нибудь "какой ужас!!!!" Воздержалась, чтобы не засорять обсуждение. Но на сегодня ясно, что ваш комментарий чем-то нравится хабру (оценка под ним высокая), поэтому я отвечу. Причём отвечу серьёзно.

Я думаю, что тестовые - это тестовые для нанимателя тоже. То есть, их получение, их прохождение кандидатом - это ещё и способ оценки работодателя соискателем.

Просто пара примеров:

1) тестовое выглядит как "ну вот есть данные типа ХХХ, напишите к ним апи на ларавель". Ок, а что именно надо сделать? Какие методы что принимают и что производят в ответ с какой частью данных? И что возвращают клиенту? Пишу про это менеджеру, тот отвечает "ну это как в (название_предметной_области)". Отлично. Только я в этой области и не клиент, и не работала. Если им нужен кто-то с навыками в предметной области - не ко мне.

Это причём не минус. То есть, это не объективный минус работодателя - это лишь про его цели. Стоит ли рваться к работодателю, возможно - в целом хорошему, если он ищет явно не вас? Не стоит. Работодателей уйма.

2) тестовое захватывающе интересное. Надо из данных, выданных в csv файлах, выстроить дерево, подсоединив вон то вон так, а это вот эдак, ну и потом вот это вот всё выложить в другой файл в джейсон формате.

В тестовом уточняется: будем отслеживать организацию кода (километровый файл-лапшекод даже проверяться не будет), будем смотреть на расходование памяти и времени.

В чём-то задача была похожа на несколько уже знакомых мне по литкоду. Разница в том, однако, что на литкоде данные уже приходят готовыми, пишется только внутренний алгоритм. Я на литкоде ни разу не видела чтения csv файла:)

Короче говоря, это было счастливое время - работа с их тестовым.

Могут ли не принять его из-за кривого (вернее, не оптимизированного) чтения исходных данных? Могут, конечно. Там вообще могут много из-за чего не принять: фирма большая, известная, у них выбор богатый.

Стоило ли это тестовое выполнять в пустоту? Конечно, стоило. Ровно ради того же, зачем я хожу на литкод: во-первых, из интереса. Ну и, опять же, мне пришлось кое-что покопать по теме, так что тут Р - развитие для соискателя (а не для нанимателя).

Но. Если вот это тестовое им не понравится именно из-за неоптимизированного чтения входящих данных, они - тупые. Ну, в моём понимании они в этом случае глупые. Это как не принять в балет человека за пачку не того цвета. Тут у нас мол все в розовых, а у вас - синяя. Я о затратах на смену пачки при прочих равных.

________________________________________

ЗЫ фирма в примере 2 обещает проверку... за долго (месяц++). Ну то есть просто они, скорее всего, не успеют проверить, я уже буду трудоустроена в другую фирму. Это тоже прекрасно - для разговора с теми другими я на вопрос "а есть ли у тебя код, которым ты можешь похвастаться" ответила, в частности, этим тестовым. И на вопрос "а что ты в последнее время прочла такого" я ответила парой статей на хабре, найденных в процессе работы над этим тестовым.

Тут заодно второй вывод (кроме того, что "тестовые делать полезно в принципе, не только для некоего трудоустройства"): "стоит делать только хорошие тестовые, развивающие".

ЗЫЗЫ добавлю тут оговорку, что у меня есть работа №0 (репетиторство), она вполне кормит. Т.е., возможно, я спокойнее многих других позволить себе придирчиво перебирать нанимателей в те периоды, когда нанимателей по программированию у меня нет, или прежние надоели. Скорее всего, когда единственная работа - именно программирование, а альтернатива - быть грузчиком, максимализм снижается. Но в перспективе-то? Ясно, что в перспективе, если ты стал разрабом на фирме, где обращаются с тобой примерно как с грузчиком, ты не вырастешь.

Я оценил со своей стороны как программист. Если бы я попросил человека сделать парсер цсв и он мне начал свои костыли самописные предлагать, я б это оценил как минус что человек плохо знает язык и стандартные функции. Ваш метод хорош для тренировки навыков программирования, но в жизни когда нужно распарсить гигабайты файла это выльется минимум в увеличенное в разы время работы и кучу багов связанных с тем что вы не учли разные концы строк. Еще скажу, что в маке другие окончания строк по дефолту. И парсер может работать на линуксе, а файл для парсинга вам сформировать на винде или маке. Для наглядности можете сравнить этот код со своим:

$csv = new \SplFileObject($path);
$csv->setFlags(\SplFileObject::READ_CSV | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY | \SplFileObject::DROP_NEW_LINE);
$csv->setCsvControl(',', '"', '\\');
foreach ($csv as $row) {
    // $row - массив данных из строки
}

думаю с fgetcsv будет на пару строк больше.

Ваш код мне напомнил как новенький коллега вместо обращения к $_GET['var'] парcил её регулярками из $_SERVER['REQUEST_URI'].

Я не настаиваю, но всё таки.

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

...

Ваш код мне напомнил как новенький коллега вместо обращения к $_GET['var'] парcил её регулярками из $_SERVER['REQUEST_URI'].

Тестовое было вообще не про парсинг файла, а про переработку данных в дерево с некоторыми характеристиками. Т.е., пришли бы данные строками в .txt файле, было бы то же самое. Или из БД читались бы. Так мне казалось до этого обсуждения.

Если бы я использовала str_getcsv вместо fgets и парсинга, это был сократило... парсинг. В одном методе одного класса. Поскольку дальше мне всё равно надо было массив превращать в ассоциативный, а этого встроенный парсер для csv не умеет, вроде бы. А дальше в главном рабочем классе учёт порядка следования элементов в строке от str_getcsv вместо использования подготовленного ассоциативного массива загромождал бы (для восприятия) и так не то, чтобы сильно прозрачную деятельность.

Вы сравниваете отсутствие узнавания на глаз отдельного частного формата передачи данных как некоего специфического формата со своими фишечками (т.е., до этой статьи я просто не видела разницы между csv и txt) с абсолютно ключевым для РНР-разработчика знанием по работе с глобальными переменными для приёма HTTP запросов.

По-моему, тут никакое сравнение невозможно: как уже написали тут в комментариях, csv используется для датасетов в машинном обучении, т.е., не самая частая для РНР область применения, там вроде чаще питон, ну и для выгрузки данных по бухгалтерии - тоже не в каждом первом проекте такое требуется.

Тестовое было вообще не про парсинг файла, а про переработку данных в дерево с некоторыми характеристиками. 

Вот именно! Если не указано, что нужно реализовать парсинг - значит нужно пользоваться встроенными возможностями и решать именно бизнес задачу.

> str_getcsv вместо использования подготовленного ассоциативного массива загромождал бы (для восприятия) и так не то, чтобы сильно прозрачную деятельность.
Разве одна дополнительная строка загромождает восприятие? Как по мне, восприятие загромождает именно велосипеды, которые при таком в подходе будут в проекте десятками-сотнями похожего кода.

$csv = new \SplFileObject($path);
$csv->setFlags(\SplFileObject::READ_CSV | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY | \SplFileObject::DROP_NEW_LINE);
$csv->setCsvControl(',', '"', '\\');
foreach ($csv as [$name, $surname, $id]) {
$associative = compact('name', 'surname', 'id');
}


> Вы сравниваете отсутствие узнавания на глаз отдельного частного формата передачи данных как некоего специфического формата со своими фишечками (т.е., до этой статьи я просто не видела разницы между csv и txt) с абсолютно ключевым для РНР-разработчика знанием по работе с глобальными переменными для приёма HTTP запросов.

В реальной работе мне с csv еще и больше приходиться работать, чем с глобальными переменными для приёма HTTP запросов, которые зачастую обрабатываются фреймворком, и которые я не использовал уже лет 5. А csv - при любой выгрузке отчета/данных из системы. Просто потому, что иногда данные нужно посмотреть человеку, и желательно в удобном ему табличном редакторе (Excel, LibreOffice etc.)

Мне кажется, у нас с вами скандал, но это ж бессмысленно. Мы просто не работаем вместе. И вряд ли будем когда-то.

Разве одна дополнительная строка загромождает восприятие?

По-моему, да. Вставьте в ваш код строчку echo "Бу!"; где-нибудь в середине. Короткая простая строка, но отвлекает, поскольку прямо к месту, где она выскочила, как-то никак она вообще не относится.

И ровно то же самое будет при обращении к элементам массива под номерами вместо экстракта.

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

Для меня это выглядит как "не вполне РНР-проект". Т.е., вот есть проект, но знатная часть его функционала - это не вполне РНР. Ну то есть для обращения к БД нужно что-то на SQL или кто там ещё внутри базы, но надо ещё обеспечить читаемость через эксель, поэтому как-то ещё обрабатываем выгрузку базы.

Ну, я делала как-то раз выгрузку из БД в .док . Это было ужасно, поскольку выяснилось, что у ребят офис ещё допотопный какой-то, ставить себе либерофис они не хотят, обновлять офис не будут, ну и вот не попробую ли я ещё одну либу, чтобы и валенком открывалось. Послала нафиг, вместе с деньгами по теме. Ибо нервы дороже.

А что такого сложного в выгрузке из БД в doc? Даже в допотопный офис? Com даже 20 летней давности офисы поддерживали. И зачем обновление от клиентов требовать? Или com - слишком сложно?

Там ключевое слово "ещё одну (либу)". Мой заказчик реально был посредником, и что там устаревшая версия офиса сообщил только после того, как я уже успела наваять этот пункт ТЗ на либе, чей результат работы старым офисом не открывался.

Но там и до этого были сюрпризы. Увеличившие итоговые затраты времени просто в разы. Ну то есть я подписывалась на нечто с одной ценой часа, а по итогу вышло (и без переделывания 4-го пункта) ниже раз в пять.

И я так подумала, что в пять или в десять раз меньше - один чёрт. Поскольку половину он мне уже выплатил, а я уже выполнила 3 пункта из 4 (да и 4-ый выполнила, просто он не предупредил же, что там специальные требования по открыванию валенком), я и сочла, что мораль на моей стороне.

(это совсем давно было, один из первых моих заказов не через друзей. Сейчас бы я там не ввязывалась, просто на код посмотрев. Такой лапшекод - самописная CRM, я потом это вот видела у студента на курсах "первая ступень РНР").

Ок. Если не сложно сообщите потом ответ от компании по поводу тестового, если они его прокомментируют.

Я, к сожалению, скорее всего просто забуду. Но так-то запросто.

Сообщаю результат:)

На способ чтения csv вообще не обратили внимания. Больше того, порадовались, что я предусмотрела там интерфейсы (в основном алгоритме интерфейсы на входе и на выходе, соответственно, подавать данные можно из разных источников, и записывать в разные).

Но. Они сочли, что я переусложнила код. Потому что я ещё вынесла две хеш-таблицы в отдельный класс. Получился такой дата-класс вообще без методов. Хуже того, с публичными переменными, в которых эти хеши сидят.

А в классе с методами пара отдельных статичных методов. Т.е., отдельно класс-билдер, который читает данные и так и эдак сначала записывает всё это дело в хеш-таблицы, а потом вторым проходом уже доделывает массив на выход. Ну и потом это передаётся интерфейсу обработки итогового массива.

За счёт этой (странной и переусложнённой, как они выразились - а мне просто это вот помогало не окончательно сломать мозги в момент разработки: вот дата-класс, вот билдер, всё ясно) архитектуры удалось добиться примерно мгновенной отработки с файлом на почти на 400 строчек, из которых надо было построить дерево сразу по двум видам связей. Там просто получился итоговый алгоритм порядка O(n) вместо очень естественного, но O(от чего-то большего).

В принципе, они изначально задавали ограничение "не более 20 000 тысяч строк во входящем файле", так что и O(n^2) там вроде не бог весть что. Современный компьютер вполне с такими справляется... Но это скушно.

Поскольку там ещё было в задании обещание оценивать скорость работы и пожирание памяти, я это всё как начала измерять, да как полезла в статьи десятых годов про вот это вот всё (раньше не знала ни про какие zval), ну я ж не могла уже делать стандартно.

В общем, прислали обратную связь, что я там перемудрила, а я в ответ переспросила, а расскажите, с каким О большое работает ваш алгоритм, без хешей. Молчат покамест:)

Спасибо, что не забыли ))

Тестовое было вообще не про парсинг файла, а про переработку данных в дерево с некоторыми характеристиками.

Ну так тем более. Если это не суть задания, зачем извращаться, если есть искоробочное решение, которое решает задачу. Тогда можно сконцентрироваться на основном задании.

P.S. Если не секрет, это на какую должно задание? Junior?

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

А главное -

 зачем извращаться, если есть искоробочное решение

если ты знаешь о нём. И считываешь детали ТЗ как именно что кусок с готовым решением. Распознавание темы как вот такого куска - это именно опыт из целевой области. У меня его не было.

Естественно, если впредь мне понадобится прочитать цсв, я это сделаю через стр_гетцсв. Тут вообще не вопрос. Просто всё в том же входящем классе на интерфейсе. И всё равно переделаю в ассоциативный массив, ну, мне так удобнее. Зачем делать что-нибудь неудобно, если можно удобно?

P.S. Если не секрет, это на какую должно задание? Junior?

Не секрет, обычная миддловская вакансия. Я в основном откликаюсь именно на миддловские. Хотя на джуновские тоже, бывает, когда там какие-то плюшки вроде внутреннего обучения.

Но вот недавно с этим как раз обломалась здорово. Был трезвый оффер (готовый) за 145 тыр миддлом в команду из десяти человек, и параллельно опция со стажировкой месяц и дальше пару месяцев на бегиннере за 30-35 тыр в месяц, но с новыми для меня технологиями, и сильно больше компания (в компаниях таких масштабов я ещё не работала). Я выбрала стажировку. Отчасти не прогадала - мне правда очень хотелось попробовать. Ну, и попробовала - битрикс, ага. Я же не знала, что выбор "ларавель или битрикс" - это "рояль или трактор". Я ожидала, что будет "похоже, но чуть другое" (на ларавель я пишу пару лет, и на вью). Сбежала, короче. Вернее, ближе к очередной к/р поняла, что нет вообще никакой мотивации для подготовки. И не сдала, разумеется. Т.е., я буквально на днях провалила контрольную на стажировке, где даже следующий грейд - ещё только бегиннер.

Распознавание темы как вот такого куска - это именно опыт из целевой области. У меня его не было.

Если у вас нет опытка, какой же это мидл? Судя по тому что вы пишете, у вас, увы, просто нет IT-кругозора и вы ждете, что вам работодатель/тимлид/кто-то еще расскажет как оно у них сделано и как вам с этим жить. Это, увы, не мидловский уровень в серьезной компании. От мидла обычно ждут способности на каком-то уровне разобраться самому и задать вопросы по непонятному/специфичному, а если он ждет что ему с порога все расскажут, то вряд ли это мидл.

И всё равно переделаю в ассоциативный массив, ну, мне так удобнее. Зачем делать что-нибудь неудобно, если можно удобно?

Потому что не все правльно, что удобно. Если у вас 100Гб данных, их не стоит загружать в память, даже если их потом так удобнее обрабатывать

Я же не знала, что выбор "ларавель или битрикс" - это "рояль или трактор". 

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

И не сдала, разумеется. Т.е., я буквально на днях провалила контрольную на стажировке, где даже следующий грейд - ещё только бегиннер.

Ну а что вы хотели, если вам нужно переучиваться с одного фреймворка на другой, при том что у вас вроде как два года опыта. Вы же не с нуля учите фремворк, вы уже понимаете как примерно оно должно быть устроего и как работает. Вам надо только адаптировать свои знания от одного фреймворка к другому. Это как переход с одного язка на другой. Вы не учите его с нуля, вы выясняете, как сделать уже привычное и знакомое вам но уже на новом языке.

Вот вы спросили выше, какая была вакансия, где было тестовое. Не джуниор ли.

Я вам ответила, по факту, вакансия там миддловская. Вы отвечаете, что ну какой я миддл.

Зачем? Зачем вы тратите время на эту чушь, серьёзно. Каждый из нас имеет в разных вещах разный уровень. Где-то я миддл, где-то джун, де факто (проверено, так сказать, на полях).

Где-то не так давно был человек-архитектор, деньги имевший за эту "архитектуру", ну так я там нашла аргументы убедить руководство, что это вот всё на проде работать не будет. Жалко, протормозила полгода - мне же казалось, кто я такая, критиковать архитектора. Который ведь сам был вряд ли даже миддлом - скорее, был синьором.

Кажется, многие зрелые разработчики просто тупо от скуки мучаются. И злятся на начинающих или середнячков, которым в диковинку вот такое вот, как в статейке тут. Собственно, самое главное ощущение у меня было - "ну надо же!! Ну нифига себе, фишка какая выскочила!" То самое восхищение глубиной темы, ради которой стоит этим всем заниматься.

У вас это в прошлом. И остаётся разве что обсуждать "Невыжившие в IT" персоналии (https://habr.com/ru/post/686566/, стопятисотый сверхсодержательный опус от Белокаменцева). Или смотреть боевики. Ну или новости. Если сегодня, то новости и боевики похожи.

Я вам ответила, по факту, вакансия там миддловская. Вы отвечаете, что ну какой я миддл.

Мидл и джун это не то на какие вакансии вы подаетесь и не то как называется ваша должность. Это про то, как вы работете. Если вы ждете, что вам расскажу все про задачу заранее -- вы джун, потому что мидл сначала попробует сам понять что к чему, а если что задаст уточняющие вопросы. Именно уточняющие, а не чтобы ему рассказали, что и как. Если вы увидев файл с непонятным расширением открываете его блокнотом и видя что он текстовый просто кидаетесь его вручную парсить -- вы джун. Если вы останавливаетесь и задумываетесь, что это за фигня и пытаетесь выяснить, что это за расширение -- вы мидл. Можно было бы продолжать, но суть вы, я думаю, поняли.

И при этом можно бесконечно размахивать прочитанными или просто купленными книгами, трудовой с названием должности и всем остальным, но это не означает, что вы работаете как мидл. В разных компаниях разный подход и разные требования на должность. Где-то вы будете синьором, а где-то и максимум мидлом, даже если задачи будете решать по сути одинаковые.

Заметьте, я ни слова не говорю о конкретных технологиях, я говорю лишь о подходе к работе и об общем кругозоре. Это не значит что надо знать все и про все, но "что-то слышать" про разное весьма полезно, чтобы не делать глупостей.

Ну а что вы хотели, если вам нужно переучиваться с одного фреймворка на другой, при том что у вас вроде как два года опыта. Вы же не с нуля учите фремворк, вы уже понимаете как примерно оно должно быть устроего и как работает.

Вы комментируете мою цитату так, как будто я спорила с этим. Но я же ну вообще не об этом. Вы спорите с тезисом, у меня отсутствующим. Мой тезис был только в том, что я обломалась в моих ожиданиях от продукта. Я думала, буду учить фреймворк - а вышло, что битрикс. Битрикс фреймворком можно назвать только с большой натяжкой. Как минимум, в рамках этого вот обучалова. Может быть, где-то есть курсы по битриксу, где битрикс выглядит более по-фреймворочному и менее-цмсочно. И менее по-экселевски. Собственно, я там успела пару статей написать, и в одной в комментариях дали ссылку на "Исповедь битрикс хейтера", год там где-то так 2016. Статья замечательная. Раньше бы прочитала, вообще не совалась бы в битрикс (про "дух копи пасты" он абсолютно прав, на 205%, и это задалбывает)

Вопрос, когда у вас csv примитивный, что лучше: взять SplFileObject (ещё с ним не сталкивалась, разумеется, но видела аналогичные инструменты для xml), который может вообще во всё (тот же SimpleXML парсит многоэтажные файлы с вложенными тегами и параметрами внутри тегов), или сделать свой мелкий кастомный класс.

Вы не забывайте, что в реальном мире в проекте могут быть десятки простых случаев csv файлов. На каждый из них по классу-парсеру? И к каждому парсеру еще и тесты? А если в формат добавиться данные, что часто бывает в реальных проектах?
Плюс ко всему классы Spl работают довольно быстро, так что если у вас не миллионы строк нужно парсить, то лучше использовать встроенные возможности языка.
Переизобретать велосипеды не выгодно, как только речь заходит о коммерческой разработке.

Скорее, в реальном проекте одни и те же данные могут менять формат. Но это не точно.

Т.е., если в предметной области данные - это "название товара, тип товара, товар-родитель, возможная доп.связь" (это вот было конкретно в тестовом), я ожидала бы скорее смены формата подачи этого набора данных, чем дополнительных csv файлов.

Но я просто вообще ни разу ещё на рабочем проекте с csv не встречалась. Вернее, вообще не встречалась - это же тестовое. Где-то уже написала тут, что был это второй раз в жизни, а первый был в детском питоне, для передачи строчек, которые не передали в массиве, поскольку массивы на тот момент не проходили ещё.

В общем, реально, вопрос, а где у вас такое встречается - в вашей жизни разработчика. Зачем вам такой формат, csv?

В общем, реально, вопрос, а где у вас такое встречается - в вашей жизни разработчика. Зачем вам такой формат, csv?

Зачастую это самый простой формат для экспорта-импорта в технически не связанных проектах. Допустим, выгрузка всех платежей из системы для бухгалтерии, чтобы провести сверку. Самый простой вариант - испольовать csv, ведь его и легко генерировать, и смотреть (банально в текстовом редакторе или в Excel etc.).

Т.е., если в предметной области данные - это "название товара, тип товара, товар-родитель, возможная доп.связь"

А потом туда еще код товара добавиться, цена товара, дата создания и еще куча всего, чего не придумали изначально (ведь раньше при импорте/экспорте данных они были не нужны).

А потом туда еще код товара добавиться, цена товара, дата создания и еще куча всего, чего не придумали изначально (ведь раньше при импорте/экспорте данных они были не нужны).

Вопрос, зачем на вход к некоему функционалу, требующему уже заданного набора данных, внезапно подавать добавочные данные. Это же нарушает открытость-закрытость уже для основного функционала - мы тогда полезем в основной рабочий класс менять код.

Куда естественнее поставить на входе именно интерфейс, который данные чистит. Интерфейс примитивный, он просто умеет в next(), а что там внутри у него - не требует никаких тестов. Ну потому что там ровно один метод торчит наружу, а в имплементирующем классе ещё пара коротких методов.

Т.е., понятно, что источник данных может выгрузить что угодно и как угодно. Но для реципиента это зачем, вот вопрос.

И даже больше. Если реально, важным для основного функционала образом, будут добавлены данные, надо тем более иметь интерфейс снаружи. Ну потому что... а вдруг эти данные станут идти из двух файлов сразу? Всё ещё csv, но двух?

Ну потому что... а вдруг эти данные станут идти из двух файлов сразу? Всё ещё csv, но двух?

Обернуть fgetcsv  в итератор, потом использовать \AppendIterator?

А если надо читать не строка за строкой, а что-нибудь вроде "если в первом файле встретили ХХХ, бросаем его и читаем дальше полностью второй файл, а если ХХХ не встретился, просто первый дочитываем"? И так далее.

Ну то есть драма с исходниками - это отдельная драма же.

И если с конкретным отдельным функционалом она не связана, вы сделаете интерфейс на входе, делающий "строку за строкой" из поступающей каши. Которую для чего-то меняют по форме, добавляют туда то да сё...

А потом туда еще код товара добавиться, цена товара, дата создания и еще куча всего, чего не придумали изначально (ведь раньше при импорте/экспорте данных они были не нужны).

Собственно, импорт и экспорт делается для конкретных целей. Можно добавить всё что угодно - в новую выгрузку. Зачем же старую отменять?

Ну или я вас неправильно понимаю. Я слышу эту вот цитату так:

  • сейчас вы пользуетесь некоей выгрузкой от чужого проекта, с такими-то характеристиками (у этой выгрузки)

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

И если так, то, как минимум, интерфейс обработки входящий данных, как максимум - ищем другую фирму, с версиями методов. Они там могут добавочно выгружать, что хотят, но если мы тут подписались на метод Х, отдающий хх, очень хотелось бы всё в том же виде иметь через год оттуда же.

А если надо читать не строка за строкой, а что-нибудь вроде "если в первом файле встретили ХХХ, бросаем его и читаем дальше полностью второй файл, а если ХХХ не встретился, просто первый дочитываем"? И так далее.

Так а что тут мешает читать строка за строкой? Читаем каждую строку и анализируем.

Ничего не мешает. Я лишь о том, что дело вовсе не в "подключите готовую либу", а в том, что есть отдельная процедура - выдача данных на вход.

И в некотором проекте, где с данными дальше надо что-нибудь сделать (а не, допустим, просто вывести на консоль три первых строчки) стоит всё это вынести в интерфейс на входе. Дальше имплементирующий класс для этого интерфейса можно оптимизировать, не оптимизировать, и оптимизировать можно по-разному, но это ж уже детали.

Тут почему-то выше ругаются на интерфейс:

Вы не забывайте, что в реальном мире в проекте могут быть десятки простых случаев csv файлов. На каждый из них по классу-парсеру? И к каждому парсеру еще и тесты? А если в формат добавиться данные, что часто бывает в реальных проектах?

(@Layan)

В мире бывают десятки простых случаев файлов с любыми расширениями. Ну и при чём тут я? И для чего обвешивать тестами парсер на пару методов по три-пять строчек в каждом?

Ну я так-то тоже против своего парсера на каждый csv и за использование библиотечного парсера, вместо написания своего. И это никак не противоречит тому что у вас может быть специальный интерфейс, который отдает какие-то данные и эти данные всегда одни и те же.

Отсюда вопрос, как получается, что обсуждение близко напоминает скандал. Хотя под скандалом нет никакого конфликта.

 И для чего обвешивать тестами парсер на пару методов по три-пять строчек в каждом?

Для того, чтобы на ревью понимать, что код работает без запуска всего проекта.
И при изменениях кода можно было также провести регресс тесты. Посмотрите код хороших Open Source проектов, хотя бы guzzlehttp.

Я не понимаю, почему вы относитесь к критике как к какому-то скандалу. Я вам помочь хочу, ведь я вам просто рассказываю о том, как происходит работа в действительно серьезных проектах, а не там, где в коде контроллер на 2-3 метода и пару тысяч строк кода, зато два архитектора с десятилетним опытом и табличками-блоксхемами.

Я хорошо отношусь к - конструктивной - критике. Проблема в том, что вы критикуете по прочтении маленькой краткой статьи.

Вы, например, не спрашивали, как я вообще отношусь к тестированию. Я бы ответила, что я где-то год была TDD-фанаткой. Настолько, что даже где-то там на одном собеседовании в рубрике "а давайте покодим с показом экрана" стала кодить именно по ТДД. Это было ужасно - ну просто собеседование же и так вполне нервное дело.

Я не "вообще не считаю тесты полезными", а как-то начала Хорикова (не закончила) и ещё пару книжек уже прям по теме. Ну и задумалась, что вообще стоит тестировать. Стоит ли вам тестировать классы на три с половиной метода, в каждом по три-пять строчек. Может ли там хоть что-то когда-то сломаться.

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

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

Т.е., вы тоже ж поймите. Я это вижу, как вы стараетесь. Просто проблема в том, что критика критике рознь. И если вы не очень знаете человека, критика будет скорее всего про другого кого-то. Именно про кого-то, кто вам как-то знаком уже. Кто-то, кто с вами работал. И был раздолбаем, допустим.

Для адресата высказываний (ну вот как я сейчас) это уже выходит не просто не критика, а что-то вроде "ответишь за Петю Васечкина".

В общем, реально, вопрос, а где у вас такое встречается - в вашей жизни разработчика. Зачем вам такой формат, csv?

А какие еще есть удобные альтернативы для экспорта табличных данных, с возможностью удобного просмотра/редактирования на ПК? Только csv и вспоминается

Ок, а где нужен экспорт табличных данных?

Тут уже упомянули датасеты в машинном обучении (но это же чаще питон, а не РНР?) и бухгалтерские выгрузки (но это же тоже сравнительно редкое дело для РНР-проектов). Опять же, бухгалтерская выгрузка не для просмотра-редактирования человеком, а именно для использования в скрипте? А почему не json? Ну в смысле тот элемент, который даёт нашу выгрузку, он же может и в том формате, и в этом из БД выгрузить?

Хотя, пожалуй, тут для больших объёмов лучше файлы с построчным чтением. Json как-то сразу придётся в память складывать. Но.

В моём этом тестовом итоговый файл всё равно требовалось писать из массива в json. Т.е., память всё равно требовалась в больших объёмах.

(на этом мысль останавливается)

Ок, а где нужен экспорт табличных данных?

Выгрузка отчетов в админках (в том числе для клиентов/контрагентов), выгрузка/загрузка товаров в интернет-магазинах, импорт большого количества информации.

А почему не json?

1) Попробуйте клиенту объяснить что отчёты он будет получать JSON-массивом )

2) Попробуйте в редакторе распарсить и отформатировать большой JSON-объект... даже при объемах порядка нескольких мегабайт это может стать нетривиальной задачей.

3) Поиск/сортировка информации. Попробуйте отсортировать файл с JSON по какому-либо полю, например (без написания скрипта)

бухгалтерские выгрузки (но это же тоже сравнительно редкое дело для РНР-проектов)

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

Ну и много чего ещё можно придумать. У каждого формата своё назначение, csv лучше приспособлен для табличных данных с фиксированной структурой, JSON же лучше подходит для древовидных данных, в том числе с разным набором полей

А почему не json? 

JSON имеет целую кучу недостатков, особенно когда идет речь про экспорт/импорт данных, так как его нужно парсить весь сразу, csv - позволяет построчное чтение. Представьте себе JSON файл на несколько сотен тысяч элементов. Сколько там лишних данных будет? Да и не каждому серверу удастся его спарсить вообще, нужно много ОЗУ.

Хотя, пожалуй, тут для больших объёмов лучше файлы с построчным чтением. Json как-то сразу придётся в память складывать. Но.

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

От примитивности данных мысль поискать готовую либу забивается в угол.

Для тренировки — да, но в боевом коде лучше использовать готовую либу, когда она есть и стандарт сильно распространённый. :)

А это вот, кмк, начальство должно сказать. Я серьёзно. Я глубоко офигела от половины каментов тут: как это так, чел не знает про csv. Какой ужас!

Ну да, не знает. У вас csv - обратите на это моё внимание, пожалуйста. Иначе я просто не распознаю этот хвостик как значимый. Там внутри строчки? Ок, значит он .txt - и точка:)

Ну да, не знает. У вас csv - обратите на это моё внимание, пожалуйста. Иначе я просто не распознаю этот хвостик как значимый. Там внутри строчки? Ок, значит он .txt - и точка:)

csv - это и есть текстовый файл. Название файла на содержание влияет примерно никак, очень часто таблицы csv сохраняют в txt файлы. Просто разбираться в базовых форматах файла стоит. И работодатели на это смотрят, так как не имеет смысла брать разработчика и объяснять ему на пальцах любую мелочь. Как минимум всегда есть Google и можно посмотреть, как в современном мире работают с тем, или иным форматом. Сообщество PHP довольно большое, и как правило, уже все более-менее описано.

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

Но есть куда больше фишек, которые мне неизвестны - даже названия. И так оно с каждым, кто не начинал ещё в ХХ веке на первых компах.

Да, для снижения пиетета добавлю. Я на одном проекте работала, на ларавель. Только собственник был сишным прогером из девяностых-двухтысячных. Он понимал под архитектурой блок-схему. Кроме того, на проекте был архитектор, которого этот собственник нанял. Всё это вылилось в код на, повторюсь, ларавель, где а) вообще ничего не использовалось, кроме моделей с контроллерами (а, ну и роутер), и б) в контроллерах было по два-три метода в целом, но в каждом по тысяче строчек. Реальная цифра оттуда - 3000+ строчек кода в контроллере с четырьмя методами, причём в одном методе из четырёх две строчки (в одной проверяем условие, и если да, то вызываем другой метод того же контроллера).

Возвращаясь к теме, чтобы что-то погуглить, надо же знать, что именно надо погуглить. Т.е., онбординг - это, в первую очередь, выдача простыни-словаря, какие именно фишки у вас используются. Какие термины. Мой идеальный работодатель делает именно так.

Но есть куда больше фишек, которые мне неизвестны - даже названия.

Поэтому достаточно перед выполнением задачи посмотреть на php.net. Поверьте, я сам многого не знаю, например при работе очень редко сталкиваюсь с XML. Но когда нужно - иду, смотрю как с этим форматом работать и делаю, без писания велосипедов. Ведь в каждом формате есть куча подводных камней, а это значит что нужно использовать готовые и протестированные решения.

Вопрос не в том, куда посмотреть. Вопрос, что именно посмотреть. Ну и когда что-то делать, пока вы всё досконально посмотрите. Если для меня цсв-файл - просто текстовый файл, и я уже где-то читала построчно и парсила просто текстовые, я же просто не вижу засады. Больше того, засады может и руководство не видеть.

Забавно, по ходу, что ни в одном комментарии нет уточнения про input как значение core.autocrlf - а он есть. Он причём в той же главке Чакона и Штрауба. Но оно как бы про другое - это чтобы гит что-нибудь там менял только в моменты Ч.

Т.е., тот факт, что какая-то там работа с гитом происходит, ну вообще прошёл незамеченным, все к цсв прицепились.

Я ж написала не про цсв, а вообще-то про гит. Гит, с настраиваемой заменой там в окончаниях. С ним куда чаще работает кто угодно, чем с цсв файлами.

Твоя проблема в том что ты csv парсить не умеешь, а не git. Git в данном случае сыграл свою роль только потому что ты файл с данными в него запихала. Если данные будут извне с другими окончаниями строк, то у тебя код опять сломается.

Гм. Отдельно переспрошу, когда мы, пупсик, на ты перешли.

Что в этом плохого? Переходите тоже на ты. С каких пор это стало считаться оскорблением?

А смысл?

Встроенные функции типа fgetcsv хорошо, да. В данном случае если файлы могут быть сильно большими еще лучше использовать генератор и str_getcsv

Зачем? Мы и так файл построчно читаем.

А генераторы же про другое. Больше про управление, чья инициатива. Я там сначала как раз с генератором пробовала.

Т.е., именно так бы я бы и делала, как@NickyX3написал. Если делала бы сейчас с нуля. Только без генератора - в данном случае в итоге лучше читался метод next(), который дёргается основным алгоритмом по мере надобности.

(вообще не пойму, почему минусуют такие каменты. Поставила плюсик, но всё таки. Я не уверена, что это прямо вот плюсик. Но только минус откуда под вот такими вот двумя строчками?.. Никак не привыкну).

Ну генератор имеет смысл если хочешь вынести чтение файла в отдельный метод, тогда получится что-то типа

function getCsvData($file) {
    $fp = fopen($file, 'r');
    while (($row = fgetcsv($fp, 0, ',')) !== false) {
        yield $row;
    }
    fclose($fp);
}
foreach (getCsvData('file1.csv') as $row) {
    // работаетшь с $row
}
foreach (getCsvData('file2.csv') as $row) {
    // работаетшь с $row
}

Ну генератор имеет смысл если хочешь вынести чтение файла в отдельный метод

Именно. Экономит память, лучше даже в генераторе просто читать строчки, а парсить (str_getcsv) уже снаружи. Более универсально, кмк

В одном из проектов у нас подобным образом импортируются оплаты-платежки в самописную систему для менеджеров. Бухгалтерия в другом городе, присылают CSV.

Все зависит от размера файла и что вы с распарсенными данными будете делать, сразу обрабатывать каждую строчку, или какие-то блоки строчек, или все целиков в память потащите. Генератор однозначно экономнее чем какой-нить вызов file/file_get_content/SplFileInfo->getContent. Но по сравнению с построчным чтением через fread не так чтоб экономит, но разделяет код обработки от собствено чтения

Я гружу блоки по сотне записей (чтоб не каждую в базу пихать, а bulk-запросом обновлять в базе сразу пачкой)

Спасибо, интересно. В принципе я так и предполагал и в последнее время csv по строкам читаю без использования встроенных функций и обрабатываю. Причина - вечно в исходном файле или переносы строк, с которыми ТС столкнулась, или, что чаще, всякие служебные символы, табы и т.д. вылезают. Ну и конечно абсолютно согласен что одним запросом в бд вставлять надо

О да, ещё у UTF-8 есть маркер, который не раз и не два порождал ошибку.

Самая эпическая ошибка с текстом у меня была много лет назад , что в дельфи написал программу , которая принимала посылку с COM порта начиная с символов "ъъ" или что-то в это роде. Ну я и забил эти символы в стандартный компонент. Дальше программа , как оказалась уехала на немецкий компьютер и долго-долго пришлось разбираться почему это она не работает, в то время как у меня и во всех компьютерах на работе всё в порядке.

Маркер называется BOM. Во времена, когда экраны были ещё синими и текстовыми, а UTF-8 был в диковинку, успел приобрести привычку смотреть на файлы в бинарном виде. Так и узнал про BOM. Удивления уже не вызывало.

Я конечно извиняюсь, но

"В разных ОС по-разному устроены концы строчек в файлах. Где-то будет просто LF, где-то - CRLF" - эта информация действительно такая новость, что достойна целой статьи на хабре в 2022?

Я осмелюсь вам предложить вдобавок почитать про кодировки, а особенно чем UTF отличается от ascii, и как с этим могут работать/не работать функции и программы.

В том-то и дело, что новичкам трудно найти какой-то список минимальных знаний, необходимых для успешного программиста. Если в начале 90-х информацию сложно было найти из-за ее недоступности, то сейчас полезную информацию почти невозможно найти из-за обилия цифрового мусора.

В моей работе с новичками нередко случаются подобные же случаи: "как, ты этого не знаешь!?" Правда, приходится сдерживаться и не спрашивать напрямую, иначе это звучит как агрессивное обвинение в глупости. А ведь ребята - очень даже умные, просто неопытные. И получить 10 лет опыта занимает некоторое время.

Мне кажется, дело даже не столько в опыте в плане лет, сколько в том, что сегодняшний опыт в годах - это работа с высокоуровневыми инструментами чаще всего. Сегодняшний человек с опытом в 10 лет начал работать в 1012. Если опыт сегодня 5 лет, это начало в 2017 - у вас уже есть композер, докер... на фоне докера сам этот факт, что там какие-то окончания (?) строчек (?) в каких-то файлах могут в гите (???) привести к поломке рабочего кода... Ну, это удар по мозгам, я скажу вам.

И что совсем удивительно - в эти кроличьи норы обычно не рекомендуют соваться, открытым текстом. Я как-то раз училась там в одной фирме, на курсе бросили фразу что вот мол, сессия живёт тут-то, эта вот переменная - место жительства сессии, можно переназначить... Я пошла в гугл, в ютуб, час этим всем занималась. И у себя в компьютере первый раз в жизни открыла файл сессии напрямую. Посмотрела в него. Там было именно то, что мне обещали. Ура.

Ну а дальше я для чего-то об этом сказала руководителю. Он парадоксально ответил "ты же не можешь всё знать". И дальше больше: надо учить то, что надо учить. Что запланировано по списку. И это вот в списки не входит.

Т.е., на курсах такое может звучать, но как бы таким внешним фоном. Когда кто-то опытный как-то прорвался в преподы (а не тарабанит по тексту), ну и тогда он вынужденно говорит, не может не говорить о таких вещах. Не вполне по программе.

Короче говоря, сегодня узнать что-то о низкоуровневых вещах возможно только при целенаправленном поиске да с препятствиями. И первое время сам для себя выглядишь идиотом - зачем ты готовишь на сковородке, когда уже есть мультиварка, такое. Зачем ты режешь руками, когда есть чоппер.

Суть в том, что очень много новых инструментов и технологий постоянно появляется. Но именно знание низкоуровневых вещей позволяет тебе более-менее свободно перескакивать из одной технологии в другую, вдобавок примерно понимать что на самом деле происходит под капотом.

Например можно считать докер или containerd магией и просто выбирать какая магия тебе нравится больше. А можно немного разобраться с cgroups как оно создается, и тогда, читая технические различия между контейнерными технологиями ты будешь понимать почему что-то работает быстрее/медленнее но какой части комфорта ты при этом лишаешься и за счет чего.
Интуиция она по идее не может работать на высокоуровневых знаниях...

вот кстати статью про отличие UTF от ASCII в виде "как я делала для своего минипроекта форму логина и пароля на латинице как вдруг пришли арабы и решили тоже логиниться" - я бы с интересом и сам бы почитал

(потому что у моего друга такое было. но у меня нет литературного таланта написать об этом интересно)

стёрла репозиторий в локальном компьютере

стёрла репозиторий

Вот будет, пардон, впредь наука. Код без сторонних библиотек занимает смехотворно мало места, зачем его удалять?

А зачем его сохранять?

Ну и, опять же, это же тоже наука: вы что-то пушите на гитхаб, дальше ваш проверяющий пуллит к себе на проверку... Это ж к нему приедет с той же ошибкой, что к вам при пулле в пустую папку.

ЗЫ моя основная цель - не перепиливать ещё пять тыщ раз - не достигается удалением на локалке в 100% (можно же спуллить обратно), но в целом работает вполне прилично. Можно хотя бы сначала позавтракать.

Я когда-то в Cygwin копировал файл, и тоже все поломалось. После копирования бинарного файла он чуть-чуть увеличился в размерах... Приблизительно на 1/256. И тут я начал что-то подозревать...

Странно, что еще никто не написал. От autocrlf работа PHP-кода не меняется. Скорее всего вы добавили в репозиторий и сам CSV-файл. При git pull в нем поменялись концы строк, поэтому код str_replace("\n", ...) перестал его правильно обрабатывать.


Поэтому код str_replace(PHP_EOL, ...) это неправильное исправление. Если проверяющий сделает pull на Windows, скинет его на флэшку и запустит на Linux, он тоже работать не будет. Правильно это убирать в конце строки оба символа "\r" и "\n", при этом в середине убирать не надо.


И это как раз именно та причина, по которой надо использовать сторонние библиотеки, а не писать свою обработку. Их проверили много людей в разных ситуациях, и там предусмотрены случаи, о которых вы не знаете и не подумали. Неважно, знаком вам формат или нет, ищете в интернете "php csv parser" или "laravel csv parser" и выбираете то, что удобнее использовать.

Правильно это убирать в конце строки оба символа "\r" и "\n", при этом в середине убирать не надо.

Это решается одной строчкой, которая постоянно применялась еще в Perl 20 лет назад - trim() или rtrim(). Заменять переводы строк через str_replace это вообще плохая практика

Ага, "в середине убирать не надо" это и есть намек на него.

В принципе, всю статью можно было бы сократить до фразы "ещё вчера я вообще не знала про этот параметр" и скриншота письма с отказом в оффере, и получилась бы классная статья о том, что незнание не освобождает от ответственности.

Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории