PHP - itera em caracteres de string

120

Existe uma boa maneira de iterar os caracteres de uma string? Eu gostaria de ser capaz de fazer foreach, array_map, array_walk, array_filteretc, sobre os caracteres de uma string.

O tipo casting / juggling não me levou a lugar nenhum (coloque a string inteira como um elemento do array), e a melhor solução que encontrei é simplesmente usar um loop for para construir o array. Parece que deveria haver algo melhor. Quer dizer, se você pode indexar nele, não deveria ser capaz de iterar também?

Este é o melhor que eu tenho

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

Existe algum:

A) Uma maneira de tornar a string iterável
B) Uma maneira melhor de construir a matriz de caracteres a partir da string (e em caso afirmativo, que tal a outra direção?)

Eu sinto que estou perdendo algo óbvio aqui.

jon_darkstar
fonte
Talvez você deva dizer mais sobre o que está tentando realizar ... parece que pode haver uma maneira melhor de fazer isso usando operações normais de string.
Vinay Pai
1
não tem um objetivo real aqui. apenas uma curiosidade com a qual eu estava brincando. parecia estranho que, embora você possa indexar em strings, você não pode iterar. Eu não conseguia nem pensar em exemplos de uso significativos, mas ainda gostaria de saber se há alguma maneira de iterar nos caracteres das strings sem construir uma matriz de caracteres explicitamente
jon_darkstar
esse é um bom ponto, obviamente, meus exemplos são bem superficiais. ou seja - principalmente qualquer coisa que você faça array_filterneste sentido poderia ser melhor realizada com funções de string ou reg-ex
jon_darkstar
Resolver projecteuler.net/problem=20 pode ser um exemplo de caso de uso (embora um tanto forçado).
Nick Edwards
uma nota, em relação a for ($ i = 0; $ i <strlen ($ s); $ i ++) eu armazenaria o strlen ($ s) em uma variável antes de fazer o loop, desta forma você não chamará strlen () mais do que 1 vez
Amin

Respostas:

176

Etapa 1: converta a string em uma matriz usando a str_splitfunção

$array = str_split($your_string);

Etapa 2: percorrer a matriz recém-criada

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

Você pode verificar os documentos do PHP para obter mais informações: str_split

SeaBrightSystems
fonte
hah uau. sim é isso. e é claro que implodir pode fazer na outra direção. Vou aceitar isso em breve, a menos que alguém possa mostrar uma maneira de fazer a iteração bem na
hora
@jon_darkstar Não conheço seu aplicativo, mas observe que cada entrada em um array tem uma sobrecarga significativa (4bytes IIRC). Pule isso, é 'bastante' muito mais: nikic.github.com/2011/12/12/…
Daan Timmer
str_split() will split into bytes, rather than characters when dealing with a multi-byte encoded string.- Então str_splitnão posso trabalhar com Unicode
Feliz
85

Iterar string:

for ($i = 0; $i < strlen($str); $i++){
    echo $str[$i];
}
Owen
fonte
7
Esta parece ser uma resposta melhor porque responde à questão - ou seja, como iterar em uma string em vez de 'converter em array'.
Robin Andrews
2
RI MUITO!!!!! Tudo @OmarTariq. Isso é muito mais eficiente do que a resposta fornecida.
0x476f72616e
5
Apenas observe que você está chamando strlen()em cada iteração. Não é uma coisa terrível, já que o PHP tem o comprimento pré-calculado, mas ainda é uma chamada de função. Se você precisa de velocidade, é melhor salvar isso em uma variável antes de iniciar o loop.
Vilx-
2
Isso não é bom para strings multibyte, porque aqui estamos obtendo deslocamento de byte, não um símbolo
cerca de
2
@OmarTariq "Esta é a resposta. O que há de errado com o mundo?" .... O errado com o mundo é que o mundo tem outros idiomas além do inglês, esta função como já disse irá iterar os bytes na string, não os caracteres.
Contador م
20

Se suas strings estão em Unicode, você deve usar preg_splitcom /umodificador

Dos comentários na documentação php:

function mb_str_split( $string ) { 
    # Split at all position not after the start: ^ 
    # and not before the end: $ 
    return preg_split('/(?<!^)(?!$)/u', $string ); 
} 
Dawid Ohia
fonte
1
Para strings multibyte, mb_splité mais confiável.
Élektra
12

Você também pode simplesmente acessar $ s1 como um array, se você só precisar acessá-lo:

$s1 = "hello world";
echo $s1[0]; // -> h
Moritur
fonte
6

Expandido da resposta @SeaBrightSystems, você pode tentar isto:

$s1 = "textasstringwoohoo";
$arr = str_split($s1); //$arr now has character array
Janela de laticínios
fonte
Eu discordo, esta resposta agrega valor, dá um exemplo prático de como str_split pode funcionar em um aplicativo PHP. @SeaBrightSystems apenas se vincula à documentação, o que às vezes não é tão útil quando uma pessoa está tentando ver como uma função pode funcionar, dado um exemplo. Caso contrário, a maioria das respostas do SO seriam apenas links para php.net
kurdtpage
6

Para aqueles que estão procurando a maneira mais rápida de iterar strings em php, preparei um teste de benchmark.
O primeiro método no qual você acessa caracteres de string diretamente especificando sua posição entre colchetes e tratando a string como uma matriz:

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

Eu mesmo pensei que o último é o método mais rápido, mas me enganei.
Tal como acontece com o segundo método (que é usado na resposta aceita):

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

Este método será mais rápido porque estamos usando um array real e não presumindo que seja um array.

Chamando a última linha de cada um dos métodos acima para 1000000 tempos leva a estes resultados de comparação:

Usando string [i]
0.24960017204285 Seconds

Usando str_split
0.18720006942749 Seconds

O que significa que o segundo método é muito mais rápido.

AmirHossein
fonte
3

Hmm ... Não há necessidade de complicar as coisas. O básico sempre funciona bem.

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

Direção para frente:

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

Saídas: abcdef

Direção oposta:

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

Saídas: fedcba

Cinza
fonte
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;
}
masakielástico
fonte
Estou surpreso que esta resposta tenha obtido apenas 1 voto positivo :( esta é a resposta mais / única confiável aqui
Contador م
1

A maioria das respostas se esqueceu de caracteres não ingleses !!!

strlenconta BYTES, não caracteres, é por isso que funciona e suas funções irmãs funcionam bem com caracteres ingleses, porque os caracteres ingleses são armazenados em 1 byte nas codificações UTF-8 e ASCII, você precisa usar as funções de string multibyte mb_*

Isso funcionará com qualquer caractere codificado emUTF-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);
}

Isso resulta

string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
string(2) "أ"
string(2) "ب"
string(2) "ت"
string(2) "ث"
Contador م
fonte