Как найти количество байтов в строке UTF-8 с PHP? - PullRequest
6 голосов
/ 05 марта 2010

У меня есть следующая функция с сайта 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)

Ответы [ 5 ]

4 голосов
/ 05 марта 2010

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

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

2 голосов
/ 05 марта 2010

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

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

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

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

1 голос
/ 05 марта 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 возвращать количество символов, а не количество байтов

1 голос
/ 05 марта 2010

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

mb_strlen($utf8_string, 'latin1');
0 голосов
/ 05 марта 2010

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

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

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

...