PHP - итерация по строковым символам

121

Есть ли хороший способ перебора символов строки? Я хотел бы быть в состоянии сделать foreach, array_map, array_walk, и array_filterт.д. на характерах строки.

Приведение типов / манипуляция никуда меня не привели (поместите всю строку как один элемент массива), и лучшее решение, которое я нашел, - это просто использовать цикл for для создания массива. Такое ощущение, что должно быть что-то получше. Я имею в виду, если вы можете проиндексировать его, разве вы не сможете итерировать?

Это лучшее, что у меня есть

function stringToArray($s)
{
    $r = array();
    for($i=0; $i<strlen($s); $i++) 
         $r[$i] = $s[$i];
    return $r;
}

$s1 = "textasstringwoohoo";
$arr = stringToArray($s1); //$arr now has character array

$ascval = array_map('ord', $arr);  //so i can do stuff like this
$foreach ($arr as $curChar) {....}
$evenAsciiOnly = array_filter( function($x) {return ord($x) % 2 === 0;}, $arr);

Есть ли:

A) Способ сделать строку итерируемой
B) Лучший способ построить массив символов из строки (и если да, как насчет другого направления?)

Я чувствую, что мне здесь не хватает чего-то очевидного.

jon_darkstar
источник
Возможно, вам стоит больше рассказать о том, чего вы пытаетесь достичь ... похоже, что может быть лучший способ сделать это, используя обычные строковые операции.
Vinay Pai
1
у меня здесь нет настоящей цели. просто любопытство, с которым я играл. Казалось странным, что, несмотря на то, что вы можете индексировать строки, вы не можете выполнять итерацию. Я был в затруднении даже придумать значимые примеры использования, но я все же хотел бы знать, есть ли какой-то способ перебрать символы строк без явного построения массива символов
jon_darkstar
это хороший момент, очевидно, что мои примеры довольно поверхностны. то есть - в основном все, что вы бы сделали array_filterв этом смысле, можно было бы лучше сделать с помощью строковых функций или функций reg-ex
jon_darkstar
Примером (хотя и несколько надуманным) может быть решение projecteuler.net/problem=20 .
Ник Эдвардс
одно примечание относительно for ($ i = 0; $ i <strlen ($ s); $ i ++) я бы сохранил strlen ($ s) в переменной перед циклом, таким образом вы не вызовете strlen () более чем 1 раз
Амин

Ответы:

176

Шаг 1: преобразуйте строку в массив с помощью str_splitфункции

$array = str_split($your_string);

Шаг 2: прокрутите вновь созданный массив

foreach ($array as $char) {
 echo $char;
}

Вы можете проверить документацию PHP для получения дополнительной информации: str_split

SeaBrightSystems
источник
ха-ха. да вот и все. и, конечно, взрыв может иметь и другое направление. Я приму это в ближайшее время, если кто-нибудь не покажет, как сделать итерацию прямо на укусе
jon_darkstar
@jon_darkstar Я не знаю ваше приложение, но учтите, что каждая запись в массиве имеет значительные накладные расходы (4 байта IIRC). Пропустите это, это намного больше: nikic.github.com/2011/12/12/…
Даан Тиммер
str_split() will split into bytes, rather than characters when dealing with a multi-byte encoded string.- Значит str_splitнельзя работать с Unicode
Днем
86

Итерировать строку:

for ($i = 0; $i < strlen($str); $i++){
    echo $str[$i];
}
Оуэн
источник
7
Это кажется лучшим ответом, потому что он отвечает на вопрос - например, как перебирать строку, а не «преобразовать в массив».
Робин Эндрюс
2
РЖУНИМАГУ!!!!! Все @OmarTariq. Это намного эффективнее, чем предоставленный ответ.
0x476f72616e
5
Просто обратите внимание, что вы вызываете strlen()каждую итерацию. Ничего страшного, так как PHP имеет предварительно вычисленную длину, но все же вызов функции. Если вам нужна скорость, лучше сохраните ее в переменной перед запуском цикла.
Vilx-
2
Это не очень хорошо для многобайтовых строк, потому что здесь мы получаем байтовое смещение, а не символ
каждый раз
2
@OmarTariq "Это ответ. Что не так с миром?" .... Проблема с миром состоит в том, что в мире есть другие языки, кроме английского, эта функция, как всегда говорилось, будет перебирать байты в строке, а не символы.
Бухгалтер م
20

Если ваши строки находятся в Unicode, вы должны использовать preg_splitс /uмодификатором

Из комментариев в документации php:

function mb_str_split( $string ) { 
    # Split at all position not after the start: ^ 
    # and not before the end: $ 
    return preg_split('/(?<!^)(?!$)/u', $string ); 
} 
Давид Охия
источник
1
Для многобайтовых строк mb_splitэто более надежно.
Élektra
12

Вы также можете просто получить доступ к $ s1 как к массиву, если вам нужен только доступ к нему:

$s1 = "hello world";
echo $s1[0]; // -> h
Moritur
источник
6

Расширенный из ответа @SeaBrightSystems, вы можете попробовать это:

$s1 = "textasstringwoohoo";
$arr = str_split($s1); //$arr now has character array
Молочное окно
источник
Я не согласен, этот ответ добавляет ценности, он дает рабочий пример того, как str_split может работать в приложении PHP. @SeaBrightSystems просто ссылается на документацию, которая иногда не так полезна, когда человек пытается увидеть, как функция может работать, на примере. В противном случае большинство ответов SO были бы просто ссылками на php.net
kurdtpage
6

Для тех, кто ищет самый быстрый способ перебора строк в php, Ive подготовил тестовое тестирование.
Первый метод, в котором вы напрямую обращаетесь к строковым символам, указывая их позицию в скобках и обрабатывая строку как массив:

$string = "a sample string for testing";
$char = $string[4] // equals to m

Я сам думал, что последний метод самый быстрый, но ошибался.
Как и во втором методе (который используется в принятом ответе):

$string = "a sample string for testing";
$string = str_split($string);
$char = $string[4] // equals to m

Этот метод будет быстрее, потому что мы используем реальный массив, а не предполагаем, что это массив.

Вызов последней строки каждого из вышеуказанных методов на 1000000время приводит к следующим результатам тестирования:

Использование строки [i]
0.24960017204285 Seconds

Использование str_split
0.18720006942749 Seconds

Это означает, что второй метод намного быстрее.

AmirHossein
источник
3

Хм ... Не надо ничего усложнять. Основы всегда отлично работают.

    $string = 'abcdef';
    $len = strlen( $string );
    $x = 0;

Прямое направление:

while ( $len > $x ) echo $string[ $x++ ];

Выходы: abcdef

Обратное направление:

while ( $len ) echo $string[ --$len ];

Выходы: fedcba

ясень
источник
2
// Unicode Codepoint Escape Syntax in PHP 7.0
$str = "cat!\u{1F431}";

// IIFE (Immediately Invoked Function Expression) in PHP 7.0
$gen = (function(string $str) {
    for ($i = 0, $len = mb_strlen($str); $i < $len; ++$i) {
        yield mb_substr($str, $i, 1);
    }
})($str);

var_dump(
    true === $gen instanceof Traversable,
    // PHP 7.1
    true === is_iterable($gen)
);

foreach ($gen as $char) {
    echo $char, PHP_EOL;
}
masakielastic
источник
Я удивлен, что этот ответ получил только 1 одобрение :( это самый / единственный надежный ответ здесь
Бухгалтер
1

Большинство ответов забыли о неанглийских символах !!!

strlenподсчитывает БАЙТЫ, а не символы, поэтому это так, и его родственные функции отлично работают с английскими символами, поскольку английские символы хранятся в 1 байте как в кодировках UTF-8, так и в кодировках ASCII, вам необходимо использовать функции многобайтовых строк mb_*

Это будет работать с любым символом, закодированным вUTF-8

// 8 characters in 12 bytes
$string = "abcdأبتث";

$charsCount = mb_strlen($string, 'UTF-8');
for($i = 0; $i < $charsCount; $i++){
    $char = mb_substr($string, $i, 1, 'UTF-8');
    var_dump($char);
}

Это выводит

string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
string(2) "أ"
string(2) "ب"
string(2) "ت"
string(2) "ث"
Бухгалтер م
источник