Правильный способ хранения текста и HTML-кода в базе MySQL
Каким образом необходимо обрабатывать текст и html-код для записи в базу MySQL? Какая схема? Перерыл интернет, везде совет для записи в базу использовать mysql_real_escape_string(), и больше вроде ничего не нужно. Но как быть со спец-мисволами html? К примеру «⇔» при вставке в форму отображается как символ, а не как html-код сивола. Соответственно при записи в базу, он превращается в «?» Как я понимаю необходимо обрабатывать текст вот так перед вставкой:
$inputText = htmlentities($inputText, ENT_NOQUOTES, 'UTF-8'); $inputText = mysql_real_escape_string($inputText);
Соответственно при выводе из базы на отображение необходимо обрабатывать текст вот так:
$inputTex = html_entity_decode($inputTex);
Такой алгоритм правильный или надо делать как-то по-другому? Кодировка сайта utf8.
Отслеживать
задан 11 мар 2015 в 14:08
copenhagen copenhagen
195 1 1 золотой знак 4 4 серебряных знака 9 9 бронзовых знаков
Я храню только текст в БД. Html храню в шаблонах сайта и рендерю их, подставляя переменные, в которых храниться текст (из БД). А про то, что html нужно хранить в БД слышу впервые. Попробуйте хранить в виде хеша (а как вы собираетесь переносить структуру html с их отступами?).
2 мая 2015 в 17:03
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Вопрос очень простой, но в то же время очень характерный.
В нем отлично собраны наиболее популярные заблуждения начинающих пользователей РНР. Попробуем в них разобраться.
Хранение
Самое первое, что надо научиться — это не путать хранение данных и их использование в SQL запросах. Это совершенно разные вещи. По поводу хранения следует понимать, что храниться в БД могут абсолютно любые данные, и при этом храниться они должны как есть. То есть, для хранения их никак, вообще никак обрабатывать не нужно.
Следовательно, отвечая на поставленные в посте (не совсем корректные, как мы видим), вопросы:
- Как хранить какие-либо данные в БД? Данные должны храниться как есть.
- Как обрабатывать данные для БД? Никак. Ни в коем случае никак обрабатывать нельзя.
- Как хранить HTML? Никаких особенных действий ни для HTML, ни для любых других данных производить не нужно. Все данные хранятся абсолютно одинаково.
Использование данных в SQL запросах.
А вот это уже совсем другой вопрос. Единственный, в ответ на который нам надо будет что-то делать. Но при этом сами данные мы всё равно трогать не будем. Да-да! Даже для помещения в SQL запрос мы никак данные обрабатывать не будем. Дело в том, что «совет для записи в базу использовать mysql_real_escape_string()» — это просто феерическая глупость, к сожалению, растиражированная в миллионах экземпляров.
А единственно правильный способ добавления данных в SQL запрос — это делать это через плейсхолдеры.
То есть, чтобы использовать любые данные в SQL запросе, надо сначала на их месте написать знаки вопроса:
INSERT INTO users (name, lastname) VALUES (. )
это, кстати, касается вообще всех запросов. SELECT мы пишем точно так же:
SELECT * FROM users WHERE name=?
после этого надо будет подготовить запрос, а потом выполнить его, передав переменные отдельно. Вот как это происходит на примере PDO:
$stmt = $pdo->prepare("INSERT INTO users (name, lastname) VALUES (. )"); $stmt->execute(array($name, $lastame));
то есть, идея такая: если нам надо подставить в запрос какую-либо переменную, вместо неё надо поставить знак вопроса. А саму переменную передать после.
Таким образом мы будем гарантированы от любых ошибок и неприятностей, поскольку РНР сам обработает за нас все переменные и сделает это правильно.
Спец-символы HTML
Я думаю, что внимательный читатель уже уловил идею: HTML вообще никакого отношения к БД не имеет. Ни малейшего. Это абсолютно разные вещи. То есть пользователю РНР никогда не должна приходить в голову идея использовать функцию, в которой встречается слово «HTML» для какой-либо работы с БД, а при работе с HTML — функцию, в которой встречается слово «mysql».
Функции для работы с HTML следует применять для работы с HTML.
То есть, если мы собираемся выводить HTML текст в HTML, то мы его должны выводить как есть.
Но если мы собираемся выводить в HTML текст, который не является HTML, мы должны отформатировать его так, чтобы он нам случайно не попортил верстку. Обычно для этого используется функция htmlspecialchars()
Кодировка
Если какой-либо символ сохраняется в виде знака вопроса, то это не проблема HTML кодирования, а проблема кодировки, которая попросту не поддерживает данный символ. Чтобы БД могла сохранять символы типа ⇔ , у нее должна быть кодировка utf8mb4 . А также РНР скрипт, устанавливая соединение с базой, должен выставлять кодировку этого соединения в utf8mb4 . При этом отображая пользователю форму, должен выдавать НТТР заголовк Content-type с кодировкой UTF-8 .
И тогда все символы запишутся в целости и сохранности.
Хранение html кода в базе данных — стоит ли?
Заинтересовал один интересный подход к сайтостроению — хранение кода сайта в базе данных. Кто-то умудряется впихнуть в БД едва ли не весь код, кто-то даже картинки хранит в БД, в чём я, конечно, смысла особого не вижу, если только нет ограничений на дисковое пространство на сервере.
В общем, идея моя проста. Сделать несколько файликов для вывода всего творения (скорее всего, вывод будет через единый index.php), а остальное, по крайней мере бОльшую часть html и php кода внести в базу данных. Соответственно, для ввода использовать htmlspecialchars(), для вывода htmlspecialchars_decode() и для вывода php — eval().
Плюсы, на мой взгляд, очевидны: возможность правки всего сайта через простую админку, возможность не замарачиватся с архитектурой и все проблемы подключения блоков решать простым запросом к определённой строке бд, возможность менять шаблоны сайта без загрузки файлов на сервер.
Но насколько такой способ жизнеспособен с точки зрения уязвимости и нагрузки на сервер?
Мои мысли по этому поводу, прошу дополнить или исправить, если я не прав:
— большую нагрузку могут дать скрипты и таблицы стилей, так что все css и js лучше оставить физическими файлами, как и картинки.
-в публичной папке, по сути, можно оставить один лишь индексный файл и скрипты со стилями, а все страницы положить в базу данных.
-скрытую часть сайта с функциями и важными файлами безопаснее будет записать в отдельной бд и тщательно обезопасить все запросы к ней.
-огромным минусом с точки зрения безопасности является то, что злоумышленник, получив доступ к БД, может получить практически все данные сайта, и изменить их на своё усмотрение. При этом изменения будет очень сложно отследить если сайт достаточно объёмный. Выход вижу один: хранить данные сайта и пользователей отдельно от бд с html и php, чтобы можно было создать резервную копию php и html структур в другой базе данных и через независимую админку осуществлять периодическую сверку полей БД на предмет соответствия оригиналу. Админку исполнить можно даже в виде отдельного сайта, что делает невозможным (поправьте, если я не прав) взлом второй БД с резервными копиями. При обнаружении несоответствии кода оригиналу осуществляется автоматическая замена взломанного или случайно испорченного админом кода на оригинал. Но насколько это увеличит нагрузку на сервер.
Отдельно отмечу пароли, хранение которых становится, на мой взгляд, менее безопасным. Можно пойти по методу наименьшего сопротивления и принудительно генерировать пользователю сложные пароли, подмешивая к ним хорошенькую соль. и отправлять их пользователю на почту, чтобы совсем отбить у хакеров желание взламывать этот сайт. Насколько это реально?
В конце концов мы имеем кучу баз данных и один индексный файлик для вывода всего этого, что напоминает архитектуру mvc.
В общем. можно ли построить быстрый и безопасный сайт, способный выдержать большой наплыв пользователей, по такому принципу?
Секционное хранение статьи в базе данных
Здравствуйте. Создаю блог и столкнулся с вопросом хранения публикаций. Публикации разделены на секции, фигуры (картинки с подписями, видео, аудио), списки, . И каждая из секций имеет заголовок, а возле этого заголовка нужно выводить статический системный код (добавление секции в закладки). Я выбирал между 3 вариантами: хранение html кода прямо в строке записи таблицы article — разбор по regex и разбор по DOMDocument; хранение статьи в отдельных таблицах: section FK article, paragraph FK section, figure FK section. Я выбрал последний вариант. Соответственно, я могу как угодно оформлять секции (на данный момент выбрал тег section), что угодно добавлять возле заголовков, добавлять кнопку «читать далее» к последнему абзацу и так далее. То-есть, в базе содержится только важная читабельная информация, а все форматирование возлагается на сторону PHP. 1. Насколько ущербная в плане производительности выборка по данной архитектуре? 2. Что лучше: выборка сначала секций к статье, потом абзацев и фигур к секции отдельными запросами или все одним запросом с последующим FULL OUTER JOIN и группировкой по позиции в статье?
- eTarget 2011:Панельная дискуссия «Стратегия и планирование рекламной кампании в интернете»
- eTarget 2011: Круглый стол «Реклама в онлайн-видео»
- Могут ли «плохие» входящие ссылки привести к ухудшению ранжирования?
На сайте с 17.04.2012
21 октября 2014, 22:50
я так понимаю структура таблиц будет такая: таблица статья, таблица секции, таблица связей вроде
Если да, то проблем с производительностью быть не должно все в рамках привычного использования реляционных бд.
Вы уверены, что секции надо делить на три разные таблицы? Вероятно там сильно много общего наверняка можно обойтись одной. Тогда и проблем с выборкой из трех таблиц не будет, а архитектура предполагающая FULL OUTER JOIN имхо заслуживает того, чтобы ее пересмотрели.
На сайте с 29.05.2008
21 октября 2014, 22:59
iqmaker, почти:
Таблица article (id, uri (адрес статьи), title (название статьи = h1), date-modified, owner (автор)).
— Таблица section (id, article (внешний ключ к article.id) title (название секции = h2), position (позиция объекта относительно начала статьи)).
— Таблица paragraph, figure, . child (объекты секции) — поля индивидуальные, например, для figure это figcaption и ссылка на объект, но у всех есть внешний ключ к section.id и position.
Просто у меня фрустрация после работы с WordPress, DLE. Кругом для форматирования текста используют регулярные выражения, а статью сохраняют в обычном HTML коде.
Но снова таки, HTML это служебные данные и должно создавать множество проблем, начиная от увеличения потребления ОЗУ (я думаю), заканчивая фиктивными результатами поиска скажем по LIKE ‘%
%’ (найдет все абзацы, вместо того, чтобы найти все статьи в которых рассказывается по значении тега p).
О regex говорить не стоит, он просто не предназначен для работы с динамическим HTML контентом. В любом случае, стабильное во всех случаях выражение будет явно менее продуктивнее DOMDocument.
ortegas:
Вы уверены, что секции надо делить на три разные таблицы? Вероятно там сильно много общего наверняка можно обойтись одной. Тогда и проблем с выборкой из трех таблиц не будет, а архитектура предполагающая FULL OUTER JOIN имхо заслуживает того, чтобы ее пересмотрели.
Я в плане проектирования БД newbe. 🙁
Вот вам отрывок кода
Neque porro quisquam est qui dolorem
Suspendisse ipsum risus, ullamcorper nec ultrices id, blandit nec lorem.
Nullam vel convallis leo, aliquam feugiat turpis. In varius tortor mollis faucibus vestibulum.
Mark chaptherNullam vel convallis leo, aliquam feugiat turpis.
В данном случае, figure относится к section, у которого нету title, а в этом случае, section опускается в форматировании.
В БД хранится только название ресурса figure, а полный адрес задает система. В случае же хранения HTML кода, мне бы опять-таки, пришлось бы изощрятся с regex.
Если INSERT я еще могу представить с помощью транзакции, то выборку из 3 таблиц трудно. Тем-более FULL OUTER в MySQL нету. Хотя это именно то, что я хочу получать в ответ.
На сайте с 17.04.2012
22 октября 2014, 10:56
ortegas, понять бы еще, зачем вы хотите выделять из текста по regexp его внутреннюю структуру, зачем она вам? Хочется именно выяснить какую цель приследуете. Вы делаете свою CMS? у которой разработчиком сайта и наполнителем контента будет конечный пользователь?
На сайте с 29.05.2008
22 октября 2014, 11:06
iqmaker, тривиальный пример, вставить в последний абзац аннотации кнопку «Читать далее». Если структуру абзацев не выделить, надо либо regex, либо DOMDocument.
Следующий пример — заголовок секции (h2). Возле него, должна выводится кнопка «Добавить в закладки». Закладка на секции должна запоминаться на стороне сервера.
Третий пример — списки. Возле каждого li, в зависимости от раздела, а именно в разделе с кулинарными рецептами, должны присутствовать checkbox, которые запоминаются на стороне сервера, чтобы пользователь вернувшись, увидел то самое состояние списка.
В случае хранения HTML кода, мне постоянно надо выполнять массу regex. А закладки и состояния списка все-равно нужно хранить привязав к пользователю, только вместо FK, там что я укажу. Номер секции по счету? Так при редактировании статьи, мне надо будет все закладки переписать что ли?
Ну и опять-таки, стараюсь свести все к нормальному состоянию. Я все-таки новичок и лишняя формализация = не лишняя.
На сайте с 17.04.2012
22 октября 2014, 11:18
ortegas, ок становится немного прозрачнее, что не совсем CMS, а некоторый сайт со своей спецификой и внутренними объектами так? Объекты страницы чем отличаются друг от друга?
Что-то мне подсказывает, что можно обойтись структурой которую указал в первом посте, добавив в таблицу Section поле section_type .
Но в целом в вашем варианте нет ничего плохого, если выборка из 3 таблиц тремя запросами это вполне себе ок, для такого варианта сайта в котором несколько типов объектов.
Если уже есть готовая структура базы именно этих таблиц, было бы понятнее
На сайте с 29.05.2008
22 октября 2014, 11:30
iqmaker, в первую очередь, это статьи. Специфика — чисто моя инициатива. Смотрите.
Статья разделена на секции для удобства чтения. Каждую секцию можно добавить в закладки с помощью кнопочки, которая отображается справа от h2 заголовка секции. При последующем открытии статьи с помеченной секцией, пользователь будет сразу же переброшен на часть, где он завершил чтении через #section-#.
В статье могут присутствовать интерактивные списки. Хороший пример — список закупок для приготовления рецепта.
- Майонез.
- Артишоки.
-
Касательно фигур, тут тоже хотелось бы хранить адрес на изображение отдельно. Точнее, внутренний идентификатор изображения. Почему? — Я планирую подключения CloudFire. Соответственно, если я буду хранить фигуры в HTML коде, мне придется переписывать все адреса изображения руками с относительных внутредоменных на CDN.
iqmaker:
Но в целом в вашем варианте нет ничего плохого, если выборка из 3 таблиц тремя запросами это вполне себе ок, для такого варианта сайта в котором несколько типов объектов.И еще вопрос: как в моем случае сохранять позицию объекта относительно статьи? Отдельное поле position для каждого дочериного объекта? А не плохо ли то, что эта position будет зависеть от других соседних объектов с другим типом? Например, figure.position = 1, paragraph.position = 2. Но при этом, 2 не имеет смысла без 1?
На сайте с 17.04.2012
22 октября 2014, 12:13ortegas:
Статья разделена на секции для удобства чтения. Каждую секцию можно добавить в закладки с помощью кнопочки, которая отображается справа от h2 заголовка секции. При последующем открытии статьи с помеченной секцией, пользователь будет сразу же переброшен на часть, где он завершил чтении через #section-#.Добавить в закладки по h2 некоторой статьи можно чисто javascript + cookies, ( пронумеровав их при загрузке страницы тем же javascript ) или хочется на разных устройствах помнить?
- Майонез.
- Артишоки.
В общем если правильно понял расчитываете на зарегистрированного пользователя .. ок ( если нет то тоже js мог справиться )
ortegas:
После последнего абзаца аннотации статьи, должна выводится кнопка «Читать далее». Обычно, ее добавляют с помощью regex к последнему абзацу. Но снова-таки, я считаю, что в моем случае, хранение HTML кода в базе неуместно. Одно дело, когда добавить кнопочку это единственное изменение кода, другое дело, когда нужно динамически выводить статью.Здесь наверное да, если аннотация это вступление к некоторой статье, то возможно статья должна уметь себя показывать как аннотация.
ortegas:
Касательно фигур, тут тоже хотелось бы хранить адрес на изображение отдельно. Точнее, внутренний идентификатор изображения. Почему? — Я планирую подключения CloudFire. Соответственно, если я буду хранить фигуры в HTML коде, мне придется переписывать все адреса изображения руками с относительных внутредоменных на CDN.Если так, то да изображение надо уметь обрабатывать без парсинга собственного сайта.
ortegas:
Хотелось бы одним-двумя запросом, с помощью JOIN. Но производительнее ли этот запрос?Обычно join показывает результат равный или худший нежели два отдельных под запроса этого join, только тест скажет как будет в конкретном случае и какие именно данные будете доставать, но чуда вероятно не случится.
ortegas:
И еще вопрос: как в моем случае сохранять позицию объекта относительно статьи? Отдельное поле position для каждого дочериного объекта? А не плохо ли то, что эта position будет зависеть от других соседних объектов с другим типом? Например, figure.position = 1, paragraph.position = 2. Но при этом, 2 не имеет смысла без 1?Ничего плохого нет, только уверены, что не хотите использовать таблицу связей (там и указывать позицию), ведь некоторую figure и др. объекты данной статьи можно было бы использовать в другой. Почему 2 не имеет смысла без 1? чем меньше тем выше, хоть -100 -10 0 10, какое именно число на мой взгляд не важно, вам же нужно только ранжировать.
ortegas:
Ах да, еще после каждым strong будет кнопка «Согласен». Что-то типа голосования по мере чтения статьи. Обратная связь.
strong дочерний к p. Для него тоже нужно создавать отдельную таблицу или уже можно как-то объединить с параграфом?Наверное лучше использовать общую с параграфом таблицу, если только вы эти strong не начнете использовать в других статьях отдельных от первоначального параграфа.
В целом когда сделаете структуру базы покажите её здесь, думаю все станет гораздо яснее.
Хранение данных, XML, HTML
Этим уроком мы начинаем блок курса, посвященный тому, как в реальном мире хранятся данные. В следующих уроках мы рассмотрим форматы данных XML, JSON и познакомимся с базами данных.
План урока
- Как по-человечески хранить данные
- Формат данных XML
- Чтение XML в Ruby
- Немного про HTML
Хранение данных для программ
Программы работают с информацией, как человек с пищей — создают, готовят, потребляют, перерабатывают, показывают. Для того, чтобы программам (и программистам) было удобнее с ней работать, информацию нужно как-то хранить, в каком-то удобном виде предоставлять пользователям или другими программам.

Информация бывает очень сложная. Нельзя просто взять и записать в всё в один файл, как попало. Чтобы избежать неразберихи, люди договариваются о стандартах: розетки, батарейки, usb-входы. Программисты — тем более люди, они договорились о форматах и способах хранения данных.
Цель любого формата — структурировать данные, чтобы с ними было проще эффективнее работать: чтобы сразу было понятно, где файл начинается, где хранится служебная информация, как в нём упакованы данные. Форматов и способов хранения данных много, в нашем курсе мы познакомимся с самыми актуальными: XML, JSON и базы данных.
Формат XML
XML — это способ представить любую информацию в виде древовидной структуры. В жизни очень часто человек организует информацию именно в виде дерева. Давайте представим, как могла бы быть в виде XML упакована информация из вашего аккаунта в какой-нибудь социальной сети:
Гоша Петя Типичный XML файл начинается с заголовка, в котором указывается версия XML и кодировка файла . Дальше идёт дерево так называемых тегов. Корень у дерева один. Так и в XML-файле корневой тег-контейнер (оборачивающий всё остальное) — один. В нашем случае .
Теги записываются в треугольных скобках и бывают открывающие (
) и закрывающие, в начале названия которых стоит слеш ( ). Если какой-то элемент XML-структуры состоит из двух таких тегов, то он называется контейнером, т.к. в нём могут находиться другие элементы:Если же у тега нет закрывающего, то такой тег содержит слеш в самом конце: ( ).
Ещё у тегов бывают атрибуты, а у атрибутов — значения. Пары атрибут-значение записываются с помощью знака равно: ( )
Также тег-контейнер может содержать внутри себя кроме других тегов просто обычный текст.
Я текст внутри тега "some_tag" Вот вкратце и всё, что вам сейчас нужно знать про формат хранения данных XML.
Какие именно теги будут в вашей структуре — уже определяете вы, XML лишь накладывает требования на структуру, которые мы описали выше. Скажем, нельзя открыть тег и не закрыть его, нельзя оставить открой треугольную скобку вот так
XML настолько популярен, что многие программы в нем хранят данные. Например, популярный редактор текста Microsoft Word: его файлы *.docx по сути являются zip-архивами, если такой файл переименовать, поменяв расширение на .zip , то можно залезть внутрь такого архива и посмотреть, какие xml-файлы там лежат.
Проектируем программу для учета расходов
Давайте напишем программу, которая будет показывать, сколько на что мы потратили, а также выводить сумму всех трат за каждый месяц. Для этого нам как-то надо хранить данные о покупках.
Для начала давайте придумаем структуру, в которой мы будем описывать информацию о тратах. Этот XML-файл пока будем редактировать руками, а на следующем уроке научимся делать это с помощью программы.
expenses.xml :
Книжка по Ruby on Rails Петличка и провода Софтбокс + штатив Вот так мы будем хранить эти данные. Как вы видите, у нас есть корневой тег expenses , в него вложены теги expense (по одному на каждую трату), внутри каждого такого тега, текст с информацией о покупке, а в атрибутах date , amount и category соответственно дата, сумма и категория покупки.
Как читать формат XML в ruby
Для того, чтобы удобно работать с XML-файлами во всех современных языках есть парсеры. Парсер — это инструмент (чаще всего библиотека) для чтения данных из текста и представления их в удобной для работы с ними форме. Обычно их в каждом языке даже несколько. Какие-то быстрее, какие-то удобнее. Один из таких парсеров (REXML) уже встроен в Ruby (начиная с версии 1.9). Им и воспользуемся.
Программа для учёта расходов
Сейчас мы напишем программу, которая будет идти по всем тратам и добавлять их в ассоциативный массив, в котором в виде ключа будут даты: то есть все траты, сделанные в один и тот же день, попадут в один и тот же элемент.

Затем мы пройдёмся по всем дням и объединим их в месяцы:


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

Давайте писать программу!
expenses_reader.rb :
require "rexml/document" # подключаем парсер require "date" # будем использовать операции с датами current_path = File.dirname(__FILE__) file_name = current_path + "/my_expenses.xml" # UNLESS в руби - противоположный по смыслу оператору IF # прерываем выполнение программы досрочно, если конечно файл не существует. abort "Извиняемся, хозяин, файлик my_expenses.xml не найден." unless File.exist?(file_name) file = File.new(file_name) # открыли файл doc = REXML::Document.new(file) # создаем новый документ REXML, построенный из открытого XML файла amount_by_day = Hash.new # пустой асс. массив, куда сложим все траты по дням # выбираем из элементов документа все тэги внутри # и в цикле проходимся по ним doc.elements.each("expenses/expense") do |item| # (Обратите внимание это локальная переменная объявленая в теле цикла, для каждой итерации # создается новая такая. За пределами цикла она не видна.) loss_sum = item.attributes["amount"].to_i # сколько потратили loss_date = Date.parse(item.attributes["date"]) # когда. Date.parse(. ) создает из строки объект Date # иницилизируем нулем значение хэша, если этой даты еще не было amount_by_day[loss_date] ||= 0 # в руби "a ||= b" эквивалентно "if a == nil a = b" # добавили трату за этот день amount_by_day[loss_date] += loss_sum end file.close # сделаем хэш, в который соберем сумму расходов за каждый месяц sum_by_month = Hash.new # в цикле по всем датам хэша amount_by_day накопим в хэше sum_by_month # значения потраченных сумм каждого дня amount_by_day.keys.sort.each do |key| # key.strftime("%B %Y") вернет одинаковую строку для всех дней одного месяца # поэтому можем использовать ее как уникальный для каждого месяца ключ sum_by_month[key.strftime("%B %Y")] ||= 0 sum_by_month[key.strftime("%B %Y")] += amount_by_day[key] # приплюсовываем к тому что было сумму следующего дня end # пришло время выводить статистику на экран # в цикле пройдемся по всем месяцам и начнем с первого current_month = amount_by_day.keys.sort[0].strftime("%B %Y") # выводим заголовок для первого месяца puts "------[ #, всего потрачено: # р. ]--------" # цикл по всем дням amount_by_day.keys.sort.each do |key| # если текущий день принадлежит уже другому месяцу. if key.strftime("%B %Y") != current_month # то значит мы перешли на новый месяц и теперь он станет текущим current_month = key.strftime("%B %Y") # выводим заголовок для нового текущего месяца puts "------[ #, всего потрачено: # р. ]--------" end # выводим расходы за конкретный день puts "\t#: # р." endНемного об HTML
HTML — грубо говоря, одна из разновидностей XML. Требования к разметке в некоторых версиях HTML, однако (а версий HTML много, сейчас, например, самая актуальная HTML5), могут существенно отличаться о того, что мы рассказали про XML.
Когда браузер заходит на какой-то сайт, он отправляет запрос и в качестве ответа получает в том числе текст в формате html. Браузер его обрабатывает и превращает в текст (также как Word превращает xml в те файлы, с которыми мы привыкли работать).

Давайте зайдём на сайт rubyrush.ru и посмотрим его html-разметку. Для этого нажмите на любой неактивный элемент на сайте правой кнопкой мыши и выберите пункт «Исходный код страницы», или просто нажмите Ctrl+U .

Те блоки гипертекста, которые вы видите, вам должны показаться знакомыми — тоже тэги, вложенные друг в друга, атрибуты и так далее. Нам это знание пригодится в последующих уроках, когда будем работать с сетью.
Итак, мы узнали о формате XML, научились читать данные из XML-файлов и узнали немного про HTML. На следующем уроке мы научимся писать свои XML файлы и сделаем так, чтобы нам не приходилось добавлять траты в файл expenses.xml руками.
