Как найти количество байтов в строке UTF-8 с помощью PHP?

У меня есть следующая функция с сайта php.net для определения количества байтов в строке ASCII и UTF-8:

<?php 
/** 
 * Count the number of bytes of a given string. 
 * Input string is expected to be ASCII or UTF-8 encoded. 
 * Warning: the function doesn't return the number of chars 
 * in the string, but the number of bytes. 
 * 
 * @param string $str The string to compute number of bytes 
 * 
 * @return The length in bytes of the given string. 
 */ 
function strBytes($str) 
{ 
  // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT 

  // Number of characters in string 
  $strlen_var = strlen($str); 

  // string bytes counter 
  $d = 0; 

 /* 
  * Iterate over every character in the string, 
  * escaping with a slash or encoding to UTF-8 where necessary 
  */ 
  for ($c = 0; $c < $strlen_var; ++$c) { 

      $ord_var_c = ord($str{$d}); 

      switch (true) { 
          case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): 
              // characters U-00000000 - U-0000007F (same as ASCII) 
              $d++; 
              break; 

          case (($ord_var_c & 0xE0) == 0xC0): 
              // characters U-00000080 - U-000007FF, mask 110XXXXX 
              // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
              $d+=2; 
              break; 

          case (($ord_var_c & 0xF0) == 0xE0): 
              // characters U-00000800 - U-0000FFFF, mask 1110XXXX 
              // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
              $d+=3; 
              break; 

          case (($ord_var_c & 0xF8) == 0xF0): 
              // characters U-00010000 - U-001FFFFF, mask 11110XXX 
              // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
              $d+=4; 
              break; 

          case (($ord_var_c & 0xFC) == 0xF8): 
              // characters U-00200000 - U-03FFFFFF, mask 111110XX 
              // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
              $d+=5; 
              break; 

          case (($ord_var_c & 0xFE) == 0xFC): 
              // characters U-04000000 - U-7FFFFFFF, mask 1111110X 
              // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 
              $d+=6; 
              break; 
          default: 
            $d++;    
      } 
  } 

  return $d; 
} 
?> 

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

Оператор switch использует условие по умолчанию. Любые идеи, почему русские символы не будут работать должным образом? Или были бы лучшие варианты для этого.

Я спрашиваю об этом, так как мне нужно сократить строку UTF-8 до определенного количества байтов. т.е. я могу отправить только макс. 169 байт данных JSON для iPhone APNS в моей ситуации (исключая другие пакетные данные).

Ссылка: PHP strlen — Руководство (Комментарий Паоло от 10 января 2007 г., 03:58)


person Luke    schedule 05.03.2010    source источник
comment
переключатель (правда) ? Это странный способ делать вещи..   -  person Brendan Long    schedule 05.03.2010
comment
Функция из комментария в ссылке внизу поста. Я не кодировал :) Однако похоже, что это правильный процесс, а не использование mb_strlen, за исключением того, что русские символы не работают.   -  person Luke    schedule 05.03.2010
comment
@ Брендан, я только что подумал о том же.   -  person alex    schedule 05.03.2010
comment
@BrendanLong Что странного в switch(true)?   -  person PeeHaa    schedule 20.10.2012
comment
@PeeHaa Это просто очень странный способ написать if ... else if ... else.   -  person Brendan Long    schedule 20.10.2012
comment
Это? Imho, если у вас есть несколько elseif, вы должны использовать switch(), когда это возможно, как в OP. Может быть, это только я. :)   -  person PeeHaa    schedule 20.10.2012
comment
do { if (1) {break;} } while (false); мухаха ;) также есть goto gg   -  person hakre    schedule 20.10.2012


Ответы (5)


Я спрашиваю об этом, так как мне нужно сократить строку utf-8 до определенного количества байтов.

mb_strcut() делает именно это, хотя вы, возможно, не сможете сказать, из малопонятной документации.

person Michael Borgwardt    schedule 05.03.2010
comment
Спасибо, использование mb_strcut() лучше, чем mb_substr() для моей ситуации. - person Luke; 05.03.2010

strlen() возвращает количество байтов.

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

Еще одна вещь, которую вам нужно обработать, это то, что когда вы помещаете строку в нотацию json, может потребоваться больше байтов, чтобы представить ее как json. Например, если ваша строка содержит символ двойной кавычки. Его нужно экранировать, а символ обратной косой черты добавит один байт. Есть и другие символы, которых тоже нужно экранировать. Дело в том, что он может стать больше. Я предполагаю, что ограничение в байтах относится к общей полезной нагрузке json, поэтому вам необходимо учитывать сам синтаксис json, а также любое экранирование, которое json наложит на вашу строку.

Неоптимизированный, довольно хакерский способ сделать это - отрезать строку, скажем, на 5 байтов больше вашего предела, используя substr(). Теперь используйте mb_strlen() для получения количества символов и mb_substr() для удаления последнего символа. Теперь закодируйте его как json и измерьте байты через strlen(). Введите цикл, который продолжает отсекать последний символ с помощью mb_substr(), кодирует как json и снова измеряет байты с помощью strlen(). Цикл завершается, когда число байтов становится приемлемым.

person goat    schedule 05.03.2010
comment
У меня уже есть цикл while, который продолжает обрезать по 1 символу за раз, используя mb_substr, пока количество байтов не упадет ниже предела. strlen, похоже, не возвращает то же количество байтов, что и функция в моем вопросе. strlen() может быть перегружен или не перегружен mb_strlen() в соответствии с другими комментариями, поэтому на него не следует полагаться. - person Luke; 05.03.2010
comment
Так что не перегружайте strlen. Если вы не контролируете это, то есть другие способы. Например, while (isset($str[$i])) $i++; будет делать свое дело. Или fwrite() в поток или что-то в этом роде... - person goat; 05.03.2010

В PHP 5 функция mb_strlen должна возвращать количество символов ; и strlen должен возвращать количество байтов.

Например, эта часть кода:

$string = 'По своей природе компьютеры могут работать лишь с числами. И для того, чтобы они могли хранить в памяти буквы или другие символы, каждому такому символу должно быть поставлено в соответствие число';
echo mb_strlen($string, 'UTF-8') . '<br />';
echo strlen($string);

Должен получиться следующий результат:

196
359


В качестве примечания: это одна из вещей, которые изменится в PHP 6: PHP 6 будет использовать Unicode по умолчанию, что означает, что strlen в PHP 6 должен возвращать количество символов, а не количество байтов. .

person Pascal MARTIN    schedule 05.03.2010
comment
Даже с PHP5 это не предположение, которое вы можете сделать. strlen() может быть перегружен функцией mb_strlen(), а может и не быть. Безопаснее просто вызвать mb_strlen($string, 'latin1'); - person Xorlev; 05.03.2010
comment
Функция, которую я предоставил в вопросе, отлично работает для utf-8. Я считаю, что проблема с моей проблемой находится где-то еще в коде iPhone PUSH APNS. Кажется, я могу НАЖАТЬ около 160 байт японского, английского текста и т. д. Однако я могу НАЖАТЬ только около 110 байт кириллических (русских) символов. - person Luke; 05.03.2010
comment
Я по-прежнему считаю, что нельзя полагаться на strlen и mb_strlen для определения фактических байтов. - person Luke; 05.03.2010
comment
PHP 6? PHP 6? Маловероятно, что PHP когда-либо будет использовать Unicode по умолчанию. - person David Spector; 13.01.2020

Если вы хотите найти длину многобайтовой строки в байтах при использовании mbstring.func_overload 2 и строки UTF-8, то вы можете использовать следующее:

mb_strlen($utf8_string, 'latin1');
person Phil Rykoff    schedule 05.03.2010
comment
Разве это не дает длину строки в количестве символов? Мне нужно знать фактическое количество байтов, которые используются. В utf-8 символ может быть больше одного байта, верно? - person Luke; 05.03.2010
comment
согласно разделу комментариев php.net/manual/en/function.mb- strlen.php (в самом низу), общепризнано, что эта функция, вызванная описанным способом, будет считать БАЙТЫ. когда вы сообщаете функции, что ваша входная строка содержит символы latin1 (ergo: ascii), он может считать каждый байт как символ, хотя это может быть недопустимый символ в смысле ascii. не могли бы вы попробовать это? у меня, к сожалению, нет среды с поддержкой mb... - person Phil Rykoff; 05.03.2010

Количество байтов ‹> Длина строки!

чтобы получить количество байтов, вы можете использовать (php4,5) strlen. чтобы получить длину строки unicode (в кодировке utf8), вы можете использовать mb_strlen (позаботьтесь о перегрузке функций из этого расширения) или вы можете просто подсчитать все байты, для которых не установлен 8-й бит.

8-й бит означает, что для этого unicodechar приходит как минимум еще один байт из ввода.

person Bernd Ott    schedule 05.03.2010