PHP и UTF-8: пятый с половиной этап
После последнего, на данный момент, пятого этапа, меня опять потянуло на нецелые числа. Что поделать — я работаю над другим проектом, поэтому на полноценный этап я ничего не наскрёб, но работу потихоньку продолжаю. В прошлый раз я занимался оптимизацией — сделал итератор по UTF-8 строке.
<?
class UtfReverseString implements Iterator
{
protected $str; // строка
protected $idx; // бинарный индекс строки
protected $pos; // UTF-8-индекс
protected $curend; // бинарный указатель на конец текущего символа
protected $idx_start; // бинарный индекс откуда стартуем
protected $pos_start; // UTF-8-индекс откуда стартуем
// проверка — байт не является началом символа
protected function isNotBegin($ch)
{
return (ord($ch) & 192) == 128;
}
public function __construct($str, $pos_start = 0)
{
$len = strlen($str);
$this->str = $str;
$this->idx = strlen($str); // этот указатель будет уточнён тут же, ниже
$this->pos = strlen(utf8_decode($str)) - 1 - $pos_start; // лучше использовать mb_strlen, если есть возможность
while ($pos_start-- >= 0) {
$this->curend = --$this->idx;
while ($this->idx > 0 && $this->isNotBegin($this->str[$this->idx])) {
$this->idx--;
}
}
$this->idx_start = $this->idx;
$this->pos_start = $this->pos;
}
public function current()
{
return substr($this->str, $this->idx, $this->curend - $this->idx + 1);
}
public function key()
{
return $this->pos;
}
public function next()
{
$this->pos--;
$this->curend = $this->idx - 1;
for ($this->idx--; $this->isNotBegin($this->str[$this->idx]); $this->idx--);
}
public function rewind()
{
$this->idx = $this->idx_start;
$this->pos = $this->pos_start;
}
public function valid()
{
return $this->idx >= 0;
}
public function __toString()
{
return $this->str;
}
}
Использовать так же просто, как и предыдущий, «прямой» итератор:
$str = new UtfReverseString('Привет', 1);
foreach ($str as $ch) {
echo $ch, ' ';
}
Второй параметр указывает с какого символа (с конца) нужно начинать итерироваться.