Многобайтовый безопасный подсчет различных символов в строке

#php #string #utf-8

#php #строка #utf-8

Вопрос:

Я не хочу находить умный и эффективный способ подсчета количества разных альфа-символов в одной строке. Пример:

 $str = "APPLE";
echo char_count($str) // should return 4, because APPLE has 4 different chars 'A', 'P', 'L' and 'E'

$str = "BOB AND BOB"; // should return 5 ('B', 'O', 'A', 'N', 'D'). 

$str = 'PLÁTANO'; // should return 7 ('P', 'L', 'Á', 'T', 'A', 'N', 'O')
  

Он должен поддерживать строки UTF-8!

Комментарии:

1. Подумайте о том, чтобы превратить его в массив символов (возможно, выбрасывая пробелы), а затем «унифицировать» массив. Если нет каких-либо требований к производительности (и анализ производительности показывает, что они не выполняются), тогда это довольно разумно и достаточно эффективно.

2. Какую кодировку кодировки будут иметь входные данные? UTF-8?

3. Да, UTF-8 символов. Забыл добавить. Я изменил свой исходный пост.

Ответ №1:

Если вы имеете дело с UTF-8 (что вам действительно следует учитывать, имхо), ни одно из опубликованных решений (с использованием strlen, str_split или count_chars) не будет работать, поскольку все они обрабатывают один байт как один символ (что, очевидно, неверно для UTF-8).

 <?php

$treat_spaces_as_chars = true;
// contains hälöwrd and a space, being 8 distinct characters (7 without the space)
$string = "hällö wörld"; 
// remove spaces if we don't want to count them
if (!$treat_spaces_as_chars) {
  $string = preg_replace('/s /u', '', $string);
}
// split into characters (not bytes, like explode() or str_split() would)
$characters = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
// throw out the duplicates
$unique_characters = array_unique($characters);
// count what's left
$numer_of_characters = count($unique_characters);
  

Если вы хотите удалить все символы, не являющиеся словами:

 <?php

$ignore_non_word_characters = true;
// contains hälöwrd and PIE, as this is treated as a word character (Greek)
$string = "h,ä* l•π‘°’lö wörld"; 
// remove spaces if we don't want to count them
if ($ignore_non_word_characters) {
  $string = preg_replace('/W /u', '', $string);
}
// split into characters (not bytes, like explode() or str_split() would)
$characters = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
// throw out the duplicates
$unique_characters = array_unique($characters);
// count what's left
$numer_of_characters = count($unique_characters);

var_dump($characters, $unique_characters, $numer_of_characters);
  

Комментарии:

1. Правильно, и я имею дело с UTF-8! Я отредактировал свой первоначальный вопрос.

2. Это решение работает, событие со строками UTF-8. Спасибо, Родни!

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

Ответ №2:

Просто используйте count_chars:

 echo count(array_filter(count_chars($str)));
  

Массив, возвращаемый из count_chars() , также сообщит вам, сколько каждого символа в строке.

Комментарии:

1. 1 Однако стоит отметить, что пробелы (и знаки препинания?) должен быть удален первым в соответствии с примером OP

2. @PrimozRome Это верно, он возвращает карту ascii (256 символов) с количеством каждого символа. Попробуйте то, что я предложил

3. @PrimozRome: Извиняюсь, я не фильтровал массив для нулевых значений, обновленных в ответе выше.

4. @JuanMendes: Я исправил ответ выше, одна строка, вероятно, предпочтительнее. Слава вашей функции, хотя 1

5. @JuanMendes правильно. Я вижу, да. Ваша функция работает правильно, но не для строк UTF-8. Но все еще приближаюсь к результату. Спасибо!

Ответ №3:

count_chars возвращает отображение всех символов ascii, сообщая вам, сколько каждого из них содержится в строке. Вот отправная точка для вашей собственной реализации.

 function countchars($str, $ignoreSpaces) {
  $map = array();
  $len = strlen($str);
  for ($i=0; $i < $len; $i  ) {
    if (!isset($map[$str{$i}])) {
      $map[$str{$i}] = 1;
    } else {
      $map[$str{$i}]  ;
    }    
  }

  if ($ignoreSpaces) {
    unset($map[' ']);
  }

  return $map;
}

print_r(countchars('Hello World'));
  

Ответ №4:

Вот функция, которая сделает это, используя магию ассоциативных массивов. Работает в линейном времени. (большой O = log(n) )

 function uniques($string){
   $arr = array();
   $parts = str_split($string);
   foreach($parts as $part)
      $arr["$part"] = "yup";
   return count($arr);
}

$str = "APPLE";
echo uniques($str);  // outputs 4
  

Ответ №5:

Мой взгляд на это,

 $chars = array_count_values(str_split($input));
  

Это даст вам ассоциативный массив уникальных букв в качестве ключа и количество вхождений в качестве значения.

Если вас не интересует количество вхождений,

 $chars = array_unique(str_split($input));
$numChars = count($chars);