1<?xml version="1.0" encoding="utf-8"?> 2 3<overlay xmlns="http://hoa-project.net/xyl/xylophone"> 4<yield id="chapter"> 5 6 <p>Les chaînes de caractères peuvent parfois être <strong>complexes</strong>, 7 particulièrement lorsqu'elles utilisent l'encodage <strong>Unicode</strong>. 8 La bibliothèque <code>Hoa\Ustring</code> propose plusieurs opérations sur des 9 chaînes de caractères UTF-8.</p> 10 11 <h2 id="Table_of_contents">Table des matières</h2> 12 13 <tableofcontents id="main-toc" /> 14 15 <h2 id="Introduction" for="main-toc">Introduction</h2> 16 17 <p>Lorsque nous manipulons des chaînes de caractères, le format 18 <a href="http://unicode.org/">Unicode</a> s'impose par sa 19 <strong>compatibilité</strong> avec les formats de base historiques (comme 20 ASCII) et par sa grande capacité à comprendre une très <strong>large</strong> 21 plage de caractères et de symboles, pour toutes les cultures et toutes les 22 régions de notre monde. PHP propose plusieurs outils pour manipuler de telles 23 chaînes, comme les extensions 24 <a href="http://php.net/mbstring"><code>mbstring</code></a>, 25 <a href="http://php.net/iconv"><code>iconv</code></a> ou encore l'excellente 26 <a href="http://php.net/intl"><code>intl</code></a> qui se base sur 27 <a href="http://icu-project.org/">ICU</a>, l'implémentation de référence 28 d'Unicode. Malheureusement, il faut parfois mélanger ces extensions pour 29 arriver à nos fins et au prix d'une certaine <strong>complexité</strong> et 30 d'une <strong>verbosité</strong> regrettable.</p> 31 <p>La bibliothèque <code>Hoa\Ustring</code> répond à ces problématiques en 32 proposant une façon <strong>simple</strong> de manipuler des chaînes de 33 caractères, de manière <strong>performante</strong> et 34 <strong>efficace</strong>. Elle propose également des algorithmes évolués pour 35 des opérations de <strong>recherche</strong> sur des chaînes de 36 caractères.</p> 37 38 <h2 id="Unicode_strings" for="main-toc">Chaîne de caractères Unicode</h2> 39 40 <p>La classe <code>Hoa\Ustring\Ustring</code> représente une chaîne de 41 caractères Unicode <strong>UTF-8</strong> et permet de la manipuler 42 facilement. Elle implémente les interfaces 43 <a href="http://php.net/arrayaccess"><code>ArrayAccess</code></a>, 44 <a href="http://php.net/countable"><code>Countable</code></a> et 45 <a href="http://php.net/iteratoraggregate"><code>IteratorAggregate</code></a>. 46 Nous allons utiliser trois exemples dans trois langues différentes : français, 47 arabe et japonais. Ainsi :</p> 48 <pre><code class="language-php">$french = new Hoa\Ustring\Ustring('Je t\'aime'); 49$arabic = new Hoa\Ustring\Ustring('أحبك'); 50$japanese = new Hoa\Ustring\Ustring('私はあなたを愛して');</code></pre> 51 <p>Maintenant, voyons les opérations possibles sur ces trois chaînes.</p> 52 53 <h3 id="String_manipulation" for="main-toc">Manipulation de la chaîne</h3> 54 55 <p>Commençons par les opérations <strong>élémentaires</strong>. Si nous 56 voulons <strong>compter</strong> le nombre de caractères (et non pas 57 d'octets), nous allons utiliser <a href="http://php.net/count">la fonction 58 <code>count</code></a> de PHP. Ainsi :</p> 59 <pre><code class="language-php">var_dump( 60 count($french), 61 count($arabic), 62 count($japanese) 63); 64 65/** 66 * Will output: 67 * int(9) 68 * int(4) 69 * int(9) 70 */</code></pre> 71 <p>Quand nous parlons de position sur un texte, il n'est pas adéquat de parler 72 de droite ou de gauche, mais plutôt de <strong>début</strong> ou de 73 <strong>fin</strong>, et cela à partir de la <strong>direction</strong> (sens 74 d'écriture) du texte. Nous pouvons connaître cette direction grâce à la 75 méthode <code>Hoa\Ustring\Ustring::getDirection</code>. Elle retourne la 76 valeur d'une des constantes suivantes :</p> 77 <ul> 78 <li><code>Hoa\Ustring\Ustring::LTR</code>, pour 79 <em lang="en">left-to-right</em>, si le texte s'écrit de gauche à 80 droite ;</li> 81 <li><code>Hoa\Ustring\Ustring::RTL</code>, pour 82 <em lang="en">right-to-left</em>, si le texte s'écrit de droite à 83 gauche.</li> 84 </ul> 85 <p>Observons le résultat sur nos exemples :</p> 86 <pre><code class="language-php">var_dump( 87 $french->getDirection() === Hoa\Ustring\Ustring::LTR, // is left-to-right? 88 $arabic->getDirection() === Hoa\Ustring\Ustring::RTL, // is right-to-left? 89 $japanese->getDirection() === Hoa\Ustring\Ustring::LTR // is left-to-right? 90); 91 92/** 93 * Will output: 94 * bool(true) 95 * bool(true) 96 * bool(true) 97 */</code></pre> 98 <p>Le résultat de cette méthode est calculé grâce à la méthode statique 99 <code>Hoa\Ustring\Ustring::getCharDirection</code> qui calcule la direction 100 d'un seul caractère.</p> 101 <p>Si nous voulons <strong>concaténer</strong> une autre chaîne à la fin ou au 102 début, nous utiliserons respectivement les méthodes 103 <code>Hoa\Ustring\Ustring::append</code> et 104 <code>Hoa\Ustring\Ustring::prepend</code>. Ces méthodes, comme la plupart de 105 celles qui modifient la chaîne, retournent l'objet lui-même, ce afin de 106 chaîner les appels. Par exemple :</p> 107 <pre><code class="language-php">echo $french->append('… et toi, m\'aimes-tu ?')->prepend('Mam\'zelle ! '); 108 109/** 110 * Will output: 111 * Mam'zelle ! Je t'aime… et toi, m'aimes-tu ? 112 */</code></pre> 113 <p>Nous avons également les méthodes 114 <code>Hoa\Ustring\Ustring::toLowerCase</code> et 115 <code>Hoa\Ustring\Ustring::toUpperCase</code> pour, respectivement, mettre la 116 chaîne en <strong>minuscules</strong> ou en <strong>majuscules</strong>. Par 117 exemple :</p> 118 <pre><code class="language-php">echo $french->toUpperCase(); 119 120/** 121 * Will output: 122 * MAM'ZELLE ! JE T'AIME… ET TOI, M'AIMES-TU ? 123 */</code></pre> 124 <p>Nous pouvons aussi ajouter des caractères en début ou en fin de chaîne pour 125 atteindre une taille <strong>minimum</strong>. Cette opération est plus 126 couramment appelée le <em lang="en">padding</em> (pour des raisons historiques 127 remontant aux machines à écrire). C'est pourquoi nous trouvons la méthode 128 <code>Hoa\Ustring\Ustring::pad</code> qui prend trois arguments : la taille 129 minimum, les caractères à ajouter et une constante indiquant si nous devons 130 ajouter en fin ou en début de chaîne (respectivement 131 <code>Hoa\Ustring\Ustring::END</code>, par défaut, et 132 <code>Hoa\Ustring\Ustring::BEGINNING</code>).</p> 133 <pre><code class="language-php">echo $arabic->pad(20, ' '); 134 135/** 136 * Will output: 137 * أحبك 138 */</code></pre> 139 <p>Une opération similairement inverse permet de supprimer, par défaut, les 140 <strong>espaces</strong> en début et en fin de chaîne grâce à la méthode 141 <code>Hoa\Ustring\Ustring::trim</code>. Par exemple, pour revenir à notre 142 chaîne arabe originale :</p> 143 <pre><code class="language-php">echo $arabic->trim(); 144 145/** 146 * Will output: 147 * أحبك 148 */</code></pre> 149 <p>Si nous voulons supprimer d'autres caractères, nous pouvons utiliser son 150 premier argument qui doit être une expression régulière. Enfin, son second 151 argument permet de préciser de quel côté nous voulons supprimer les 152 caractères : en début, en fin ou les deux, toujours en utilisant les 153 constantes <code>Hoa\Ustring\Ustring::BEGINNING</code> et 154 <code>Hoa\Ustring\Ustring::END</code>. Nous pouvons combiner ces constantes 155 pour exprimer « les deux côtés », ce qui est la valeur par défaut : 156 <code class="language-php">Hoa\Ustring\Ustring::BEGINNING | 157 Hoa\Ustring\Ustring::END</code>. Par exemple, pour supprimer tous les nombres 158 et les espaces uniquement à la fin, nous écrirons :</p> 159 <pre><code class="language-php">$arabic->trim('\s|\d', Hoa\Ustring\Ustring::END);</code></pre> 160 <p>Nous pouvons également <strong>réduire</strong> la chaîne à une 161 <strong>sous-chaîne</strong> en précisant la position du premier caractère 162 puis la taille de la sous-chaîne à la méthode 163 <code>Hoa\Ustring\Ustring::reduce</code> :</p> 164 <pre><code class="language-php">echo $french->reduce(3, 6)->reduce(2, 4); 165 166/** 167 * Will output: 168 * aime 169 */</code></pre> 170 <p>Si nous voulons obtenir un caractère en particulier, nous pouvons exploiter 171 l'interface <code>ArrayAccess</code>. Par exemple, pour obtenir le premier 172 caractère de chacun de nos exemples (en les reprenant depuis le début) :</p> 173 <pre><code class="language-php">var_dump( 174 $french[0], 175 $arabic[0], 176 $japanese[0] 177); 178 179/** 180 * Will output: 181 * string(1) "J" 182 * string(2) "أ" 183 * string(3) "私" 184 */</code></pre> 185 <p>Si nous voulons le dernier caractère, nous utiliserons l'index -1. L'index 186 n'est pas borné à la taille de la chaîne. Si jamais l'index dépasse cette 187 taille, alors un <em>modulo</em> sera appliqué.</p> 188 <p>Nous pouvons aussi modifier ou supprimer un caractère précis avec cette 189 méthode. Par exemple :</p> 190 <pre><code class="language-php">$french->append(' ?'); 191$french[-1] = '!'; 192echo $french; 193 194/** 195 * Will output: 196 * Je t'aime ! 197 */</code></pre> 198 <p>Une autre méthode fort utile est la transformation en 199 <strong>ASCII</strong>. Attention, ce n'est pas toujours possible, selon votre 200 installation. Par exemple :</p> 201 <pre><code class="language-php">$title = new Hoa\Ustring\Ustring('Un été brûlant sur la côte'); 202echo $title->toAscii(); 203 204/** 205 * Will output: 206 * Un ete brulant sur la cote 207 */</code></pre> 208 <p>Nous pouvons aussi transformer de l'arabe ou du japonais vers de l'ASCII. 209 Les symboles, comme les symboles Mathématiques ou les emojis, sont aussi 210 transformés :</p> 211 <pre><code class="language-php">$emoji = new Hoa\Ustring\Ustring('I ❤ Unicode'); 212$maths = new Hoa\Ustring\Ustring('∀ i ∈ ℕ'); 213 214echo 215 $arabic->toAscii(), "\n", 216 $japanese->toAscii(), "\n", 217 $emoji->toAscii(), "\n", 218 $maths->toAscii(), "\n"; 219 220/** 221 * Will output: 222 * ahbk 223 * sihaanatawo aishite 224 * I (heavy black heart)️ Unicode 225 * (for all) i (element of) N 226 */</code></pre> 227 <p>Pour que cette méthode fonctionne correctement, il faut que l'extension 228 <a href="http://php.net/intl"><code>intl</code></a> soit présente, pour que la 229 classe <a href="http://php.net/transliterator"><code>Transliterator</code></a> 230 existe. Si elle n'existe pas, la classe 231 <a href="http://php.net/normalizer"><code>Normalizer</code></a> doit exister. 232 Si cette classe n'existe pas non plus, la méthode 233 <code>Hoa\Ustring\Ustring::toAscii</code> peut quand même essayer une 234 transformation mais moins efficace. Pour cela, il faut passer 235 <code>true</code> en seul argument. Ce tour de force est déconseillé dans la 236 plupart des cas.</p> 237 <p>Nous trouvons également la méthode <code>getTransliterator</code> qui 238 retourne un objet <code>Transliterator</code>, ou <code>null</code> si cette 239 classe n'existe pas. Cette méthode prend en argument un identifiant de 240 translitération. Nous conseillons de 241 <a href="http://userguide.icu-project.org/transforms/general">lire la 242 documentation sur le translitérateur d'ICU</a> pour comprendre cet 243 identifiant. La méthode <code>transliterate</code> permet de translitérer la 244 chaîne courante à partir d'un identifiant et d'un index de début et de 245 fin. Elle fonctionne de la même façon que la méthode 246 <a href="http://php.net/transliterator.transliterate"><code>Transliterator::transliterate</code></a>.</p> 247 248 <p>Plus généralement, pour des changements d'<strong>encodage</strong> brut, 249 nous pouvons utiliser la méthode statique 250 <code>Hoa\Ustring\Ustring::transcode</code>, avec en premier argument une chaîne 251 de caractères, en deuxième argument l'encodage d'origine et en dernier 252 argument l'encodage final souhaité (par défaut UTF-8). Pour la liste des 253 encodages, il faut se reporter à l'extension 254 <a href="http://php.net/iconv"><code>iconv</code></a> ou entrer la commande 255 suivante dans un terminal :</p> 256 <pre><code class="language-php">$ iconv --list</code></pre> 257 <p>Pour savoir si une chaîne est encodée en UTF-8, nous pouvons utiliser la 258 méthode statique <code>Hoa\Ustring\Ustring::isUtf8</code> ; par exemple :</p> 259 <pre><code class="language-php">var_dump( 260 Hoa\Ustring\Ustring::isUtf8('a'), 261 Hoa\Ustring\Ustring::isUtf8(Hoa\Ustring\Ustring::transcode('a', 'UTF-8', 'UTF-16')) 262); 263 264/** 265 * Will output: 266 * bool(true) 267 * bool(false) 268 */</code></pre> 269 <p>Nous pouvons <strong>éclater</strong> la chaîne en plusieurs sous-chaînes 270 en utilisant la méthode <code>Hoa\Ustring\Ustring::split</code>. En premier 271 argument, nous avons une expression régulière (type 272 <a href="http://pcre.org/">PCRE</a>), puis un entier représentant le nombre 273 maximum d'éléments à retourner et enfin une combinaison de constantes. Ces 274 constantes sont les mêmes que celles de 275 <a href="http://php.net/preg_split"><code>preg_split</code></a>.</p> 276 <p>Par défaut, le deuxième argument vaut -1, qui symbolise l'infini, et le 277 dernier argument vaut <code>PREG_SPLIT_NO_EMPTY</code>. Ainsi, si nous 278 voulons obtenir tous les mots d'une chaîne, nous écrirons :</p> 279 <pre><code class="language-php">print_r($title->split('#\b|\s#')); 280 281/** 282 * Will output: 283 * Array 284 * ( 285 * [0] => Un 286 * [1] => ete 287 * [2] => brulant 288 * [3] => sur 289 * [4] => la 290 * [5] => cote 291 * ) 292 */</code></pre> 293 <p>Si nous voulons <strong>itérer</strong> sur tous les 294 <strong>caractères</strong>, il est préférable d'exploiter l'interface 295 <code>IteratorAggregate</code>, soit la méthode 296 <code>Hoa\Ustring\Ustring::getIterator</code>. Voyons plutôt sur l'exemple en 297 arabe :</p> 298 <pre><code class="language-php">foreach ($arabic as $letter) { 299 echo $letter, "\n"; 300} 301 302/** 303 * Will output: 304 * أ 305 * ح 306 * ب 307 * ك 308 */</code></pre> 309 <p>Nous remarquons que l'itération se fait suivant la direction du texte, 310 c'est à dire que le premier élément de l'itération est la première lettre de 311 la chaîne en partant du début.</p> 312 <p>Bien sûr, si nous voulons obtenir un tableau des caractères, nous pouvons 313 utiliser la fonction 314 <a href="http://php.net/iterator_to_array"><code>iterator_to_array</code></a> 315 de PHP :</p> 316 <pre><code class="language-php">print_r(iterator_to_array($arabic)); 317 318/** 319 * Will output: 320 * Array 321 * ( 322 * [0] => أ 323 * [1] => ح 324 * [2] => ب 325 * [3] => ك 326 * ) 327 */</code></pre> 328 329 <h3 id="Comparison_and_search" for="main-toc">Comparaison et recherche</h3> 330 331 <p>Les chaînes peuvent également être <strong>comparées</strong> entre elles 332 grâce à la méthode <code>Hoa\Ustring\Ustring::compare</code> :</p> 333 <pre><code class="language-php">$string = new Hoa\Ustring\Ustring('abc'); 334var_dump( 335 $string->compare('wxyz') 336); 337 338/** 339 * Will output: 340 * string(-1) 341 */</code></pre> 342 <p>Cette méthode retourne -1 si la chaîne initiale vient avant (par ordre 343 alphabétique), 0 si elle est identique et 1 si elle vient après. Si nous 344 voulons utiliser la pleine 345 puissance du mécanisme sous-jacent, nous pouvons appeler la méthode statique 346 <code>Hoa\Ustring\Ustring::getCollator</code> (si la classe 347 <a href="http://php.net/Collator"><code>Collator</code></a> existe, sinon 348 <code>Hoa\Ustring\Ustring::compare</code> utilisera une comparaison simple 349 octet par octets sans tenir compte d'autres paramètres). Ainsi, si nous 350 voulons trier un tableau de chaînes, nous écrirons plutôt :</p> 351 <pre><code class="language-php">$strings = array('c', 'Σ', 'd', 'x', 'α', 'a'); 352Hoa\Ustring\Ustring::getCollator()->sort($strings); 353print_r($strings); 354 355/** 356 * Could output: 357 * Array 358 * ( 359 * [0] => a 360 * [1] => c 361 * [2] => d 362 * [3] => x 363 * [4] => α 364 * [5] => Σ 365 * ) 366 */</code></pre> 367 <p>La comparaison entre deux chaînes dépend de la <strong>locale</strong>, 368 c'est à dire de la régionalisation du système, comme la langue, le pays, la 369 région etc. Nous pouvons utiliser <a href="@hack:chapter=Locale">la 370 bibliothèque <code>Hoa\Locale</code></a> pour modifier ces données, mais ce 371 n'est pas une dépendance de <code>Hoa\Ustring</code> pour autant.</p> 372 <p>Nous pouvons également savoir si une chaîne <strong>correspond</strong> à 373 un certain motif, toujours exprimé avec une expression régulière. Pour cela, 374 nous allons utiliser la méthode <code>Hoa\Ustring\Ustring::match</code>. Cette 375 méthode repose sur les fonctions 376 <a href="http://php.net/preg_match"><code>preg_match</code></a> et 377 <a href="http://php.net/preg_match_all"><code>preg_match_all</code></a> de 378 PHP, mais en modifiant les options du motif afin qu'il supporte Unicode. Nous 379 avons les paramètres suivants : le motif, une variable par référence pour 380 récupérer les captures, les <em lang="en">flags</em>, la position de début de 381 recherche (<em lang="en">offset</em>) et enfin un booléen indiquant si la 382 recherche est globale ou non (respectivement si nous devons utiliser 383 <code>preg_match_all</code> ou <code>preg_match</code>). Par défaut, la 384 recherche n'est pas globale.</p> 385 <p>Ainsi, nous allons vérifier que notre exemple en français contient bien 386 <code>aime</code> avec son complément d'objet direct :</p> 387 <pre><code class="language-php">$french->match('#(?:(?&lt;direct_object>\w)[\'\b])aime#', $matches); 388var_dump($matches['direct_object']); 389 390/** 391 * Will output: 392 * string(1) "t" 393 */</code></pre> 394 <p>Cette méthode retourne <code>false</code> si une erreur est survenue (par 395 exemple si le motif n'est pas correct), 0 si aucune correspondance n'a été 396 trouvée, le nombre de correspondances trouvées sinon.</p> 397 <p>Similairement, nous pouvons <strong>chercher</strong> et 398 <strong>remplacer</strong> des sous-chaînes par d'autres sous-chaînes suivant 399 un motif, toujours exprimé avec une expression régulière. Pour cela, nous 400 allons utiliser la méthode <code>Hoa\Ustring\Ustring::replace</code>. Cette 401 méthode repose sur les fonctions 402 <a href="http://php.net/preg_replace"><code>preg_replace</code></a> et 403 <a href="http://php.net/preg_replace_callback"><code>preg_replace_callback</code></a> 404 de PHP, mais toujours en modifiant les options du motif afin qu'il supporte 405 Unicode. En premier argument, nous trouvons le ou les motifs, en deuxième 406 argument, le ou les remplacements et en dernier argument la limite de 407 remplacements à faire. Si le remplacement est un <em lang="en">callable</em>, 408 alors la fonction <code>preg_replace_callback</code> sera utilisée.</p> 409 <p>Ainsi, nous allons modifier notre exemple français pour qu'il soit plus 410 poli :</p> 411 <pre><code class="language-php">$french->replace('#(?:\w[\'\b])(?&lt;verb>aime)#', function ($matches) { 412 return 'vous ' . $matches['verb']; 413}); 414 415echo $french; 416 417/** 418 * Will output: 419 * Je vous aime 420 */</code></pre> 421 <p>La classe <code>Hoa\Ustring\Ustring</code> propose des constantes qui sont 422 des aliases de constantes PHP et qui permettent une meilleure lecture du 423 code:</p> 424 <ul> 425 <li><code>Hoa\Ustring\Ustring::WITHOUT_EMPTY</code>, alias de 426 <code>PREG_SPLIT_NO_EMPTY</code> ;</li> 427 <li><code>Hoa\Ustring\Ustring::WITH_DELIMITERS</code>, alias de 428 <code>PREG_SPLIT_DELIM_CAPTURE</code> ;</li> 429 <li><code>Hoa\Ustring\Ustring::WITH_OFFSET</code>, alias de 430 <code>PREG_OFFSET_CAPTURE</code> et 431 <code>PREG_SPLIT_OFFSET_CAPTURE</code> ;</li> 432 <li><code>Hoa\Ustring\Ustring::GROUP_BY_PATTERN</code>, alias de 433 <code>PREG_PATTERN_ORDER</code> ;</li> 434 <li><code>Hoa\Ustring\Ustring::GROUP_BY_TUPLE</code>, alias de 435 <code>PREG_SET_ORDER</code>.</li> 436 </ul> 437 <p>Comme ce sont des aliases stricts, nous pouvons écrire :</p> 438 <pre><code class="language-php">$string = new Hoa\Ustring\Ustring('abc1 defg2 hikl3 xyz4'); 439$string->match( 440 '#(\w+)(\d)#', 441 $matches, 442 Hoa\Ustring\Ustring::WITH_OFFSET 443 | Hoa\Ustring\Ustring::GROUP_BY_TUPLE, 444 0, 445 true 446);</code></pre> 447 448 <h3 id="Characters" for="main-toc">Caractères</h3> 449 450 <p>La classe <code>Hoa\Ustring\Ustring</code> offre des méthodes statiques 451 travaillant sur un seul caractère Unicode. Nous avons déjà évoqué la méthode 452 <code>getCharDirection</code> qui permet de connaître la 453 <strong>direction</strong> d'un caractère. Nous trouvons aussi 454 <code>getCharWidth</code> qui calcule le <strong>nombre de colonnes</strong> 455 nécessaires pour l'affichage d'un seul caractère. Ainsi :</p> 456 <pre><code class="language-php">var_dump( 457 Hoa\Ustring\Ustring::getCharWidth(Hoa\Ustring\Ustring::fromCode(0x7f)), 458 Hoa\Ustring\Ustring::getCharWidth('a'), 459 Hoa\Ustring\Ustring::getCharWidth('㽠') 460); 461 462/** 463 * Will output: 464 * int(-1) 465 * int(1) 466 * int(2) 467 */</code></pre> 468 <p>Cette méthode retourne -1 ou 0 si le caractère n'est pas 469 <strong>imprimable</strong> (par exemple si c'est un caractère de contrôle, 470 comme <code>0x7f</code> qui correspond à <code>DELETE</code>), 1 ou plus si 471 c'est un caractère qui peut être imprimé. Dans notre exemple, <code>㽠</code> 472 s'imprime sur 2 colonnes.</p> 473 <p>Pour plus de sémantique, nous avons accès à la méthode 474 <code>Hoa\Ustring\Ustring::isCharPrintable</code> qui permet de savoir si un 475 caractère est imprimable ou pas.</p> 476 <p>Si nous voulons calculer le nombre de colonnes pour tout une chaîne, il 477 faut utiliser la méthode <code>Hoa\Ustring\Ustring::getWidth</code>. 478 Ainsi :</p> 479 <pre><code class="language-php">var_dump( 480 $french->getWidth(), 481 $arabic->getWidth(), 482 $japanese->getWidth() 483); 484 485/** 486 * Will output: 487 * int(9) 488 * int(4) 489 * int(18) 490 */</code></pre> 491 <p>Essayez dans un terminal avec une police <strong>mono-espacée</strong>. 492 Vous verrez que le japonais demande 18 colonnes pour s'afficher. Cette mesure 493 est très utile si nous voulons connaître la largeur d'une chaîne pour la 494 positionner correctement.</p> 495 <p>La méthode <code>getCharWidth</code> est différente de 496 <code>getWidth</code> car elle prend en compte des caractères de contrôles. 497 Elle est destinée à être utilisée, par exemple, avec des terminaux (voir 498 <a href="@hack:chapter=Console">la bibliothèque 499 <code>Hoa\Console</code></a>).</p> 500 <p>Enfin, si cette fois nous ne nous intéressons pas aux caractères Unicode 501 mais aux caractères <strong>machines</strong> <code>char</code> (soit 1 502 octet), nous avons une opération supplémentaire. La méthode 503 <code>Hoa\Ustring\Ustring::getBytesLength</code> va compter la 504 <strong>taille</strong> de la chaîne en octets :</p> 505 <pre><code class="language-php">var_dump( 506 $arabic->getBytesLength(), 507 $japanese->getBytesLength() 508); 509 510/** 511 * Will output: 512 * int(8) 513 * int(27) 514 */</code></pre> 515 <p>Si nous comparons ces résultats avec ceux de la méthode 516 <code>Hoa\Ustring\Ustring::count</code>, nous comprenons que les caractères 517 arabes sont encodés sur 2 octets alors que les caractères japonais sont 518 encodés sur 3 octets. Nous pouvons également obtenir un octet précis à l'aide 519 de la méthode <code>Hoa\Ustring\Ustring::getByteAt</code>. Encore une fois, 520 l'index n'est pas borné.</p> 521 522 <h3 id="Code-point" for="main-toc">Code-point</h3> 523 524 <p>Chaque caractère est représenté en machine par un entier, appelé 525 <strong>code-point</strong>. Pour obtenir le code-point d'un caractère, nous 526 pouvons utiliser la méthode statique <code>Hoa\Ustring\Ustring::toCode</code>, 527 et pour obtenir un caractère à partir d'un code, nous pouvons utiliser la 528 méthode statique <code>Hoa\Ustring\Ustring::fromCode</code>. Nous avons aussi 529 la méthode statique <code>Hoa\Ustring\Ustring::toBinaryCode</code> qui 530 retourne la représentation sous forme binaire d'un caractère. Prenons un 531 exemple :</p> 532 <pre><code class="language-php">var_dump( 533 Hoa\Ustring\Ustring::toCode('Σ'), 534 Hoa\Ustring\Ustring::toBinaryCode('Σ'), 535 Hoa\Ustring\Ustring::fromCode(0x1a9) 536); 537 538/** 539 * Will output: 540 * int(931) 541 * string(32) "1100111010100011" 542 * string(2) "Σ" 543 */</code></pre> 544 545 <h2 id="Search_algorithms" for="main-toc">Algorithmes de recherche</h2> 546 547 <p>La bibliothèque <code>Hoa\Ustring</code> propose des algorithmes de 548 <strong>recherches</strong> sophistiquées sur les chaînes de caractères à 549 travers la classe <code>Hoa\Ustring\Search</code>.</p> 550 <p>Nous allons étudier l'algorithme 551 <code>Hoa\Ustring\Search::approximated</code> qui fait une recherche d'une 552 sous-chaîne dans une chaîne avec au maximum <strong><em>k</em> 553 différences</strong> (une différence étant une insertion, une délétion ou une 554 modification). Prenons un exemple classique avec une représentation 555 ADN : nous allons chercher toutes les sous-chaînes s'approchant de 556 <code>GATAA</code> à 1 différence près (au maximum) dans 557 <code>CAGATAAGAGAA</code>. Pour cela, nous allons donc écrire :</p> 558 <pre><code class="language-php">$x = 'GATAA'; 559$y = 'CAGATAAGAGAA'; 560$k = 1; 561$search = Hoa\Ustring\Search::approximated($y, $x, $k); 562$n = count($search); 563 564echo 'Try to match ', $x, ' in ', $y, ' with at most ', $k, ' difference(s):', "\n"; 565echo $n, ' match(es) found:', "\n"; 566 567foreach ($search as $position) { 568 echo ' • ', substr($y, $position['i'], $position['l'), "\n"; 569} 570 571/** 572 * Will output: 573 * Try to match GATAA in CAGATAAGAGAA with at most 1 difference(s): 574 * 4 match(es) found: 575 * • AGATA 576 * • GATAA 577 * • ATAAG 578 * • GAGAA 579 */</code></pre> 580 <p>Cette méthode retourne un tableau de tableaux. Chaque sous-tableau 581 représente un résultat et contient trois indexes : <code>i</code> pour la 582 position du premier caractère (octet) du résultat, <code>j</code> pour la 583 position du dernier caractère et <code>l</code> pour la taille du résultat 584 (tout simplement <code>j</code> - <code>i</code>). 585 Ainsi, nous pouvons calculer les résultats en utilisant notre chaîne initiale 586 (ici <code class="language-php">$y</code>) et ces indexes.</p> 587 <p>Avec notre exemple, nous avons quatre résultats. Le premier est 588 <code>AGATA</code>, soit <code>GATA<em>A</em></code> avec un caractère 589 déplacé, et <code>AGATA</code> existe bien dans 590 <code>C<em>AGATA</em>AGAGAA</code>. Le deuxième résultat est 591 <code>GATAA</code>, notre sous-chaîne, qui existe bel et bien dans 592 <code>CA<em>GATAA</em>GAGAA</code>. Le troisième résultat est 593 <code>ATAAG</code>, soit <code><em>G</em>ATAA</code> avec un caractère 594 déplacé, et <code>ATAAG</code> existe bien dans 595 <code>CAG<em>ATAAG</em>AGAA</code>. Enfin, le dernier résultat est 596 <code>GAGAA</code>, soit <code>GA<em>T</em>AA</code> avec un caractère 597 modifié, et <code>GAGAA</code> existe bien dans 598 <code>CAGATAA<em>GAGAA</em></code>.</p> 599 <p>Prenons un autre exemple, plus concret cette fois-ci. Nous allons 600 considérer la chaîne <code>--testIt --foobar --testThat --testAt</code> (qui 601 représente les options possibles d'une ligne de commande), et nous allons 602 chercher <code>--testot</code>, une option qu'aurait pu donner 603 l'utilisateur. Cette option n'existe pas telle quelle. Nous allons donc 604 utiliser notre algorithme de recherche avec 1 différence au maximum. Voyons 605 plutôt :</p> 606 <pre><code class="language-php">$x = 'testot'; 607$y = '--testIt --foobar --testThat --testAt'; 608$k = 1; 609$search = Hoa\Ustring\Search::approximated($y, $x, $k); 610$n = count($search); 611 612// … 613 614/** 615 * Will output: 616 * Try to match testot in --testIt --foobar --testThat --testAt with at most 1 difference(s) 617 * 2 match(es) found: 618 * • testIt 619 * • testAt 620 */</code></pre> 621 <p>Les résultats <code>testIt</code> et <code>testAt</code> sont des vraies 622 options, donc nous pouvons les proposer à l'utilisateur. C'est un mécanisme 623 utilisé par <code>Hoa\Console</code> pour proposer des corrections à 624 l'utilisateur s'il se trompe.</p> 625 626 <h2 id="Conclusion" for="main-toc">Conclusion</h2> 627 628 <p>La bibliothèque <code>Hoa\Ustring</code> propose des facilités pour 629 manipuler des chaînes encodées au format Unicode, mais aussi pour effectuer 630 des recherches sophistiquées sur des chaînes.</p> 631 632</yield> 633</overlay> 634