Pull to refresh

Comments 227

>_<
Вы когда в кинотеатр кино пойдёте смотреть, помните: эти двое поссорятся, а вон того убьют последнего.
Дык не тыкайте, в чем проблема то?
Это разве спойлер…
Вот это спойлер:

Kevin Spacey spoiler
эмм)) не слишко ли много буков для такого тривиального решения? одна рекурсивная функция всегто
Потратил минут 12 на написание.

<?php

function array_merge_recursive_distinct () {
  $arrays = func_get_args();
  $base = array_shift($arrays);
  if(!is_array($base)) $base = empty($base) ? array() : array($base);
  foreach($arrays as $append) {
    if(!is_array($append)) $append = array($append);
    foreach($append as $key => $value) {
      if(!array_key_exists($key, $base) and !is_numeric($key)) {
        $base[$key] = $append[$key];
        continue;
      }
      if(is_array($value) or is_array($base[$key])) {
        $base[$key] = array_merge_recursive_distinct($base[$key], $append[$key]);
      } else if(is_numeric($key)) {
        if(!in_array($value, $base)) $base[] = $value;
      } else {
        $base[$key] = $value;
      }
    }
  }
  return $base;
}

function read_conf($path) {
	$output=array();
	$file=fopen($path,'r');
	while($row=fgets($file)) {
		list($key,$value)=explode('=',$row);
		$path=explode('.',$key);
		$path=array_reverse($path); $tmp=$value;
		foreach ($path as $v) {
			$tmp=array($v=>$tmp);
		}
		$output=array_merge_recursive_distinct($output,$tmp);
	}
	fclose($file);
	return $output;
}


$res = read_conf("conf.cnf");
var_dump($res);
?>
function array_merge_recursive_distinct () {
$output=array_merge_recursive_distinct($output,$tmp);

12 минут на написание, а на тестирование?
включая тестирование. если не верите что работает проверьте ;)
и пусть вас не смущает что в описании функции нету параметров.
$arrays = func_get_args();

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

Единственное, не понятно зачем вы применили в данном конкретном случае, ведь количество аргументов у вас одинаковое во всех вызовах функции.
изначальная задумка, думалось что будет несколько аргументов… но в итоге получилось два
>list($key,$value)=explode('=',$row);
Если в значении параметра встретиться знак "=", то есть шанс что ваша функция выдаст чушь.
кстати да, но решается заменой explode('.',$key); на explode('.',$key,2);
list($key,$value)=explode('=',$row, 2) + array('','');

Позволит избежать «Notice: Undefined offset: 1» если explode вернет меньше чем 2 элемента. Также можно использовать для указания значения по умолчанию (например, null сделать) для настроек вида «param» (пропущено '=значение').
Не вводите людей в заблуждение!
protected function _loadIniFile($filename) — внимательно читаем и видим, что больше двух уровней вложенности она не парсит
Вы невнимательны. Этот метод не парсит инишник, он отвечает за наследование секций.
И вы будете полностью разочарованны. Я бы вас не взял работать в серьёзную компанию.

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

Причина отказа: любой другой программист не сможет разобраться в вашей функции, потому что она ужасна.
А что вы товарища минусуете? Я, как человек, ведущий собеседования PHP-ников в проект с несколькими сотнями мильёнов динамических хитов в сутки, сразу бы послал его вон. Если с $k и $v ещё можно как-то жить, то $cc — это «досвиданься, спасибо за ваше время». И да, это несмотря на впечатляющее время выполнения задачи.
Подобное наименование переменных допустимо в функции такого размера.
Вы преувеличиваете. Или не видели по-настоящему ужасных функций.
Не преувеличивает. Нормальное именование должно быть в крови.
Преувеличивает. Вы явно не видели говнокод. Это вообще еще семечки.
Вы не сталкивались с ситуациями, когда вам нужно исправить ошибку в коде, когда второй программист заболел?

Да, он написал эту функцию за 6 минут, но исправлять ошибки будете ещё час.
Я понимаю, о чём речь. Работал как-то в паре с программистом, который именовал переменные набором букв со случайно подвернувшихся клавиш, типа «jhg» или «uiop»; но, по счастью, я оттуда ушёл чуть раньше.

И всё же в этом примере вполне понятная логика, так что не думаю, что может потребоваться час на дебаггинг.
Я другой программист, писал на PHP 4 года. Разобрался сразу же.
Я не стал бы работать в вашей «серьезной» компании, поскольку вы явно недооцениваете кандидатов.
<?
function parseline($conf, $line) {
  list($key, $val) = explode('=', $line, 2);
  $keypath = explode('.', $key);
  $cconf = &$conf;
  foreach($keypath as $k)
    $cconf = &$cconf[$k];
  $cconf = $val;
}

$conf = array();

$configtext = file_get_contents('config.txt');
foreach(explode("\n", $configtext) as $l) {
  $l = trim($l);
  parseline(&$conf, $l);
}

var_dump($conf);


* This source code was highlighted with Source Code Highlighter.
Угу. 400ms на парсинг конфиг-файла весом в ~килобайт. Когда я нашел это в нашем проекте — повесил девелопера на фикусе прямо посреди офиса.
Ага, вишу я на фикусе и думаю:) ранняя оптимизация зло или нет…
А потом врубили APC и полетели…
ахах, Андрей:)) Не заметил что это ты. Слушай, ну за это время пора бы и исцелиться от Zend'офилии:)
с «ооп головного мозга», это раз и навсегда ;)
Функция split() уже deprecated, почему не explode()?
Перловые привычки, простите.
parse_ini_file + дополнительный процессинг точек (ака разбивка на массив)?

Интересная задачка, но больно прикладная ИМХО
Ну если стояла задача написать именно полностью свое решение, а не применять функции парсинга ini-файлов, то можно было написать что-то вроде следующего:

function addToDictionary($array, $keyArray, $value)
{
    if(!isset($array[$keyArray[0]]))
    {
        $array[$keyArray[0]] = array();
    }
    
    if(count($keyArray) > 1)
    {   
        $partOfKey = $keyArray[0];
        unset($keyArray[0]);
        addToDictionary($array[$partOfKey], $keyArray, $value);
    }
    else
    {
        $array[$keyArray[0]] = $value;
    }
}

function read_conf($filename)
{
    $result = array();
    $fp = fopen($filename, "r");
    while(!feof($fp))
    {
        $line = fgets($fp); //Считываем новую строку конфига
        $keyValueArray = split("=", $line); //Отделяем ключ от значения
        $keyArray = split(".", $keyValueArray[0]); //Отделяем части ключа
        addToDictionary($result, $keyArray, $keyValueArray[1]); //Добавляем в массив
    }
    fclose($fp);
    
    return result;
}
Код не тестировал, возможно где-то допустил ошибку, не судите строго. В кратце алгоритм таков: считываем строку из конфига, разделяем ее на ключ и значение. Ключ также разделяем на куски. Также создаем функцию, которая рекурсивно (пока в массиве частей ключа больше 1 элемента) создает массивы по частям ключа. Когда кол-во элементов массива ключей становится равным 1, присваиваем переданное значение.
Сам же заметил некритическую ошибку:

Первые 3 строки функции addToDictionary стоит внести в if(count($keyArray) > 1) блок
массив по ссылке надо передавать:
function addToDictionary(& $array, $keyArray, $value)
Я хоть и совсем себя программистом не считаю (так, с CMS Drupal иногда играюсь), но эту ошибку тоже заметил…
Признаться, считал, что массивы по умолчанию ссылкой передаются, как например, в C#. Век живи — век учись.
По ссылке передаются только объекты. Точнее, они не совсем по ссылке, но работаешь с тем же объектом.
Вот как то вот так:
function read_conf($filename) {
foreach (parse_ini_file($filename) as $key=>$value) {
$key = vsprintf('$result["%s"] = "%s";', array(
str_replace('.', '"]["', $key),
$value,
));
eval($key);
}
return $result;
}
Сорри, отбивка не получилась. Работает при любом уровне вложенности и код понятный
Кстати, тоже влепил реплейс точек в ключе с последующим eval. Хоть в общем случае eval и зло, но тут получается просто и понятно.
Красивое решение, нестандартный подход… Спасибо, запомнил на будущее.
Когда дочитал задание сразу про этот способ подумал. Прелесть интерпретируемых языков.
Тоже сразу об этом подумал, но только в качестве «на коленке для себя». На собеседовании такое не стал бы показывать. (Многие считают eval вселенским злом по умолчанию, и кстати не зря, так что многие работодатели забраковали бы подобное решение даже не посмотрев работает оно или нет.)
если других вариантов не было можно было и рискнуть же
Не знаю, у меня сразу возник вариант (в комметариях ниже был подобный вариант, по этому свой приводить не буду) с обычным file+foreach, а уже потом (как вариант «для извращенцев») возникла идея что можно также использовать и eval (но как мне кажется это вариант больше «для прикола» чем для реальной ситуации)
Нет смысла ИМХО идти к работодателю, который считает eval вселенским злом. Это либо человек с предрассудками, либо владелец бизнеса с плохой тенденцией к микроменеджменту. Если собеседуемый действительно подкованный человек, он сумеет найти подходящий аргумент и переубедить, почему именно его вариант правильный. Именно таких людей и ищут на senior+ должности — уверенных (но не самоуверенных) :)
заколебетесь переубеждать. ;) неожиданно вспомнится, что ключи могут содержать символы ], [ и $ (ни к то ж не утверждал обратного), а элементы с цифрами валидны только на третьем уровне.

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

Поскольку общепринятого стандарта описания INI-файла нет — то разработчик вполне вправе предпологать, что исходные данные по задаче — максимальны по содержанию и требованию. Все остальное — защита «от дурака» (описано чуть ниже, в другом комментарии).

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

Никакой шутки — никакого смайла

З.Ы для защиты от «запрещенных» символов — достаточно всего навсего дописать экранирование ключа. Но опять же, это выходит за рамки конкретной задачи. Если собеседующие люди укажут на те факторы, которые вы описали выше — они будут безусловно правы (заказчик всегда прав?), но это уже будет «change request», так как одна из составляющих бизнес-логики изменилась (формат данных). И это уже будет совершенно другая задача, пусть и связанная.

Вы можете сказать, что можно было подобную задачу решить «наперед», просто подумав об этой возможности? Безусловно можно, любой мало-мальски толковый программист именно так и сделает! Но есть одно НО. Давайте представим, что мы описываем не простейшую функцию, а огромный и сложный кусок бизнес-логики. И добавление неописанных в ТЗ обработчиков поведения — затягивает рабочий процесс, релиз, не на несколько минут, а на дни, недели, месяцы? А потом выясняется, что этот функционал и вовсе не нужен, так как там всегда будет только точка и литералы, и ничего кроме? Понимаете, о чем я? :)
Нет уж, усложнять требования задачи самостоятельно нужно, в этом лежит основа безопасности любой программы. Короче говоря на «возможно всё что угодно, пока не доказано обратного». Если в спецификации не написано «там не может быть буквы зю», она там может быть, и никак не наоборот. Не редко на собеседовании хотят от вас именно такого мышления, и «Защита от запрещенных» символов тут не причём, это не защита (звучит как нечто опциональное) а часть скрипта точно так-же как тормоз часть автомобиля. Да можно ехать без тормозов и даже приехать в нужное место в нужное время, но называть автомобиль без тормоза хорошим автомобилем, я бы постеснялся.
В противном случае окажется что там будут не только точка и литералы через месяц после сдачи — придется брать время из новых проектов, которые по этой самой причине отстанут на месяц, на два.

В вопросе срок/качество я выбираю качество. Поэтому я не профессионал :(
Ну и ничего страшного. Вам дали ТЗ, по нему вы написали код. Заказчик под принял, протестил, денег отдал — проект закрыли. Через месяц поменялись условия — открываем новый таск на доработку с новой таксой.
Это, конечно, на словах и в идеале, и я понимаю, что на практике заказчик будет говорить: «ну как же так, это ж ежу понятно, что могут быть спецсимволы в ключах»
Со словами «как вариант, если надо быстро» — чень даже. А если еще добавить «в данном подходе eval будет злом только в том случае, если программист в конфиг ресь напишет»…
Люди, вы чего. Красивое решение? Прелесть интерпретируемых языков?

===config.txt===
a = "\"; echo \"php>=5.3 required; Предлагаю назвать этот вид уязвимости PHP-injection\";\" "

В задаче ничего не сказано о том, что конфиг является опасным. Не стоит ИМХО придумывать самому себе задачи :) Если имеем дело с потенциально опасным конфигом — тогда условие задачи должно быть сформулировано соотвтствующим образом и в код должна быть добавлена логика валидации
Если вы выбрасываете свой код через 5 минут после написания — то безусловно, ваш вариант можно использовать. Набыдлокодил и забыл.
Но если вы тешите себя надеждой что после вас еще кто-то будет с этим кодом работать, поддерживать и ре-использовать его — такие eval'ы даже в голову не должны приходить.
Ничего личного.
Вы ненавидите eval потому, что… кто то сказал, что это плохо? PHP интерпретируемый язык, это все один сплошной eval, нет?

Есть задача. Строгая, типизированная, задача. Поставленная программистами — программисту. Задача содержит в себе глубокий смысл — а именно, возможность понять, как мыслит человек, которых проходит собеседование. И поверьте — мне кажется, что ответ с eval вполне удовлетворил бы этих господ. Почему? Он (ответ) не стандартен, а значит человек который можут думать нестандартно — будет способен решать другие задачи быстро, дешево и изящно. Лично я бы хотел бы видеть как можно больше людей с нестандартным мышлением в своей команде. Почему? Для стандартных решений я умею пользоваться google.

Задача. Черный ящик, со входом и выходом. Давайте попробуем в данные 10 минут решить самое главное требование — реализацию бизнес-логики приложения. А проверки на «присутствует ли физически файл конфига», «не пришел ли барабашка и не положил ли в конфиг беду» и прочие вещи, которые выходят за рамки конкретной задачи — будем решать по мере поступления новых требований, а не заниматься отсебятиной?

Вот потому и имеем (к сожалению) в мире людей, которые занимаются всем чем угодно: красивостями, построением «защиты от дурака», стадным блюдением стандартов — всем, чем угодно, но только не самой задачей в полной мере ее объема до ее логического завершения.

Знаете, в жизни каждого программиста происходит переломный момент, когда выражения вида «вселенское зло», «быдлокод» и пр. начинают вызывать добрую улыбку :) Вселенское зло не есть eval. Вселенское зло есть 30 строк дополнительного (читать избыточного) кода, созданного либо потому, что другого подхода к решению программист не видел (это лечится, приходит с опытом и все будет хорошо), либо же (второй вариант, клинический) — когда программист знает и отдает себе отчет, что это избыточный код, но… в его eval будут тыкать пальцами.

Зло eval'a в параграфе выше преувеличено, на его примере показана практика чтения и поддержки стороннего кода, когда main-программист вместо того, чтобы написать коротко и изящно (при этом читаемо и понятно) — пишет операционную систему. Зло, это когда вместо одной регулярки — имеем 50 строк хлама. Зло — это когда простейшую функцию набиваем таким количеством хлама (может быть и нужного, безусловно, но только не на текущий момент времени), что функция просто не читаема.

Зло — это преждевременная оптимизация велосипеда, который изобретать не нужно, но очень хочется.
eval — плохо понимается средой, плохо отлавливаются ошибки, не оптимизируется оптимизаторами, может стать причиной уязвимости и ещё куча-куча всего. Возможно, это неплохой инструмент иногда (крайне редко), но лучше убедить людей, что он плохой, чем стараться объяснить, когда его можно использовать.
Вот тут вот, в ваших словах, и скрыт глубокий смысл — эти самые убеждения, порой, доходят до абсурда. Программист просто не в состоянии порой объяснить, почему именно это плохо. Это просто — плохо. Плохо и все, так в интернете написано было да и друг, старый программист на Cobol рассказал.

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

Есть очень хорошая пословица: «Не уверен — не обгоняй». В нашем с вами деле — то же самое :)
UFO just landed and posted this here
> Вы ненавидите eval потому, что… кто то сказал, что это плохо?
Вы выше привели кусок кода который отлично показывает почему eval испльзовать не стоит, если не понимаешь чем это грозит.

> Задача. Черный ящик, со входом и выходом.
Да нивапрос. Вас попроси сделать вывод ности из базы — вы, наверное, напишете
$news = mysql_query('SELECT * FROM news WHERE id=' . $_GET['news_id']);
А чё — работает, условиям задачи отвечает.

> Да нивапрос. Вас попроси сделать вывод ности из базы — вы, наверное, напишете
А вот тут уже флейм пошел (и причем здесь новости?). Простите, ухожу, не хочу
Вы правда не видите разницы? В первом случае конфиг, контролируемый админом, который и так имеет ПОЛНЫЙ доступ к машине. А во втором практически неконтролируемый параметр news_id и поэтому должны его проверять.
> В первом случае конфиг, контролируемый админом
Это с чего вы взяли?
Давайте, попробуйте убедить меня что функция которая позволяет сделать eval пользовательского ввода (того самого которому никогда нельзя доверять, ага), которая завершает работу ВСЕЙ программы (не возвращает «false», не выбрасывает эксепшен, а падает с fatal error) если в значении параметра встречается \" — это «красивое решение» :)))
> > В первом случае конфиг, контролируемый админом
> Это с чего вы взяли?
Тут пример приведу — эта функция может использоваться для редактирования настроек форума на сайте. На большом сайте есть маленький форум, у которого есть отдельный админ. Всё что он может делать с форумом — редачить его настройки через ini-file. И тут привет — вы даёте ему полный доступ к eval'у чего угодно на сервере.

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

Повторюсь — для домашних страничек на коленке эту функцию можно использовать — написал и выкинул, через неделю уже и не вспомнишь чего там было. И боже упаси это потом поддерживать.
Но если у вас проект на много тысяч строк над которым вы работаете уже второй год и не помните с точностью до процессорной инструкции какая функция как работает — такое писать нельзя. Сами же потом на все грабли в коде и наступите.
>Тут пример приведу — эта функция может использоваться для редактирования настроек форума на сайте. На большом сайте есть маленький форум, у которого есть отдельный админ.

А еще эта функция может предназначаться для конфигурации космического корабля. Эти шведские программисты — они такие хитрецы!

З.Ы простите, мне кажется у вас паранойя, эвалофобия и пробелы в знании предметной области
> Давайте, попробуйте убедить меня что функция которая позволяет сделать eval

php.net/manual/en/function.eval.php

Return Values

eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned. If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally. It is not possible to catch a parse error in eval() using set_error_handler().
Ололо. Я, если что, уже 3 года как на php не пишу.
и то смог прочитать: In case of a fatal error in the evaluated code, the whole script exits.
>Ололо. Я, если что, уже 3 года как на php не пишу.

Так а что вы тогда делаете в этом топике?

З.Ы предположу, что функция eval всегда так работала
>Это с чего вы взяли?
Ну посмотрите наконец содержимое файла. Или bind адрес может каждый менять?
>того самого которому никогда нельзя доверять, ага
Защитное программирование головного мозга? :-) У каждой задачи есть границы, выход за которые неразумная трата средств.
addslashes/addcslashes никто не отменял
Использование eval — первое же решение о котором я подумал по прочтении задачи, но я его отринул (но я не говорю что не использую eval вообще) из-за особенностей отлова ошибочных ситуаций и даже отслеживания различного рода атак через конфиг (например если не делать проверок на символы в наименованиях параметра, туда можно поместить вызовы функций типа:
===config.txt===
fwrite(fopen('test','x'),'test');//.=ha!


P.S. Конечно достаточно указать точный формат (проверки регулярками), который должен исключить все возможные варианты,… обычно если проект простой и незначительный, такие проверки опускают временно… но 'нет ничего постоянного чем временное'. И часто эти проверки обычно слишком простые (например вместо исключения опасных символов указывается список допустимых) — что порождает проблемы использования не латинских символов (русские например) или еще какие случаи.

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

(Точнее моя 'слепая' реализация была чуть покороче и я допустил две примитивные синтаксические ошибки :) )
Вот нечто похожее:
Есть массив вида:

$items = array(
array(
'id' => 12,
'category_id' => 4,
'color' => 'red',
),
array(
'id' => 13,
'category_id' => 5,
'color' => 'green'
),
array(
'id' => 14,
'category_id' => 4,
'color' => 'blue'
),
array(
'id' => 15,
'category_id' => 6,
'color' => 'blue'
)
);

id товара
category_id — id категории (трусы, очки, сигары)
color — цвет
… — могут быть ещё параметры (ширина, глубина, секс и т.п.)

Надо преобразовывать этот массив в вид дерева (сгруппировать в дерево) на основе массива настройки:
$template = array('color', 'category_id', 'id');
или
$template = array('category_id', 'color', 'id');
или
$template = array('sex', 'category_id', 'color', 'id')
и т.п.

Полей в $items может быть больше, количество полей в $template может быть тоже любым.
$template = array('category_id', 'color', 'id');

l2t($arr, $template);
-->

array
--4 =>
----array
------'red' =>
--------array
----------12 => null
------'blue' =>
--------array
----------14 => null
--5 =>
----array
------'green' =>
--------array
----------13 => null
--6 =>
----array
------'blue' =>
--------array
----------15 => null
оу оу хватит ребята, сегодня пятница)))
Если бы автор сказал, что нашел на просторах сети очередную задачку, то даже и париться не стал бы. Но предисловие о том, что подобное задание дают на собеседовании в очень серьезной конторе, заставило напрячь извилины :)
У меня получилось вот так никуда не заглядывая.
Самая хитрая часть — конечно же сделать уровни вложенности. Решил делать через ссылки.
Если бы не вложенность, то решение в два раза короче 8))

function read_conf($f){
    $lines = file($f);
    $result = array();
    foreach($lines as $line){
	$line = trim($line);
	if(false === strpos($line ,'=')) continue;
	if(0 === strpos($line, '=')) continue; 
	list($key, $value) = explode('=', $line);
	$key = trim($key);
	$value = trim($value);
	$parts = explode('.', $key);
	
	$iterator = &$result;
	foreach($parts as $part){
	    if(isset($iterator[$part])){
		$iterator = &$iterator[$part];
	    } else {
		$iterator[$part] = array();
	    }
	}
	$iterator[$part] = $value;
//	$result[$key] = $value;
    }
    return $result;
}

Только одно но: для строки конфига session.server.0.id=session1 сперва для ключа 0 будет создан массив ($iterator[$part] = array();), а потом сразу будет заменен на значение ($iterator[$part] = $value;).
Это не страшно 8)
ведь в задаче не сказано как поступать с a = 1
Это не страшно 8)
ведь в задаче не сказано как поступать с
a = 1
a.b.c = 1
a.b = 1

Если без таких коллизий, то результат у меня должен быть верным
Также в цикле foreach если на каком-то уровне вложенности ключа, в массиве такого элемента нет, то вы его добавляете, но не двигаете итератор.
Да, пожалуй, вы — правы
	    if(isset($iterator[$part])){
		$iterator = &$iterator[$part];
	    } else {
		$iterator[$part] = array();
	    }


Стоит заменить на
	    if(!isset($iterator[$part])){
		$iterator[$part] = array();
	    }
	    $iterator = &$iterator[$part];


Во! Вот так должно быть!
	$iterator = &$result;
	foreach($parts as $part){
	    if(!isset($iterator[$part])){
		$iterator[$part] = array();
	    }
	    $iterator = &$iterator[$part];
	}
	$iterator = $value;
Может быть заменить
if(false === strpos($line ,'=')) continue;
if(0 === strpos($line, '=')) continue;

на
if( ! strpos($line ,'=')) continue;
Не, в таких случаях предпочитаю именно два условия, чтобы ни у кого не было подозрений, что здесь может быть ошибка. Два условия выражают две разные мысли. Одна о невалидных строках, вторая о комментариях 8) Просто мне показалось, что ==== code ==== — это комментарии такие 8)))
В любом случае strpos() стоило бы вызвать один раз.
$eqpos = strpos($line, '=');
if ($eqpos === false || $eqpos === 0) continue;

Мысли выражены, лишнего вызова функции нет.
UFO just landed and posted this here
Ну почему сразу так, это же не пакистанцы, запрашивающие убежище и в поисках работы.
Хотя мне не очень нравится способ проверки знаний в виде «а реши-ка ты это сейчас быстро», причем дают довольно комплексные задачки, где не мешало бы посидеть и хорошо подумать.
Никто ведь в последующем не будет стоять над тобой и подгонять…

P.S. Это случаем не Aftonbladet? Мне всегда кажется, что каждый уважающий себя швед пролистывает этот сайт хотя бы раз в день. :)

Не Alftonbladet :) Но размер сайта сопоставим.
задача элементарнейшая, а вы демонстрируете типичный снобизм эмигрантов. чуть что, сразу ущемление прав? ты бы хотел работать с человеком, который не может такую задачу решить?
UFO just landed and posted this here
UFO just landed and posted this here
То что вы Игнат Уадчи — обязательно писать в каждом комменте? :)
UFO just landed and posted this here
Вы правы, среди PHP-шников очень немало «даже до быдлокодеров не дотягивают», причина проста, потому что с этого языка многие начинают (как раньше начинали с Паскаля или Бейсика, и упаси вас бог увидеть мою писанину на Бейсике) но как вы и сказали исключения есть.

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

Удачи вам, сеньеро.
У меня вот так сходу получилось:

function read_conf($filename) {
	$lines = file($filename);
	$result = array();
	foreach($lines as $line) {
		$parts = explode('=', $line);
		$path = explode('.',$parts[0]);
		$r = &$result;
		foreach($path as $q) {
			$r = &$r[$q];
		}
		$r = $parts[1];
		
		
	}
	return $result;
}

$r = read_conf('config.txt');
print_r($r);
Вместо:
$parts = explode('=', $line);
Нужно делать:
$parts = explode('=', $line, 2);

Ибо, если в значении будет встречен знак "=", то у вас будут глюки.
Ну тут как ставить задачу.
Вообще код можно расширить проверками в 3-5 раз.
И проверить есть ли такой файл, и правильность синтаксиса, и разные кодировки, и кеширование.
А еще обернуть все это в ООП и добавить expception, и покрыть код тестами.

Только вот зачем? Ведь это задачка на собеседовании на 10 мин и в даном случае нужно было просто показать что ты знаешь как оно должно работать, а вот всякие ситуации рассматривать это уже когда возьмут на работу)
Наверняка те, кто проводит собеседование, хотят так же узнать насколько человек способен отслеживать вероятные глюки в процессе написания кода, а не после его 105-го выполнения. Так что на мой взгляд учитывать всяческие нюансы полезно так же и на 10-минутных задачах на собеседовании.
Но и с вами не могу не согласится. Наверное все же лучший вариант написать самый елементарный код, что бы он работал.
А дальше уже не спеша, вслух рассказать о потенциальных проблемах и как их решать.
Тогда хорошее впечатление 100% гарантировано.
по-моему, кодить, а затем рассказывать о потенциальных проблемах этого кода — не самая лучшая стратегия для программиста. :)

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

встречаются разные неудобные типажи. если претендент сразу бросается писать закорючки на php, — перед вами скорее «кодер». если начинает до упора мусолить постановку, значит — «формалист». ну и т.п.

в общем случае, лучше начинать с уточнения требований. ведь через 10 минут может выясниться, что строк в конфиге бывает over 9K, а памяти в обрез, ключи содержат юникод, и кучу т.п. причин, которые превратят весь ваш труд в бесполезную трату времени (кстати, отличный способ троллить «кодеров» :))

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

писанина на бумажке сильно отличается от привычного текстового редактора, т.к. не допускает вставок. для многих программистов это не привычно — нужно писать последовательно. но есть и плюсы — можно псевдокодить. короче, если подозреваете, что на собеседовании могут быть подобные упражнения, лучше потренируйтесь заранее, решив несколько головоломок из интернета именно на листочках.
Мда, у меня с налета понять работу с ссылками в вашем примере не получилось :) Может, разъяснитье немного принцип работы?
Конечно
пусть есть конфиг
server.name=main

тогда
$parts = array('server.name', 'main');
$path = array('server','name');

$r = &$result; // $r ==$result
и в цикле foreach (по итерациям)
$r = &$r['server'] // $r == $result['server']
$r = &$r['name'] //$r == $result['server']['name']

$r = $parts[1] // $result['server']['name'] = $parts[1]

Вспоминая определение ссылок в PHP — переменные a и b, если сказано $a = &$b, ссылаются на один и тот же участок памяти. Из-за этого и происходит изменение переменной $result, поскольку переменная $r начинает ссылаться на место в памяти, указаное индексом массива (кстати, удивило, что PHP никак не ругнулся на неопределеный по сути своей элемент в массиве), я верно понял?
Да верно. То есть после

Упс, случайно отправилось

Да верно, то есть после
$r = &$r['server']
вызов $r['something'] полностью равносильно $result['server']['something']
ну и т.д.
Интересует момент еще, почему после во время смены ссылки ($r = &$r[$q]) не разрывается старая ссылка с $r = &$result;? Чем это обусловлено?
сначала вычисляется правая часть выражения
потом она присваивается в левую.

это как $x = $x +1;
Чему в данном случае равно «вычисление» — вот это я никак не могу понять.
$r = &$result// это я думаю понятно
теперь например идет такое присвоение
$r = &($r[$q]) //взял в скобки для понятности
вычисляем правую часть
&($r[$q]) == &($result[$q]) (замена из 1 строки)
присвоение
$r = &($result[$q])

повторяем все еще раз, итерация 2
$r = &($r[$t])
вычисляем правую часть
&($r[$t]) == &($result[$q][$t])
$r = &($result[$q][$t])
ну и так далее

если все так же не понимаете, учите c/c++, там с этим прийдется разобраться)
Спасибо большое, теперь, наконец, понял.
Пожалуйста, всегда рад помочь чем могу)
Весь секрет в том, что если вы присваиваете по ссылке значение неинициализированной переменной — она автоматически создается. Таким образом, если сделать $b = &$a['key']; $b = 'val', то будет автоматически создан массив $a = array('key' => 'val') (при условии, что такого массива еще нет). В приведенном примере автор просто последовательно проходит по всей иерархии (создавая массивы, если их еще нет) и потом присваивает значение последней ссылке. Красота в том, что $result при этом не меняется и все время содержит целиком массив с конфигом, а вся работа просходит при помощи ссылок.
По моему это — самое адекватное решение. Единственный момент —
$r = &$r[$q];
не нужно ли предварительно создать массив (прежде чем обращаться к его ключу)? Мне кажется текущая версия будет сыпать Warning-и.
Я тоже так сначала подумал. Но насколько я понимаю я получаю лишь ссылку на такое елемент, а не его значение, а значит все ок. А в конце, когда задаю само значение, цепочка всех массивов автоматически создается, тоже без варнингов.
Это я понимаю. Все-таки тут особенность PHP… В питоне такой код бы отвалился например в той строчке, где я выделил на второй итерации.

Суть в том, что когда мы делаем
## $path=array('key1', 'key2');
$r=&$result; #$r=array();
$r=&$r[$q]; #$r=array('key1'=>NULL)
$r=&$r[$q]; #тут мы обращаемся ключу 'key2' значения массива с ключом 'key1' т.е. NULL.
PHP преобразовывает NULL к массиву автоматом при первом обращении к NULL как к массиву.

Такое только в PHP срабатывает…

php > $path=array('a', 'b', 'c');
php > $res=array();
php > $r=&$res;
php > foreach($path as $q){
php {     $r=&$r[$q];
php {     var_dump($res);
php { }
array(1) {
  ["a"]=>
  &NULL
}
array(1) {
  ["a"]=>
  array(1) {
    ["b"]=>
    &NULL
  }
}
array(1) {
  ["a"]=>
  array(1) {
    ["b"]=>
    array(1) {
      ["c"]=>
      &NULL
    }
  }
}
php > 

Ухты) Буду знать. Хотя я не устал от php ожидать всяких вот таких приколов.
Вот только как же портит php программиста, если это его первый язык. Не зря говорят, начинать обучение именно с c++/c
А что говорят про тех, кто начинал с машинных кодов, ассемблера и машины Поста?
«елементарный» выше конечно режет глаз, но с кем описок не бывает. Но когда в еще одном комменте пишете «елемент» это уже перебор! :)
Виноват! Просто русский язык никогда не учил, пора взяться за книжку :)
Даже в Strict-режиме никаких уорнингов?

error_reporting(E_ALL|E_STRICT|E_NOTICE);

Все равно все чисто.
Чуть выше автор (за что ему огромное спасибо, жаль, не могу плюсануть) все подробно разъяснил в ветке, которую я начал :)
У меня точно такой же вариант получился!
Почитайте доку по функции explode — и проверку мутить не придется. Достаточно один дополнительный параметр передать.
У меня получилось вот такое решение. На все потритил 6 минут. Из-за ограничений по времени, пришлось использовать eval(), что не очень хорошо. Правильнее было бы использовать рекурсию.

  1. <?php
  2.  
  3. function read_conf ($fn)
  4. {
  5.     $file = file($fn);
  6.     $config = array();
  7.  
  8.     foreach ($file as $line)
  9.     {
  10.         $line = explode("=", $line);
  11.         $key = $line[0];
  12.         unset($line[0]);
  13.         $value = trim(implode("=", $line));
  14.  
  15.         $key = explode(".", $key);
  16.         $str = '$config';
  17.         foreach ($key as $l)
  18.         {
  19.             $str .= "['$l']";
  20.         }
  21.         $str .= " = '$value';";
  22.         eval($str);
  23.     }
  24.  
  25.     return $config;
  26. }
  27.  
  28.  
  29. $res = read_conf("config.txt");
  30. var_dump($res);
  31.  
Поспорьте об этом с Эрланг программистом
output руками чтоли писали? Ох лол:)
Поправьте, в первой стороке string, а не strong)
Поспорьте об этом с Эрланг программистом.
UFO just landed and posted this here
UFO just landed and posted this here
function read_conf($file_name) {
parse_str(implode("&", array_map(function($line) {
list($key, $value) = explode('=', $line);
return preg_replace('/\.([^\.=]+)/i', '[\\1]', $key) .'='. $value;
}, file($file_name, FILE_IGNORE_NEW_LINES))), $data);
return $data;
}
Поправлено для знака равно в значении.
function read_conf($file_name) {
  parse_str(implode("&", array_map(function($line) {
      return preg_replace('/\.([^\.=]+)/i', '[\\1]', substr($line, 0, strpos($line, '='))) . substr($line, strpos($line, '='));
  }, file($file_name, FILE_IGNORE_NEW_LINES))), $data);
  return $data;
}
Красиво, чесслово.
И не придерёшься, что eval используется, и рекурсий нет.
Даже невозможно придраться к именам переменных.
И коротко. И быстро.
Раз уж можно использовать eval, то
function read_conf($file_name){
    return eval(preg_replace_callback('/(.+?)=(.+)/',function($m){return'$r["'.str_replace('.','"]["',$m[1]).'"]="'.$m[2].'";';},file_get_contents($file_name)).'return $r;');
}
А зачем в три строчки? Можно ведь было в одну уместить;)
В плане производительности мне тоже кажется, разумнее преобразовать к формату, для которого есть готовый парсер (сишный, встроенный в php), ведь он работает быстрее кода на самом php.
Сначала подумал «ключи» точки заменить на ][ + пошаманить и заюзать евал. но потом подмал что это будет говнокод.

получилось так:function read_conf($fname)
{
$result = array();
$fp = fopen($fname, "r");
while($line = fgets($fp))
{
if( trim($line)=='') continue;
list( $keys, $value ) = explode("=", trim($line));
$akeys = explode(".", $keys);
$temp = $value;
for($i=count($akeys)-1;$i>=0;$i--)
$temp = array( $akeys[$i] => $temp);
$result = array_merge_recursive($result, $temp);

}
return $result;
}
извините за критику, но мне кажется это не сложная задачка, и «плохо Вам» если «С моей стороны она так и осталась нерешенной». Я надеюсь прийдя домой Вы таки ее решили? Иначе грош цена Вашей профессиональной деятельности.
Прежде чем заниматься решением таких задач следует задать себе вопрос — «Зачем?». Я, например, не вижу в этой задае никакого интереса и сложностей кроме «ручкой на бумаге», так зачем тратить на нее время? Это повысит скилл? Нет. Это принесет денег? Нет. Тогда Что!?
Это даст человеку понять _что именно_ на бумаге он не так сделал, это очень помогает. Решение задач, даже если они и просты, но которые с первого раза не решил, нужно обязательно решить потом, чтоб узнать что же ты не так сделал.
Ну, не знаю, не знаю… Есть задачи которые решать действительно надо, есть которые хочется, есть такие которые пока не решишь не будешь находить себе места, но эта, по моему, ни к одной из перечисленных категорий не относится. Алгоритм решения ясен, более того их несколько и все простые. Будет необходимость напишется, а значит не стоит и заморачиваться. Имхо.
Не в задаче дело, а в подходе. Если не сделал простую задачу на бумаге, значит что-то нетак. Нужно знать что именно не так, в этом дело.
function read_conf($path) {

die('Не очень-то и хотелось у вас работать.');

}

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

some.stupid.config=value=another

В этом случае функцию следует применить как explode('=', $config_data, 2)
про это ничего не сказано, равно как и необходимость в проверке доступности файла и много чего другого. есть пример входных и выходных данных — все.
У меня такое решение получилось. Единственное — не обрабатывает значения в кавычках (кавычки идут как значение).
<?php
$res = read_conf('conf.txt');
var_dump($res);

function read_conf($conf)
{
$conf = file_get_contents($conf);
$pattern = '/^((?:[^.=]+\.?)+)\s*=\s*(.*)$/mu';
$res = array();
preg_match_all($pattern,$conf,$res, PREG_SET_ORDER);
$config = array();
foreach($res as $match)
{
_set($config, $match[1], $match[2]);
}
return $config;
}

function _set(&$map, $keys, $value)
{
$keys = explode('.', $keys);
$key = array_shift($keys);
if($key && !isset($map[$key]))
{
$map[$key] = array();
}
if(empty($keys))
{
$map[$key] = $value;
}
else
{
$map[$key] = _set($map[$key], implode('.', $keys), $value);
}
return $map;
}
С первым куском почти согласен, а вот рекурсивный вызов функции можно было избежать (экономия!!! хоть и по мелочам)

1) регулярка /^ ([^\=]+) [\s]* [\=] [\s]* (.*) $/mxu
2) создаем переменную $x которая будет указывать на &$config
$x = &$config; (каждый новый муль-кей)
и по мере разбора $keys
$x = &$config[$key];
если $config[$key] нет, но дальше его создаем… ;)
1) Зачем один символ задавать через символьный класс?
2) А еще можно было избежать лишних explode/implode ) Просто сделал первое что пришло в голову. Все же ограничение в 10 минут.
> 1) Зачем один символ задавать через символьный класс?

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

> 2) А еще можно было избежать лишних explode/implode ) Просто сделал первое что пришло в голову. Все же ограничение в 10 минут.

а где сдась лишние explode/implode? здесь один explode для разбиения $keys по символу «точка». А насчет 10 мин. рекурсию функций в принципе отлаживать дольше чем просто цикл ;)
хотя в данном случае рекурсия примитивная и не слишком замороченая…
Внутри рекурсивной функции:
Сначала мы делаем explode, а потом при рекурсивном вызове делаем implode.
Я б такую задау на карандше решать не взялся. Не то чтобы не смогу, но настолько разбалован PhpStorm'ом, что лишний раз думать становится лениво и без дебагера уже никуда, по крайней мере в сжатые сроки :)
Да даже зачастую не помнишь точное написание некоторых не самых употребляемых функций или порядок параметров. Благо PHP в этом плане богат на варианты.
И это тоже одна из причин. Без мануала, гугла и удобной IDE я работать просто не могу. В смсле если жизнь заставит, то смогу, конечно, но очень и очень не хочу. Если у меня будет выбор между кодингом без хелпов, в блокноте, и разгрузкой вагонов я, пожалуй, пойду разгружать.
Им не нужно так ваше знание названий функций, как умение создавать в голове алгоритм решения. Дальше они смотрят, насколько быстро у вас это получается. И просят комментировать, чтобы понимать, вы тупите, потому что забыли название функции или потому что не знаете, что делать дальше.
Хм; а если я не могу думать и разговаривать одновременно — всё, плохой, негодный программист?
если вы не можете говорить и думать одновременно — значит вы говорите необдуманно — и тогда вы не только плохой программист… :)
Если я не могу говорить и думать одновременно — это не значит, что я говорю необдуманно. Это как раз значит, что я высказываю уже обдуманное ранее.
кодинг без хэлпов не страшен — компилятора/интерпретатора на бумажке все равно нету. Ошибок именований не высветит.
С другой стороны, я думаю, что задания писать в блокноте безошибочно рабочий код не было. И вряд ли бы пристали к имени функции или перепутанному порядку аргументов. Нужно было просто показать сам ход решения хотя бы.
UFO just landed and posted this here
На собеседовании смело можно сначала описать решение в псевдокоде, главное чтобы подход был правильный.
Если с листка функция сразу заработает — это, конечно, громадный плюс, но не стоит отчаиваться если синтаксис забыт, можно списать на волнение и на ту же зависимость от разных кодхэлперов. Зачастую работодателям очень сложно найти толковых сотрудников, а если они видят, что человек соображает, то могут взять и с поблажкой на дообучение.
Выучить синтаксис — легко, научится мыслить — совсем другое.
Точно не засекал, но больше 10 минут. Специально делал не в IDE, а в блокнотике. Но с учётом того, что на собеседованиях всегда напряженная обстановка, то там бы делал дольше ;)

function read_conf ( $confFile ) {
    $content = explode ( PHP_EOL, trim ( file_get_contents ( $confFile ) ) );
    array_walk ( $content,
            function(&$item) {
                $item = sprintf ( "\$ar['%s'] = '%s';" . PHP_EOL,
                        str_replace ( '.', "']'",
                                substr ( $item, 0, $pos = strpos ( $item, '=' ) ) ),
                                substr ( $item, $pos + 1 ) );
            } );
    $content = implode ( null, $content );
    $fnc = create_function ( null, "{$content}; return \$ar;" );
    return $fnc ();
}


Либо более мизонтропичный вариант (но это уже подумать надо, как всё расипхать и самому не запутаться):
function read_conf($confFile) {  
  $f=create_function(null,implode(null,array_map(function(&$x) {return sprintf("\$ar['%s'] = '%s';".PHP_EOL,str_replace('.',"']['",substr($x,0,$o=strpos($x,'='))),substr($x,$o+1));},explode(PHP_EOL,trim(file_get_contents($confFile))))).'return $ar;');return $f();
}


Кстати, ищу работу ;).
$content = explode ( PHP_EOL, trim ( file_get_contents ( $confFile ) ) );
Чем вам так функция file() не угодила?
Вы правы, file() будет даже быстрее работать в этом случае.
Просто то, чем редко пользуешься — забывается.
Кстати, при использовании file(), добавляется необходимость дополнительных проверок на пустую строку, что не совсем благоприятно для читаемости кода.
FILE_SKIP_EMPTY_LINES и никаких дополнительных проверок.
Вы уж извините, но если Вы действительно пишете как во втором варианте, то неудивительно что Вы ищете работу. Бедные ваши коллеги…
Нет, как во втором варианте я не пишу.
Господин или госпожа, влепивший или влепившая минус данному комментарию, а чем вы руководстовались? Зайдите на гитхаб и покажите мне примеры моего кода соответсвующие второму варианту, будьте добры.
function read_conf($filename)
{
        $data = file_get_contents($filename);
        $rows = explode("\n",$data);
        $dict = array();
        foreach($rows as $row)
        {
                if($row=='') continue;
                list($key,$value) = explode('=',$row,2);
                $key = explode('.',$key);
                $addTo = &$dict;
                while(count($key)>1)
                {
                        $addTo = &$addTo[array_shift($key)];
                }
                $addTo[$key[0]] = $value;
        }
        return $dict;
}
да, сначала challenge accepted а потом уже топик читать стал, отличие только в array_shift вместо итерации по массиву
Аватарка позволяет в комментах не писать <?php, а сразу начинать с кода.
Автор. 5+ от души. не смог — признался.
Сам не программер, но поступил бы так же. Держи пять!
function read_cond($filename) {
  $data = array();
  $ini = parse_ini_file($filename);
  array_walk($ini, "ini_callback", &$data);

  return $data;
}

function ini_callback($element, $key, $data) {
  if (strstr($key, ".")) {
    $k = explode(".", $key);
    if (count($k) == 2) $data[$k[0]][$k[1]] = $element;
    //if (count($k) == 3) $data[$k[0]][$k[1]][$k[2]] = $element;
    //.....
  } else {
    $data[$key] = $element;
  }
}


да в ini_callback можно красивее что нибудь придумать, хотя зависит от уровня вложенности :)
Поприветствуем нашего колегу из Индии!
Скажите, а если вложенность будет стократной? А если тысячекратной? Вдруг этот конфиг… будет генерироваться прогой и нужна тысячекратная вложенность? Там банальный цикл, который позволяет хранить неограниченную вложенность.
Так я же не претендовал на идеальное решение, просто написал что в зависимости от задачи написал бы. А где может понадобится тысясекратная вложенность?
Та при чём тут это? Просто быдлокод, стыдно такое должно быть такое показывать.
Зато первое что пришло за 10 минут в голову быдлокодеру :)
ну и от меня на python:
def read_conf( filename ):
    lines = open( filename ).readlines()
    array = {}
    for i in lines:
        key, value = i.split('=')
        items = key.split( '.' )

        current = array
        for j, i in enumerate(items):
            if i in current:
                current = current[i]
            else:
                if j == len(items)-1:
                    current[i] = value
                else:
                    current[i] = {}
                    current = current[i]
    return array

print read_conf( "dialog.txt" )


а минусы то за что?
не заработало? :)
На php уже написали, мне кажется любопытно посмотреть как это выглядит на других языках.
Не хотел никого обидеть :))
Я, как питонист, тоже не удержался решить эту задачу:
def read_conf(filename):
    d = {};
    with open(filename, 'r') as file:
        for line in file:
            key, value = line.split('=',1)
            t = d
            for subkey in key.strip().split('.'):
                if not t.has_key(subkey):
                    t.update({subkey: {}})
                    p = t
                t = t[subkey]
            p.update({subkey:value.strip()})
    return d

print read_conf('config.txt')


Учитывает пробелы между элементами.
За точку с запятой не ругать — просто не заметил её.
А вот этот же алгоритм, но с учётом уже пустых строк, комментариев и c обработкой событий, когда значение переопределяется и когда не соблюдается вложенность. Также проверяется доступ к файлу.
Этим уже можно пользоваться.
def read_conf(filename):
    from os import access, R_OK
    if not access(filename, R_OK):
        raise IOError('Error: Cannot read the configuration file "' + filename + '".')
        return False
    d = {}
    with open(filename, 'r') as file:
        for linenumber, line in enumerate(file):
            line = line.strip()
            if not line or line[0] == '#' or line[0] == ';':
                continue
            line_sp = line.split('=',1)
            if not (len(line_sp) == 2 and line_sp[1]):
                raise IOError('Error: Cannot parse the configuration file "' + filename + '" at line ' + str(linenumber) + '.')
                return False
            key, value = line_sp
            t = d
            for subkey in key.strip().split('.'):
                if not t.has_key(subkey) or t[subkey].__class__.__name__ != 'dict':
                    t.update({subkey: {}})
                p = t
                t = t[subkey]
            if p[subkey]:
                if type(p[subkey]) != type(value):
                    raise IOError('Error: Cannot assign a value to a node in the configuration file "' + filename + '" at line ' + str(linenumber) + '.')
                    return False
                print 'Warning: Overriding previously defined value for key "' + key + '" at line ' + str(linenumber) + '.'
            p.update({subkey:value.strip()})
    return d

try:
    print read_conf('config.txt')
except IOError, error:
    print 'Failed to read the configuration file:'
    print error
Спасибо, люблю такие задачки! :)

Я справился приблизительно за 8-9 минут, причём всё заработало правильно с первого раза без дебага. А сколько времени ушло у вас?

Задачка на стрессоустойчивость. А дым в лицо собеседователи при этом не пускали, иголкой внезапно не кололи? А то ещё можно внезапно крикнуть соискателю в ухо: «Бу!» Какой же он специалист, если его смутят такие, обычные в программистской практике мелочи?
Бу кричать не надо — надо дать одну задачу, взять решение.
Дать вторую задачу и в процессе решения второй задавать вопросы по решению первой задачи.
И да. естественно с временными рамками.
А у меня вот такая бодяга вышла за 10 минут…
<code>  
  $config = file_get_contents("./test-config.txt");
  $config_array = explode("\n", $config);
  sort($config_array);
  $result = array();

  foreach($config_array as $config_item) {
    if ($config_item != NULL) {
      $value = substr($config_item, strpos($config_item, "=") + 1, strlen($config_item));
      $key = substr($config_item, 0, strpos($config_item, "="));
      $key_arr = explode(".", $key);
     
      $tmp = &$result;
      for($i=0; $i < count($key_arr); $i++) {
        if ( !isset($tmp[$key_arr[$i]]) ) $tmp[$key_arr[$i]] = "";
        $tmp = &$tmp[$key_arr[$i]];
      }
      $tmp = $value;
    }
  }

  var_dump($result);
</code>
Блин, сел за решил эту задачу практически идентичным способом. Очень удивился, когда нашел ваш код в комментариях.

Вот мой:
<?php

function read_conf( $file )
{
$result = array();

$file = file( $file ) or die('File not found!');

foreach( $file as $line )
{
$eq_pos = strpos( $line, '=' );
$key = trim( substr( $line, 0, $eq_pos ) );
$value = trim( substr( $line, $eq_pos + 1 ) );

$arr = & $result;

$arr_keys = explode( '.', $key );

foreach( $arr_keys as $i => $arr_key )
{
if( ! isset( $arr[$arr_key] ) )
$arr[ $arr_key ] = ($i + 1 == count($arr_keys) ? $value : array());

$arr = & $arr[ $arr_key ];
}
}

return $result;
}

//read_conf('conf.ini');
echo '
';
print_r( read_conf('conf.ini') );

?></code>
function read_conf($file) {
$lines = file($file);
$result = array();
foreach ($lines as &$line) {
$line = trim($line);
if (!empty($line)) continue;
$lineParts = explode("=", $line);
$path = explode(".", $lineParts[0]);
$current = &$result;
$pathSize = sizeof($path) - 1;
for ($i = 0; $i <= $pathSize; $i++) {
if ($i == $pathSize) {
$current[$path[$i]] = $lineParts[1];
} else {
if (!isset($current[$path[$i]])) $current[$path[$i]] = array();
$current = &$current[$path[$i]];
}
}
unset($current);
}
return $result;
}

Знакомый, не имеющий аккаунта, попросил запостить:

Для интереса решил попробовать. Но, т.к. сам — рубист, то на ruby.

Сначала сохранил исходный текст с данными в файл, открыл текстовый редактор, и засек время. Накодил, нажал SAVE, снова засек время — итого 4 минуты 46 секунд.

Затем запустил проверить — все сработало как надо. Размер скрипта 433 байта:
def read_cfg(fname)
   s = File.read(fname)
   rez = {}
   s.each_line do |l|
      l.strip!
      if l != ''
         key, value = l.split('=',2)
         key.strip!
         value.strip!
         keys = key.split('.')
         last_key = keys.pop
         c = rez
         if keys.length > 0
            keys.each do |k|
               if c.has_key?(k)
                  c = c[k]
               else
                  c[k] = {}
                  c = c[k]
               end
            end
         end
         c[last_key] = value
      end
   end
   rez
end

cfg = read_cfg('cfg')
p cfg

Потом, если подумать, код можно подсократить (там пара-тройка строчек необзательные), но раз уж речь идет про лимит в 10 минут, то код выложил без рефакторинга.
А на питоне вообще достаточно сделать:

from sys.interview.swedish import read_conf

По мотивам xkcd.
UFO just landed and posted this here
Скажите честно, вам доставляет удовольствие троллить? Хабр — форум, что вы к каждому своему комменту добавляете подпись?
UFO just landed and posted this here
Вам явно стоит принять успокоительного. Если мой коммент Вас оскорбил, то прошу извинить, т.к. не ставил такой цели. К чему угрозы? Причем тут влияние денег? Где вы видите обман простых трудовых мужиков? Где вы тут увидели беспредельщиков? Простите, но не улавливаю ни грамма логики.
UFO just landed and posted this here
Пригласили меня на данный ресурс, по всей видимости, администраторы ресурса (отсюда и надпись в профиле «приглашен НЛО») по время последних новогодних праздников за топик в песочнице (позже его переместили в блог .NET). Таким образом абсолютно никакого отношения к «Тематические медиа» не имею.
UFO just landed and posted this here
Слушай, ты реально уже задолбал этой своей подписью в каждом комментарии.
В комментариях такое не принято. Если хочешь, чтобы все знали, как тебя зовут и какого ты года рождения, то напиши это в своём профиле. Кому станет интересно, тот зайдёт в твой профиль и посмотрит. Пора взрослеть.
Удачи, ignat.
UFO just landed and posted this here
UFO just landed and posted this here
Ноги умыть не забудь и зубы почистить! ;)))))
UFO just landed and posted this here
Чувак, извини, я ошибся. Ты не просто 3.1415здец, ты ФЕЕРИЧЕСКИЙ 3.1415ЗДЕЦ! Ибо я написал всего один пост и больше в этом кармадрочерстве участвовать на собираюсь! :))))))))))
UFO just landed and posted this here
>>> закон математики
Ваш комментарий мне почему-то напомнил анекдот…

Биолог, инженер и математик попивают кофе в патио, и замечают, что на другой стороне улицы в дом зашли два человека.
Через некоторое время дом покинуло уже три человека.
Биолог: Двое спарились, размножились и дом покинуло трое.
Инженер: Нет, просто наше первоначальное наблюдение содержит ошибку!
Математик: Вы оба ошибаетесь. Нужно дождаться, пока в дом войдёт ещё один человек и тогда он снова будет пуст.
UFO just landed and posted this here
В 2001 году мы писали интерпретатор на C++, artPublishing (вот тут немного про него http://habrahabr.ru/blogs/development/104191) мы такую штуку писали на сях.

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

К примеру, должны нормально парситься такие файлы:

[block ]
;asdasd=
a="длинный текст
с переносом на следующую строку #решетка работает,
потому что в кавычках" # а это комментарий
b=а тут пишем без кавычек, ясен перец, что должно в одну строку тогда #комментарий


Потом нужно сразу понять, что делать с квотингом кавычек и «решеток». Что делать с ОЧЕНЬ ДЛИННЫМИ СТРОКАМИ.
Что делать с несколькими одинаковыми блоками.
Что делать с несколькими одинаковыми переменными.
в случае a=3,54 трактовать ли правую часть как число или как строку?
Что делать, если в файле стали присутствовать странные символы
Что делать, если знака = вообще нет
Что делать, если скобка квадратная не закрыта
ну и много всего такого.
Без проверок и прочего я бы написал так

function read_conf($fileName)
{
$result = array();
$fp = fopen($fileName, 'r');

while ($line = fgets($fp)) {
list($key, $value) = explode('=', $line);
$keyParts = explode('.', $key);
setValueFromKeyParts($result, $keyParts, $value);
}

return $result;
}

function setValueFromKeyParts(&$result, &$parts, $value)
{
$part = array_shift($parts);

if (count($parts) == 0) {
$result[$part] = $value;
} else {
if (!isset($result[$part])) {
$result[$part] = array();
}
setValueFromKeyParts($result[$part], $parts, $value);
}
}
===config.txt===
id=www
id.2=www2
session.timeout=120
session.timeout.old=200
session.timeout.next=500
session.server.0=open
session.server.0.host=127.0.0.1
session.server.0.port=1111
session.server.0.id=session1
session.server.0=closed
session.server.1.host=127.0.0.1
session.server.1.port=1111
session.server.1.id=session2
image=picture.pic
image.width=640
image.height=480
image.watermark=yes
image.watermark.small=wsmall.png
image.watermark.normal=wnormal.png
===
;-)
Sign up to leave a comment.

Articles

Change theme settings