PHP - ітерація рядкових символів


120

Чи є приємний спосіб перебрати символи рядка? Я хотів би бути в змозі зробити 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) Кращий спосіб побудувати масив символів з рядка (і якщо так, як щодо іншого напрямку?)

Я відчуваю, що я тут пропускаю щось очевидно.


Можливо, вам слід сказати більше про те, що ви намагаєтеся досягти ... здається, може бути кращий спосіб зробити це за допомогою звичайних рядкових операцій.
Vinay Pai

1
не майте тут реальної мети. просто цікавість, з якою я грав. здавалося дивним, що хоч ти можеш індексувати рядки, ти не можеш повторити. я був у збитку навіть придумати змістовні приклади використання, але все одно хотів би знати, чи є спосіб
перебрати

це хороший пункт, хоча, очевидно, мої приклади досить дрібні. тобто - в основному все, що ви робили array_filterв цьому сенсі, можна краще зробити за допомогою рядкових або reg-ex функцій
jon_darkstar

Розв’язання projecteuler.net/problem=20 може бути прикладом (хоча дещо надуманим) випадком використання.
Нік Едвардс

одна примітка, що стосується ($ 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


ха-вау так це. і, звичайно, імплоде може зробити інший напрямок. Я скоро прийму це, якщо хтось не покаже спосіб зробити ітерацію правильно на жалі
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
Щасливий

85

Ітераційна рядок:

for ($i = 0; $i < strlen($str); $i++){
    echo $str[$i];
}

7
Це здається кращою відповіддю, оскільки воно відповідає на питання - тобто як перебрати рядок на відміну від "перетворити в масив".
Робін Ендрюс

2
ЛОЛ!!!!! Все @OmarTariq. Це набагато ефективніше, ніж надана відповідь.
0x476f72616e

5
Просто зауважте, що ви закликаєте strlen()до кожної ітерації. Не страшна річ, адже PHP має попередній розрахунок за довжиною, але все-таки виклик функції. Якщо у вас є потреба у швидкості, краще збережіть її у змінній перед початком циклу.
Vilx-

2
Це не добре для багатобайтових рядків, тому що тут ми отримуємо байт зміщення, а не символ
alvery

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

6

Розширений з відповіді @SeaBrightSystems, ви можете спробувати це:

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

Я не погоджуюся, ця відповідь додає значення, вона дає робочий приклад того, як str_split може працювати в додатку PHP. @SeaBrightSystems просто посилається на документацію, що іноді не так корисно, коли людина намагається зрозуміти, як функція може працювати, наводячи приклад. В іншому випадку більшість відповідей
ТА

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

Це означає, що другий метод - це швидше.


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;
}

Я здивований, що ця відповідь отримала лише 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) "ث"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.