PHP - itérer sur des caractères de chaîne

121

Existe-t-il une bonne manière d'itérer sur les caractères d'une chaîne? Je voudrais être en mesure de le faire foreach, array_map, array_walk, array_filteretc. sur les caractères d'une chaîne.

Le type casting / jonglage ne m'a amené nulle part (mettez la chaîne entière comme un élément du tableau), et la meilleure solution que j'ai trouvée consiste simplement à utiliser une boucle for pour construire le tableau. On a l'impression qu'il devrait y avoir quelque chose de mieux. Je veux dire, si vous pouvez l'indexer, ne devriez-vous pas être capable d'itérer également?

C'est le meilleur que j'ai

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

Y a-t-il soit:

A) Un moyen de rendre la chaîne itérable
B) Une meilleure façon de construire le tableau de caractères à partir de la chaîne (et si oui, qu'en est-il de l'autre sens?)

J'ai l'impression de manquer quelque chose d'évident ici.

jon_darkstar
la source
Vous devriez peut-être en dire plus sur ce que vous essayez d'accomplir ... il semble qu'il y ait peut-être une meilleure façon de le faire en utilisant des opérations de chaîne normales.
Vinay Pai
1
n’a pas de véritable objectif ici. juste une curiosité avec laquelle je jouais. semblait étrange que même si vous pouvez indexer sur des chaînes, vous ne pouvez pas itérer. J'étais
incapable de
thats bon point cependant, évidemment mes exemples sont assez superficiels. c'est-à-dire - la plupart du temps, tout ce que vous feriez array_filterdans ce sens pourrait être mieux fait avec des fonctions string ou reg-ex
jon_darkstar
La résolution de projecteuler.net/problem=20 pourrait être un exemple (bien que quelque peu artificiel) de cas d'utilisation.
Nick Edwards
une note, concernant for ($ i = 0; $ i <strlen ($ s); $ i ++) je stockerais le strlen ($ s) dans une variable avant de boucler, de cette façon vous n'appelerez pas strlen () plus que 1 fois
Amin le

Réponses:

176

Étape 1: convertir la chaîne en tableau à l'aide de la str_splitfonction

$array = str_split($your_string);

Étape 2: parcourez le tableau nouvellement créé

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

Vous pouvez consulter la documentation PHP pour plus d'informations: str_split

SeaBrightSystems
la source
hah wow. oui c'est tout. et bien sûr imploser peut faire l'autre sens. J'accepterai cela bientôt à moins que quelqu'un ne puisse montrer un moyen de faire l'itération directement
jon_darkstar
@jon_darkstar Je ne connais pas votre application, mais notez que chaque entrée dans un tableau a une surcharge importante (4 octets IIRC). Sautez ça, c'est bien plus: 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.- Je str_splitne peux donc pas travailler avec Unicode
Joyeux
86

Itérer la chaîne:

for ($i = 0; $i < strlen($str); $i++){
    echo $str[$i];
}
Owen
la source
7
Cela semble être une meilleure réponse car elle répond à la question - c'est-à-dire comment itérer sur une chaîne par opposition à «convertir en tableau».
Robin Andrews
2
LOL !!!!! Tout @OmarTariq. C'est beaucoup plus efficace que la réponse fournie.
0x476f72616e
5
Notez simplement que vous appelez strlen()à chaque itération. Pas une chose terrible, puisque PHP a la longueur précalculée, mais toujours un appel de fonction. Si vous avez besoin de vitesse, mieux vaut l'enregistrer dans une variable avant de démarrer la boucle.
Vilx-
2
Ce n'est pas bon pour les chaînes multi-octets, car ici nous obtenons un décalage d'octet, pas un symbole
tous les
2
@OmarTariq "C'est la réponse. Quel est le problème avec le monde?" .... Le problème avec le monde est que le monde a d'autres langues que l'anglais, cette fonction comme toujours dit itérera les octets de la chaîne, pas les caractères.
Accountant م
20

Si vos chaînes sont en Unicode, vous devez utiliser preg_splitavec le /umodificateur

À partir des commentaires dans la documentation 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
la source
1
Pour les chaînes multi-octets, mb_splitc'est plus fiable.
Élektra
12

Vous pouvez également accéder simplement à $ s1 comme un tableau, si vous avez seulement besoin d'y accéder:

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

Développé à partir de la réponse @SeaBrightSystems, vous pouvez essayer ceci:

$s1 = "textasstringwoohoo";
$arr = str_split($s1); //$arr now has character array
Fenêtre laitière
la source
Je ne suis pas d'accord, cette réponse ajoute de la valeur, elle donne un exemple fonctionnel de la façon dont str_split pourrait fonctionner dans une application PHP. @SeaBrightSystems renvoie simplement à la documentation, ce qui n'est parfois pas très utile lorsqu'une personne essaie de voir comment une fonction peut fonctionner, à partir d'un exemple. Sinon, la plupart des réponses SO ne seraient que des liens vers php.net
kurdtpage
6

Pour ceux qui recherchent le moyen le plus rapide d'itérer des chaînes en php, j'ai préparé un test de référence.
La première méthode dans laquelle vous accédez directement aux caractères de chaîne en spécifiant sa position entre crochets et en traitant la chaîne comme un tableau:

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

J'ai moi-même pensé que cette dernière était la méthode la plus rapide, mais je me suis trompé.
Comme pour la deuxième méthode (qui est utilisée dans la réponse acceptée):

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

Cette méthode va être plus rapide car nous utilisons un tableau réel et ne supposons pas qu'un tableau est un tableau.

L'appel de la dernière ligne de chacune des méthodes ci-dessus pour les 1000000temps conduit à ces résultats d'analyse comparative:

Utilisation de la chaîne [i]
0.24960017204285 Seconds

Utilisation de str_split
0.18720006942749 Seconds

Ce qui signifie que la deuxième méthode est beaucoup plus rapide.

AmirHossein
la source
3

Hmm ... Il n'y a pas besoin de compliquer les choses. Les bases fonctionnent toujours très bien.

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

Direction avant:

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

Les sorties: abcdef

Direction inverse:

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

Les sorties: fedcba

Cendre
la source
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
la source
Je suis surpris que cette réponse n'ait obtenu qu'un seul vote positif: (c'est la réponse la plus / la seule fiable ici
Accountant م
1

La plupart des réponses ont oublié les caractères non anglais !!!

strlencompte BYTES, pas les caractères, c'est pourquoi c'est le cas et ses fonctions frères fonctionnent bien avec les caractères anglais, car les caractères anglais sont stockés dans 1 octet dans les encodages UTF-8 et ASCII, vous devez utiliser les fonctions de chaîne multioctets mb_*

Cela fonctionnera avec n'importe quel caractère encodé enUTF-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);
}

Cette sortie

string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
string(2) "أ"
string(2) "ب"
string(2) "ت"
string(2) "ث"
Comptable م
la source