Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Погружаясь в строки Основы PHP

Юникод

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

В самом начале была ровно одна кодировка — ASCII, основанная на английском алфавите. В этой кодировке одному символу соответствует 7 бит, всего в ней закодировано 128 символов. 95 из них печатные, они включают в себя буквы алфавита в верхнем и нижнем регистрах, цифры и знаки препинания, а также 33 непечатных символа или так называемых управляющих кодов. Большинство из них сейчас не актуальны, но некоторые, например, перевод строки \n по-прежнему используются. Например, символ i в нижнем регистре соответствует двоичному числу 1101001, что соответствует числу 105 в десятичной системе счисления.

Поначалу все было хорошо, но с распространением компьютеров возникла потребность в других алфавитах. Каждая страна решала данную проблему созданием собственной кодировки, большинство из которых совместимы с ASCII. То есть первые 128 номеров полностью соответствовали ASCII, а вот остальные 128 заполнялись локальным алфавитом. 128 + 128 = 256, а это 2 в 8 степени. Эти кодировки были однобайтовыми (для хранения одного символа требовался один байт). Внезапно открылись врата ада. Попытка открыть в редакторе файл в другой кодировке, приводила к появлению крякозябр: Øèðîêàÿ ýëåêòðèôèêàöèÿ þæíûõ ãóáåðíèé äàñò ìîùíûé òîë÷îê ïîäú¸ìó ñåëüñêîãî õîçÿéñòâà. Возникают они потому, что один и тот же код в разных кодировках соответствует совершенно разным символам, за исключением первых 128. Поэтому текст, использующий английские буквы всегда читался, а в остальном как повезет. Ситуация усугублялась тем, что даже в рамках одного алфавита создавалось множество разных кодировок, например: Windows-1252, KOI8-R, CP 866, ISO 8859-5.

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

Разные кодировки стали причиной постоянных проблем при взаимодействии людей и программ. Особенно остро эта проблема проявилась с развитием интернета. Такая ситуация не могла продолжаться бесконечно, и в конце концов был создан стандарт Юникод (Unicode). На данный момент он содержит более 100 тысяч символов и включает в себя все существующие (и даже мертвые) языки. Стандарт Юникод не является кодировкой и ничего не говорит о том, как должны храниться символы в памяти, он лишь определяет связь между символом и некоторым числом. Конкретный способ кодирования юникода определяется соответствующими кодировками, среди которых UTF-8, UTF-16 и некоторые другие. В этих кодировках для хранения одного символа уже недостаточно одного байта, они используют больше. UTF-8 ведет себя хитрее: для символов английского алфавита (и некоторых других) используется один байт, для других алфавитов — 2.

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

<?php

echo strlen('Привет!'); // => 13

https://replit.com/@hexlet/php-basics-strings-deeper-strlen

Языки разделились на два лагеря. Некоторые встроили поддержку в уже существующие функции и переход на UTF-8 никак не отразился на процессе программирования. Среди них Java, Ruby, JavaScript. А вот PHP пошел своим особенным путем. Для работы с многобайтовыми кодировками в язык добавили отдельное расширение по работе с многобайтовыми строками, которое по большей части добавляет множество функций для работы со строками, с той лишь разницей, что каждая функция из них имеет префикс mb_ (multibyte).

<?php

echo mb_strlen('Привет!'); // => 7

https://replit.com/@hexlet/php-strings-deeper-mbstrlen

А вот достойной альтернативы взятию конкретного символа в строке по индексу не существует. Такую задачу нужно выполнять с помощью функции mb_substr().

<?php

$str = 'Привет';
$symbol = mb_substr($str, 2, 1); // и

https://replit.com/@hexlet/php-basics-strings-deeper-mbsubstr

Локализация

Глобализация проявляется во многих аспектах, например, единой системе мер: время, размеры и расстояния, температуры, даты и многое другое. Конечно, есть страны которые выпадают из общего потока, например, США, чуть ли не единственная страна, в которой сохраняется и превалирует английская система мер. Она использует мили вместо километров, фунты вместо килограммов. Во многих восточных странах по-другому устроены календари, и где-то сейчас идет четырехтысячный год. Но в целом все унифицировано.

Несмотря на эту унификацию, в каждой стране есть свои особенности по способу записи, сравнению и другим аспектам. Например, в США принято в датах первым ставить месяц, а затем уже день. В некоторых странах для вывода вещественных чисел используют точку, а в некоторых — запятую: 1,234. Эти особенности не обязательно привязаны к целой стране. Внутри одной страны может быть множество дополнительных разделений. Каждый такой набор особенностей называют локалью, а процесс адаптации к особенностям конкретной локали — локализацией.

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

В PHP для управления локалью используется функция setlocale(). Эта функция настраивает локаль глобально. То есть ее вызов влияет на все последующие функции, опирающиеся на локализацию.

setlocale(int $category, string $locales, string ...$rest): string|false

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

  • LC_ALL — все нижеперечисленное
  • LC_COLLATE — функции сравнения строк (см. strcoll())
  • LC_CTYPE — функции преобразования и классификации строк, например strtoupper()
  • LC_MONETARY — для функции localeconv()
  • LC_NUMERIC — задает символ десятичного разделения (см. также localeconv())
  • LC_TIME — форматирование даты/времени функцией strftime()

Пример того, как влияет установленная локаль на отображение данных:

<?php

print_r(1.234); // => 1.234
setlocale(LC_NUMERIC, 'ru_RU');
print_r(1.234); // => 1,234

Если вторым аргументом передать 0 вместо локали: setlocale(LC_ALL, 0), то функция будет возвращать текущую установленную локаль для категории.

Поиск стартовой позиции

Одна из частых задач при работе со строками — определить, является ли одна строка частью (подстрокой) другой строки. В большинстве языков программирования есть встроенная функция, предназначенная именно для этой задачи. В PHP такая функция появилась лишь в PHP 8. До этого ее имитировали с помощью функции mb_strpos(). Эта функция ищет позицию первого вхождения одной строки в другую и мы пока будем использовать именно mb_strpos().

<?php

if (mb_strpos('Валар Моргулис', 'Моргулис')) { // 6
    // ...
}

В примере выше функция вернет 6, что соответствует индексу буквы М.

<?php

if (mb_strpos('Валар Моргулис', 'Валар')) { // 0
    // ...
}

А в этом примере вернется 0, что соответствует первой букве строки. В этом коде кроется ошибка, которую часто допускают новички. В PHP 0 рассматривается, как false, а значит, условие не выполнится. Правильная проверка выглядит так:

<?php

if (mb_strpos('Валар Моргулис', 'Валар') !== false) {
    // ...
}

Функция mb_strpos() возвращает false если подстрока не была найдена, а значит можно воспользоваться строгим сравнением с false.


Дополнительные материалы

  1. Что такое кодировки?

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы PHP-разработчик
Профессия
с нуля
Разработка веб-приложений на Laravel
1 декабря 10 месяцев

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и соглашаетесь с «Условиями использования»