Писать код не сложно. Сложно писать код так, чтобы его было легко читать и поддерживать. К сожалению, не всегда понятно, как лучше организовать код. Бывает, что при чтении кода спотыкаешься и долго не можешь понять, что же здесь не так. Продолжить чтение →
Категория / Программирование
Технологии работы с информацией. Реляционные базы данных, примеры работы с SQL. Парсинг, дата майнинг, извлечение знаний.
Как расшифровать текст с кракозябрами вместо русских букв
Время от времени приходится сталкиваться с текстом в непонятной кодировке, типа такого:
Ëþäè â Ãðîóâëåíäå, ìàëåíüêîì (ïî ìåðêàì Êàëèôîðíèè) ãîðîäêå â øåñòüñîò æèòåëåé, âûõîäèëè íà óëèöû, ñòîÿëè ïåðåä ñâîèìè äîìàìè ñ öâåòàìè íà ïîäîêîííèêàõ è ñìîòðåëè, êàê ýòîò ïèðîêóìóëþñ âûðàñòàåò âûøå Ñüåððû-Íåâàäû. ß è ñàìà ñòîÿëà òàì â áëàãîãîâåíèè è óæàñå è ïîíèìàëà áåç âñÿêèõ ñëîâ, ÷òî åñëè íå ïîéäåò äîæäü, òî ñëåäóþùèå ïîæàðû áóäóò åù¸ óæàñíåå, à åñëè äîæäè âñ¸ æå ïîéäóò è îêàæóòñÿ ñëèøêîì îáèëüíûìè, òî ýòî ñîææ¸ííûå ãîðíûå ñêëîíû ñìîåò íàâîäíåíèÿìè. Âñ¸ áûëî áóêâàëüíî íà ãðàíè êàòàñòðîôû. Íî ðÿäîì áûëè öâåòû â ãîðøêàõ, è íåîáîææ¸ííûå ñîñíû, è òðóùèåñÿ î íîãè ñîáàêè, è ðåñòîðàí, îòêðûòûé äëÿ óæèíà; è ÷óâñòâîâàëîñü, ÷òî âñå íà óëèöå âçäûõàþò ñ áëàãîäàðíîñòüþ çà òî, ÷òî âñ¸ ýòî ó íèõ åù¸ åñòü. Õîòÿ áû íåíàäîëãî (Äèàíà Ìàðêóì, Äåñÿòûé îñòðîâ).
Если текст маленький, то поможет онлайн-декодер, а если текст большой — поможет текстовый редактор. Я использую Sublime Text, подозреваю что этот трюк возможен и в других текстовых редакторах, которые умеют работать с разными кодировками.
В любом случае, начните с онлайн-декодера, чтобы понять с какими кодировками нужно работать. Раскодируйте фрагмент текста и запомните направление декодирования (в нашем случае: ISO-8859-1 → Windows-1251):

Вся дальнейшая работа будет проходить в текстовом редакторе.
Шаг 1. Создайте обычный пустой текстовый файл, сохраните его с кодировкой Western (ISO 8859-1):

Шаг 2. Скопируйте текст с кракозябрами и вставьте его в созданный файл:

Шаг 3. Сохраните файл (Ctrl + S).
Шаг 4. Откройте файл с кодировкой Cyrillic (Windows 1251):

Шаг 5. Наслаждайтесь результатом:

MongoDB: The ‘cursor’ option is required
При использовании агрегации через устаревший драйвер MongoDB без указания опций возникает ошибка
Throwable; type: [MongoResultException]; code: [9]; message: [localhost:27017: The ‘cursor’ option is required, except for aggregate with the explain argument]; err level: [1 (E_ERROR)]
Внятного описания в документации, что именно должно быть передано в параметре cursor, нет. Так как драйвер ждёт объект, избавиться от ошибки можно, передав в параметрах метода aggregate() в опции cursor пустой объект:
$pipeline = array(array(
'$group' => array(
'_id' => null,
'min' => array('$min' => '$update_time'),
'max' => array('$max' => '$update_time'),
),
));
$options = array('cursor' => new stdClass());
$res = $MongoCollection->aggregate($pipeline, $options);
Пример выше показывает, как выбрать из коллекции минимальную и максимальную запись.
MongoDB: как разделить поле _id после группировки
Группировку данных в MongoDB можно делать как по одному полю, так и по нескольким, превращая их во вложенный документ:
db.my_collection.aggregate([
{$group: {
_id: {surname: "$surname", name: "$name"}
}},
]);
Этот запрос вернёт такой результат:
{_id: {name: "Аттикус", surname: "Финч"}}
{_id: {name: "Индиана", surname: "Джонс"}}
{_id: {name: "Джеймс", surname: "Бонд"}}
{_id: {name: "Рик", surname: "Блейн"}}
{_id: {name: "Уилл", surname: "Кейн"}}
А вложенные документы поднять на уровень выше, нужна проекция:
db.my_collection.aggregate([
{$group: {
_id: {surname: "$surname", name: "$name"}
}},
{$project: {_id: 0, surname: "$_id.surname", name: "$_id.name"}},
{$sort: {surname: 1, name: 1}}
]);
Этот запрос вернёт данные в таком виде:
{name: "Рик", surname: "Блейн"}
{name: "Джеймс", surname: "Бонд"}
{name: "Индиана", surname: "Джонс"}
{name: "Уилл", surname: "Кейн"}
{name: "Аттикус", surname: "Финч"}
PHP: Detected an illegal character in input string
При преобразовании строки в требуемую кодировку к кодировке, требуемой на выходе можно добавить параметр TRANSLIT или IGNORE. Первый включает режим транслитерации и заменяет проблемный символ на один или несколько наиболее близких по внешнему виду. Ключевое слово — заменяет. Возможно, наиболее близким символом будет знак вопроса, но тем не менее. Второй параметр удаляет те символы, которые не могут быть представлены в требуемой кодировке.
В комментариях к документации один из пользователей упомянул про символ, который не получается проигнорировать — на нём даже с параметром IGNORE iconv падает с ошибкой «Detected an illegal character in input string». И поэтому появилась рекомендация использовать одновременно два параметра: //TRANSLIT//IGNORE — типа, игнорировать всё, что не удалось транслитерировать. Однако, повторюсь, параметр TRANSLIT заменяет все проблемные символы, после него уже нечего игнорировать. В итоге получаем такую картину:
// работает
iconv('utf-8', 'windows-1251//TRANSLIT', '∙');
iconv('utf-8', 'windows-1251//TRANSLIT//IGNORE', '∙');
// падает с ошибкой Detected an illegal character in input string
iconv('utf-8', 'windows-1251//IGNORE', '∙');
iconv('utf-8', 'windows-1251//IGNORE//TRANSLIT', '∙');
В итоге имеем бессмысленные комбинации //TRANSLIT//IGNORE и //IGNORE//TRANSLIT, которые встречаются в ответах на SO.
Python: проверка на прерывание цикла
В Python у циклов while и for есть опциональный блок else, который проверяет, выполнился ли цикл полностью. Если ключевое слово break не было вызвано, будет выполнен блок else.
while… else:
numbers = [1, 3, 5]
position = 0
while position < len(numbers):
number = numbers[position]
if number % 2 == 0:
print('Найдено четное число', number)
break
position += 1
else:
print('Четное число не найдено')
for… else:
numbers = [1, 3, 5]
for number in numbers:
if number % 2 == 0:
print('Найдено четное число', number)
break
else:
print('Четное число не найдено')
Подобная конструкция позволяет выполнить некоторое действие при полном завершении работы с циклом (вывод сообщения здесь — явный флаг штатного завершения обхода цикла):
for name in ['Алиса', 'Боб']:
print(name)
else:
print('Обход цикла завершен')
Предел прочности
Прекрасное о Дамбе Баньцяо в Википедии:
Дамба была сконструирована таким образом, чтобы пережить крупнейшие наводнения, которые случаются раз в тысячу лет (306 мм осадков за день). Однако в августе 1975 года произошло крупнейшее за 2000 лет наводнение…
Когда делаешь код-ревью своего же кода
Начало: 5:24
PHP: Проверка строк, содержащих числа
Если на входе есть строка, которая может содержать целое число или число с плавающей точкой и нужно эту строку привести к нужному состоянию, то можно сделать так:
if (is_numeric($string)) {
if ((int)$string == $string) {
$string = (int)$string;
} else {
$string = (float)$string;
}
}
Функция is_numeric проверяет, является ли переменная числом или строкой, содержащей число (ни is_int, ни is_float не делают вторую часть проверки и при получении строки на входе возвращают false). Далее приводим строку к целому числу и делаем нестрогое сравнение, чтобы убедиться, что перед нами действительно целое число.
Стили написания составных слов
- kebab-case (lisp-case) — шашлычный-регистр, позвоночный-регистр
- snake_case — змеиный_регистр
- SCREAMING_SNAKE_CASE
- Train-Case
- CamelCase (UpperCamelCase, PascalCase) — ВерблюжийРегистр, ГорбатыйРегистр, СтильВерблюда
- lowerCamelCase