mirror of
https://github.com/zoe-may/TDoG-Skin.git
synced 2025-01-19 19:17:23 +08:00
634 lines
28 KiB
XML
Executable File
634 lines
28 KiB
XML
Executable File
<?xml version="1.0" encoding="utf-8"?>
|
||
|
||
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
|
||
<yield id="chapter">
|
||
|
||
<p>Les chaînes de caractères peuvent parfois être <strong>complexes</strong>,
|
||
particulièrement lorsqu'elles utilisent l'encodage <strong>Unicode</strong>.
|
||
La bibliothèque <code>Hoa\Ustring</code> propose plusieurs opérations sur des
|
||
chaînes de caractères UTF-8.</p>
|
||
|
||
<h2 id="Table_of_contents">Table des matières</h2>
|
||
|
||
<tableofcontents id="main-toc" />
|
||
|
||
<h2 id="Introduction" for="main-toc">Introduction</h2>
|
||
|
||
<p>Lorsque nous manipulons des chaînes de caractères, le format
|
||
<a href="http://unicode.org/">Unicode</a> s'impose par sa
|
||
<strong>compatibilité</strong> avec les formats de base historiques (comme
|
||
ASCII) et par sa grande capacité à comprendre une très <strong>large</strong>
|
||
plage de caractères et de symboles, pour toutes les cultures et toutes les
|
||
régions de notre monde. PHP propose plusieurs outils pour manipuler de telles
|
||
chaînes, comme les extensions
|
||
<a href="http://php.net/mbstring"><code>mbstring</code></a>,
|
||
<a href="http://php.net/iconv"><code>iconv</code></a> ou encore l'excellente
|
||
<a href="http://php.net/intl"><code>intl</code></a> qui se base sur
|
||
<a href="http://icu-project.org/">ICU</a>, l'implémentation de référence
|
||
d'Unicode. Malheureusement, il faut parfois mélanger ces extensions pour
|
||
arriver à nos fins et au prix d'une certaine <strong>complexité</strong> et
|
||
d'une <strong>verbosité</strong> regrettable.</p>
|
||
<p>La bibliothèque <code>Hoa\Ustring</code> répond à ces problématiques en
|
||
proposant une façon <strong>simple</strong> de manipuler des chaînes de
|
||
caractères, de manière <strong>performante</strong> et
|
||
<strong>efficace</strong>. Elle propose également des algorithmes évolués pour
|
||
des opérations de <strong>recherche</strong> sur des chaînes de
|
||
caractères.</p>
|
||
|
||
<h2 id="Unicode_strings" for="main-toc">Chaîne de caractères Unicode</h2>
|
||
|
||
<p>La classe <code>Hoa\Ustring\Ustring</code> représente une chaîne de
|
||
caractères Unicode <strong>UTF-8</strong> et permet de la manipuler
|
||
facilement. Elle implémente les interfaces
|
||
<a href="http://php.net/arrayaccess"><code>ArrayAccess</code></a>,
|
||
<a href="http://php.net/countable"><code>Countable</code></a> et
|
||
<a href="http://php.net/iteratoraggregate"><code>IteratorAggregate</code></a>.
|
||
Nous allons utiliser trois exemples dans trois langues différentes : français,
|
||
arabe et japonais. Ainsi :</p>
|
||
<pre><code class="language-php">$french = new Hoa\Ustring\Ustring('Je t\'aime');
|
||
$arabic = new Hoa\Ustring\Ustring('أحبك');
|
||
$japanese = new Hoa\Ustring\Ustring('私はあなたを愛して');</code></pre>
|
||
<p>Maintenant, voyons les opérations possibles sur ces trois chaînes.</p>
|
||
|
||
<h3 id="String_manipulation" for="main-toc">Manipulation de la chaîne</h3>
|
||
|
||
<p>Commençons par les opérations <strong>élémentaires</strong>. Si nous
|
||
voulons <strong>compter</strong> le nombre de caractères (et non pas
|
||
d'octets), nous allons utiliser <a href="http://php.net/count">la fonction
|
||
<code>count</code></a> de PHP. Ainsi :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
count($french),
|
||
count($arabic),
|
||
count($japanese)
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* int(9)
|
||
* int(4)
|
||
* int(9)
|
||
*/</code></pre>
|
||
<p>Quand nous parlons de position sur un texte, il n'est pas adéquat de parler
|
||
de droite ou de gauche, mais plutôt de <strong>début</strong> ou de
|
||
<strong>fin</strong>, et cela à partir de la <strong>direction</strong> (sens
|
||
d'écriture) du texte. Nous pouvons connaître cette direction grâce à la
|
||
méthode <code>Hoa\Ustring\Ustring::getDirection</code>. Elle retourne la
|
||
valeur d'une des constantes suivantes :</p>
|
||
<ul>
|
||
<li><code>Hoa\Ustring\Ustring::LTR</code>, pour
|
||
<em lang="en">left-to-right</em>, si le texte s'écrit de gauche à
|
||
droite ;</li>
|
||
<li><code>Hoa\Ustring\Ustring::RTL</code>, pour
|
||
<em lang="en">right-to-left</em>, si le texte s'écrit de droite à
|
||
gauche.</li>
|
||
</ul>
|
||
<p>Observons le résultat sur nos exemples :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
$french->getDirection() === Hoa\Ustring\Ustring::LTR, // is left-to-right?
|
||
$arabic->getDirection() === Hoa\Ustring\Ustring::RTL, // is right-to-left?
|
||
$japanese->getDirection() === Hoa\Ustring\Ustring::LTR // is left-to-right?
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* bool(true)
|
||
* bool(true)
|
||
* bool(true)
|
||
*/</code></pre>
|
||
<p>Le résultat de cette méthode est calculé grâce à la méthode statique
|
||
<code>Hoa\Ustring\Ustring::getCharDirection</code> qui calcule la direction
|
||
d'un seul caractère.</p>
|
||
<p>Si nous voulons <strong>concaténer</strong> une autre chaîne à la fin ou au
|
||
début, nous utiliserons respectivement les méthodes
|
||
<code>Hoa\Ustring\Ustring::append</code> et
|
||
<code>Hoa\Ustring\Ustring::prepend</code>. Ces méthodes, comme la plupart de
|
||
celles qui modifient la chaîne, retournent l'objet lui-même, ce afin de
|
||
chaîner les appels. Par exemple :</p>
|
||
<pre><code class="language-php">echo $french->append('… et toi, m\'aimes-tu ?')->prepend('Mam\'zelle ! ');
|
||
|
||
/**
|
||
* Will output:
|
||
* Mam'zelle ! Je t'aime… et toi, m'aimes-tu ?
|
||
*/</code></pre>
|
||
<p>Nous avons également les méthodes
|
||
<code>Hoa\Ustring\Ustring::toLowerCase</code> et
|
||
<code>Hoa\Ustring\Ustring::toUpperCase</code> pour, respectivement, mettre la
|
||
chaîne en <strong>minuscules</strong> ou en <strong>majuscules</strong>. Par
|
||
exemple :</p>
|
||
<pre><code class="language-php">echo $french->toUpperCase();
|
||
|
||
/**
|
||
* Will output:
|
||
* MAM'ZELLE ! JE T'AIME… ET TOI, M'AIMES-TU ?
|
||
*/</code></pre>
|
||
<p>Nous pouvons aussi ajouter des caractères en début ou en fin de chaîne pour
|
||
atteindre une taille <strong>minimum</strong>. Cette opération est plus
|
||
couramment appelée le <em lang="en">padding</em> (pour des raisons historiques
|
||
remontant aux machines à écrire). C'est pourquoi nous trouvons la méthode
|
||
<code>Hoa\Ustring\Ustring::pad</code> qui prend trois arguments : la taille
|
||
minimum, les caractères à ajouter et une constante indiquant si nous devons
|
||
ajouter en fin ou en début de chaîne (respectivement
|
||
<code>Hoa\Ustring\Ustring::END</code>, par défaut, et
|
||
<code>Hoa\Ustring\Ustring::BEGINNING</code>).</p>
|
||
<pre><code class="language-php">echo $arabic->pad(20, ' ');
|
||
|
||
/**
|
||
* Will output:
|
||
* أحبك
|
||
*/</code></pre>
|
||
<p>Une opération similairement inverse permet de supprimer, par défaut, les
|
||
<strong>espaces</strong> en début et en fin de chaîne grâce à la méthode
|
||
<code>Hoa\Ustring\Ustring::trim</code>. Par exemple, pour revenir à notre
|
||
chaîne arabe originale :</p>
|
||
<pre><code class="language-php">echo $arabic->trim();
|
||
|
||
/**
|
||
* Will output:
|
||
* أحبك
|
||
*/</code></pre>
|
||
<p>Si nous voulons supprimer d'autres caractères, nous pouvons utiliser son
|
||
premier argument qui doit être une expression régulière. Enfin, son second
|
||
argument permet de préciser de quel côté nous voulons supprimer les
|
||
caractères : en début, en fin ou les deux, toujours en utilisant les
|
||
constantes <code>Hoa\Ustring\Ustring::BEGINNING</code> et
|
||
<code>Hoa\Ustring\Ustring::END</code>. Nous pouvons combiner ces constantes
|
||
pour exprimer « les deux côtés », ce qui est la valeur par défaut :
|
||
<code class="language-php">Hoa\Ustring\Ustring::BEGINNING |
|
||
Hoa\Ustring\Ustring::END</code>. Par exemple, pour supprimer tous les nombres
|
||
et les espaces uniquement à la fin, nous écrirons :</p>
|
||
<pre><code class="language-php">$arabic->trim('\s|\d', Hoa\Ustring\Ustring::END);</code></pre>
|
||
<p>Nous pouvons également <strong>réduire</strong> la chaîne à une
|
||
<strong>sous-chaîne</strong> en précisant la position du premier caractère
|
||
puis la taille de la sous-chaîne à la méthode
|
||
<code>Hoa\Ustring\Ustring::reduce</code> :</p>
|
||
<pre><code class="language-php">echo $french->reduce(3, 6)->reduce(2, 4);
|
||
|
||
/**
|
||
* Will output:
|
||
* aime
|
||
*/</code></pre>
|
||
<p>Si nous voulons obtenir un caractère en particulier, nous pouvons exploiter
|
||
l'interface <code>ArrayAccess</code>. Par exemple, pour obtenir le premier
|
||
caractère de chacun de nos exemples (en les reprenant depuis le début) :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
$french[0],
|
||
$arabic[0],
|
||
$japanese[0]
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* string(1) "J"
|
||
* string(2) "أ"
|
||
* string(3) "私"
|
||
*/</code></pre>
|
||
<p>Si nous voulons le dernier caractère, nous utiliserons l'index -1. L'index
|
||
n'est pas borné à la taille de la chaîne. Si jamais l'index dépasse cette
|
||
taille, alors un <em>modulo</em> sera appliqué.</p>
|
||
<p>Nous pouvons aussi modifier ou supprimer un caractère précis avec cette
|
||
méthode. Par exemple :</p>
|
||
<pre><code class="language-php">$french->append(' ?');
|
||
$french[-1] = '!';
|
||
echo $french;
|
||
|
||
/**
|
||
* Will output:
|
||
* Je t'aime !
|
||
*/</code></pre>
|
||
<p>Une autre méthode fort utile est la transformation en
|
||
<strong>ASCII</strong>. Attention, ce n'est pas toujours possible, selon votre
|
||
installation. Par exemple :</p>
|
||
<pre><code class="language-php">$title = new Hoa\Ustring\Ustring('Un été brûlant sur la côte');
|
||
echo $title->toAscii();
|
||
|
||
/**
|
||
* Will output:
|
||
* Un ete brulant sur la cote
|
||
*/</code></pre>
|
||
<p>Nous pouvons aussi transformer de l'arabe ou du japonais vers de l'ASCII.
|
||
Les symboles, comme les symboles Mathématiques ou les emojis, sont aussi
|
||
transformés :</p>
|
||
<pre><code class="language-php">$emoji = new Hoa\Ustring\Ustring('I ❤ Unicode');
|
||
$maths = new Hoa\Ustring\Ustring('∀ i ∈ ℕ');
|
||
|
||
echo
|
||
$arabic->toAscii(), "\n",
|
||
$japanese->toAscii(), "\n",
|
||
$emoji->toAscii(), "\n",
|
||
$maths->toAscii(), "\n";
|
||
|
||
/**
|
||
* Will output:
|
||
* ahbk
|
||
* sihaanatawo aishite
|
||
* I (heavy black heart)️ Unicode
|
||
* (for all) i (element of) N
|
||
*/</code></pre>
|
||
<p>Pour que cette méthode fonctionne correctement, il faut que l'extension
|
||
<a href="http://php.net/intl"><code>intl</code></a> soit présente, pour que la
|
||
classe <a href="http://php.net/transliterator"><code>Transliterator</code></a>
|
||
existe. Si elle n'existe pas, la classe
|
||
<a href="http://php.net/normalizer"><code>Normalizer</code></a> doit exister.
|
||
Si cette classe n'existe pas non plus, la méthode
|
||
<code>Hoa\Ustring\Ustring::toAscii</code> peut quand même essayer une
|
||
transformation mais moins efficace. Pour cela, il faut passer
|
||
<code>true</code> en seul argument. Ce tour de force est déconseillé dans la
|
||
plupart des cas.</p>
|
||
<p>Nous trouvons également la méthode <code>getTransliterator</code> qui
|
||
retourne un objet <code>Transliterator</code>, ou <code>null</code> si cette
|
||
classe n'existe pas. Cette méthode prend en argument un identifiant de
|
||
translitération. Nous conseillons de
|
||
<a href="http://userguide.icu-project.org/transforms/general">lire la
|
||
documentation sur le translitérateur d'ICU</a> pour comprendre cet
|
||
identifiant. La méthode <code>transliterate</code> permet de translitérer la
|
||
chaîne courante à partir d'un identifiant et d'un index de début et de
|
||
fin. Elle fonctionne de la même façon que la méthode
|
||
<a href="http://php.net/transliterator.transliterate"><code>Transliterator::transliterate</code></a>.</p>
|
||
|
||
<p>Plus généralement, pour des changements d'<strong>encodage</strong> brut,
|
||
nous pouvons utiliser la méthode statique
|
||
<code>Hoa\Ustring\Ustring::transcode</code>, avec en premier argument une chaîne
|
||
de caractères, en deuxième argument l'encodage d'origine et en dernier
|
||
argument l'encodage final souhaité (par défaut UTF-8). Pour la liste des
|
||
encodages, il faut se reporter à l'extension
|
||
<a href="http://php.net/iconv"><code>iconv</code></a> ou entrer la commande
|
||
suivante dans un terminal :</p>
|
||
<pre><code class="language-php">$ iconv --list</code></pre>
|
||
<p>Pour savoir si une chaîne est encodée en UTF-8, nous pouvons utiliser la
|
||
méthode statique <code>Hoa\Ustring\Ustring::isUtf8</code> ; par exemple :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
Hoa\Ustring\Ustring::isUtf8('a'),
|
||
Hoa\Ustring\Ustring::isUtf8(Hoa\Ustring\Ustring::transcode('a', 'UTF-8', 'UTF-16'))
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* bool(true)
|
||
* bool(false)
|
||
*/</code></pre>
|
||
<p>Nous pouvons <strong>éclater</strong> la chaîne en plusieurs sous-chaînes
|
||
en utilisant la méthode <code>Hoa\Ustring\Ustring::split</code>. En premier
|
||
argument, nous avons une expression régulière (type
|
||
<a href="http://pcre.org/">PCRE</a>), puis un entier représentant le nombre
|
||
maximum d'éléments à retourner et enfin une combinaison de constantes. Ces
|
||
constantes sont les mêmes que celles de
|
||
<a href="http://php.net/preg_split"><code>preg_split</code></a>.</p>
|
||
<p>Par défaut, le deuxième argument vaut -1, qui symbolise l'infini, et le
|
||
dernier argument vaut <code>PREG_SPLIT_NO_EMPTY</code>. Ainsi, si nous
|
||
voulons obtenir tous les mots d'une chaîne, nous écrirons :</p>
|
||
<pre><code class="language-php">print_r($title->split('#\b|\s#'));
|
||
|
||
/**
|
||
* Will output:
|
||
* Array
|
||
* (
|
||
* [0] => Un
|
||
* [1] => ete
|
||
* [2] => brulant
|
||
* [3] => sur
|
||
* [4] => la
|
||
* [5] => cote
|
||
* )
|
||
*/</code></pre>
|
||
<p>Si nous voulons <strong>itérer</strong> sur tous les
|
||
<strong>caractères</strong>, il est préférable d'exploiter l'interface
|
||
<code>IteratorAggregate</code>, soit la méthode
|
||
<code>Hoa\Ustring\Ustring::getIterator</code>. Voyons plutôt sur l'exemple en
|
||
arabe :</p>
|
||
<pre><code class="language-php">foreach ($arabic as $letter) {
|
||
echo $letter, "\n";
|
||
}
|
||
|
||
/**
|
||
* Will output:
|
||
* أ
|
||
* ح
|
||
* ب
|
||
* ك
|
||
*/</code></pre>
|
||
<p>Nous remarquons que l'itération se fait suivant la direction du texte,
|
||
c'est à dire que le premier élément de l'itération est la première lettre de
|
||
la chaîne en partant du début.</p>
|
||
<p>Bien sûr, si nous voulons obtenir un tableau des caractères, nous pouvons
|
||
utiliser la fonction
|
||
<a href="http://php.net/iterator_to_array"><code>iterator_to_array</code></a>
|
||
de PHP :</p>
|
||
<pre><code class="language-php">print_r(iterator_to_array($arabic));
|
||
|
||
/**
|
||
* Will output:
|
||
* Array
|
||
* (
|
||
* [0] => أ
|
||
* [1] => ح
|
||
* [2] => ب
|
||
* [3] => ك
|
||
* )
|
||
*/</code></pre>
|
||
|
||
<h3 id="Comparison_and_search" for="main-toc">Comparaison et recherche</h3>
|
||
|
||
<p>Les chaînes peuvent également être <strong>comparées</strong> entre elles
|
||
grâce à la méthode <code>Hoa\Ustring\Ustring::compare</code> :</p>
|
||
<pre><code class="language-php">$string = new Hoa\Ustring\Ustring('abc');
|
||
var_dump(
|
||
$string->compare('wxyz')
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* string(-1)
|
||
*/</code></pre>
|
||
<p>Cette méthode retourne -1 si la chaîne initiale vient avant (par ordre
|
||
alphabétique), 0 si elle est identique et 1 si elle vient après. Si nous
|
||
voulons utiliser la pleine
|
||
puissance du mécanisme sous-jacent, nous pouvons appeler la méthode statique
|
||
<code>Hoa\Ustring\Ustring::getCollator</code> (si la classe
|
||
<a href="http://php.net/Collator"><code>Collator</code></a> existe, sinon
|
||
<code>Hoa\Ustring\Ustring::compare</code> utilisera une comparaison simple
|
||
octet par octets sans tenir compte d'autres paramètres). Ainsi, si nous
|
||
voulons trier un tableau de chaînes, nous écrirons plutôt :</p>
|
||
<pre><code class="language-php">$strings = array('c', 'Σ', 'd', 'x', 'α', 'a');
|
||
Hoa\Ustring\Ustring::getCollator()->sort($strings);
|
||
print_r($strings);
|
||
|
||
/**
|
||
* Could output:
|
||
* Array
|
||
* (
|
||
* [0] => a
|
||
* [1] => c
|
||
* [2] => d
|
||
* [3] => x
|
||
* [4] => α
|
||
* [5] => Σ
|
||
* )
|
||
*/</code></pre>
|
||
<p>La comparaison entre deux chaînes dépend de la <strong>locale</strong>,
|
||
c'est à dire de la régionalisation du système, comme la langue, le pays, la
|
||
région etc. Nous pouvons utiliser <a href="@hack:chapter=Locale">la
|
||
bibliothèque <code>Hoa\Locale</code></a> pour modifier ces données, mais ce
|
||
n'est pas une dépendance de <code>Hoa\Ustring</code> pour autant.</p>
|
||
<p>Nous pouvons également savoir si une chaîne <strong>correspond</strong> à
|
||
un certain motif, toujours exprimé avec une expression régulière. Pour cela,
|
||
nous allons utiliser la méthode <code>Hoa\Ustring\Ustring::match</code>. Cette
|
||
méthode repose sur les fonctions
|
||
<a href="http://php.net/preg_match"><code>preg_match</code></a> et
|
||
<a href="http://php.net/preg_match_all"><code>preg_match_all</code></a> de
|
||
PHP, mais en modifiant les options du motif afin qu'il supporte Unicode. Nous
|
||
avons les paramètres suivants : le motif, une variable par référence pour
|
||
récupérer les captures, les <em lang="en">flags</em>, la position de début de
|
||
recherche (<em lang="en">offset</em>) et enfin un booléen indiquant si la
|
||
recherche est globale ou non (respectivement si nous devons utiliser
|
||
<code>preg_match_all</code> ou <code>preg_match</code>). Par défaut, la
|
||
recherche n'est pas globale.</p>
|
||
<p>Ainsi, nous allons vérifier que notre exemple en français contient bien
|
||
<code>aime</code> avec son complément d'objet direct :</p>
|
||
<pre><code class="language-php">$french->match('#(?:(?&lt;direct_object>\w)[\'\b])aime#', $matches);
|
||
var_dump($matches['direct_object']);
|
||
|
||
/**
|
||
* Will output:
|
||
* string(1) "t"
|
||
*/</code></pre>
|
||
<p>Cette méthode retourne <code>false</code> si une erreur est survenue (par
|
||
exemple si le motif n'est pas correct), 0 si aucune correspondance n'a été
|
||
trouvée, le nombre de correspondances trouvées sinon.</p>
|
||
<p>Similairement, nous pouvons <strong>chercher</strong> et
|
||
<strong>remplacer</strong> des sous-chaînes par d'autres sous-chaînes suivant
|
||
un motif, toujours exprimé avec une expression régulière. Pour cela, nous
|
||
allons utiliser la méthode <code>Hoa\Ustring\Ustring::replace</code>. Cette
|
||
méthode repose sur les fonctions
|
||
<a href="http://php.net/preg_replace"><code>preg_replace</code></a> et
|
||
<a href="http://php.net/preg_replace_callback"><code>preg_replace_callback</code></a>
|
||
de PHP, mais toujours en modifiant les options du motif afin qu'il supporte
|
||
Unicode. En premier argument, nous trouvons le ou les motifs, en deuxième
|
||
argument, le ou les remplacements et en dernier argument la limite de
|
||
remplacements à faire. Si le remplacement est un <em lang="en">callable</em>,
|
||
alors la fonction <code>preg_replace_callback</code> sera utilisée.</p>
|
||
<p>Ainsi, nous allons modifier notre exemple français pour qu'il soit plus
|
||
poli :</p>
|
||
<pre><code class="language-php">$french->replace('#(?:\w[\'\b])(?&lt;verb>aime)#', function ($matches) {
|
||
return 'vous ' . $matches['verb'];
|
||
});
|
||
|
||
echo $french;
|
||
|
||
/**
|
||
* Will output:
|
||
* Je vous aime
|
||
*/</code></pre>
|
||
<p>La classe <code>Hoa\Ustring\Ustring</code> propose des constantes qui sont
|
||
des aliases de constantes PHP et qui permettent une meilleure lecture du
|
||
code:</p>
|
||
<ul>
|
||
<li><code>Hoa\Ustring\Ustring::WITHOUT_EMPTY</code>, alias de
|
||
<code>PREG_SPLIT_NO_EMPTY</code> ;</li>
|
||
<li><code>Hoa\Ustring\Ustring::WITH_DELIMITERS</code>, alias de
|
||
<code>PREG_SPLIT_DELIM_CAPTURE</code> ;</li>
|
||
<li><code>Hoa\Ustring\Ustring::WITH_OFFSET</code>, alias de
|
||
<code>PREG_OFFSET_CAPTURE</code> et
|
||
<code>PREG_SPLIT_OFFSET_CAPTURE</code> ;</li>
|
||
<li><code>Hoa\Ustring\Ustring::GROUP_BY_PATTERN</code>, alias de
|
||
<code>PREG_PATTERN_ORDER</code> ;</li>
|
||
<li><code>Hoa\Ustring\Ustring::GROUP_BY_TUPLE</code>, alias de
|
||
<code>PREG_SET_ORDER</code>.</li>
|
||
</ul>
|
||
<p>Comme ce sont des aliases stricts, nous pouvons écrire :</p>
|
||
<pre><code class="language-php">$string = new Hoa\Ustring\Ustring('abc1 defg2 hikl3 xyz4');
|
||
$string->match(
|
||
'#(\w+)(\d)#',
|
||
$matches,
|
||
Hoa\Ustring\Ustring::WITH_OFFSET
|
||
| Hoa\Ustring\Ustring::GROUP_BY_TUPLE,
|
||
0,
|
||
true
|
||
);</code></pre>
|
||
|
||
<h3 id="Characters" for="main-toc">Caractères</h3>
|
||
|
||
<p>La classe <code>Hoa\Ustring\Ustring</code> offre des méthodes statiques
|
||
travaillant sur un seul caractère Unicode. Nous avons déjà évoqué la méthode
|
||
<code>getCharDirection</code> qui permet de connaître la
|
||
<strong>direction</strong> d'un caractère. Nous trouvons aussi
|
||
<code>getCharWidth</code> qui calcule le <strong>nombre de colonnes</strong>
|
||
nécessaires pour l'affichage d'un seul caractère. Ainsi :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
Hoa\Ustring\Ustring::getCharWidth(Hoa\Ustring\Ustring::fromCode(0x7f)),
|
||
Hoa\Ustring\Ustring::getCharWidth('a'),
|
||
Hoa\Ustring\Ustring::getCharWidth('㽠')
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* int(-1)
|
||
* int(1)
|
||
* int(2)
|
||
*/</code></pre>
|
||
<p>Cette méthode retourne -1 ou 0 si le caractère n'est pas
|
||
<strong>imprimable</strong> (par exemple si c'est un caractère de contrôle,
|
||
comme <code>0x7f</code> qui correspond à <code>DELETE</code>), 1 ou plus si
|
||
c'est un caractère qui peut être imprimé. Dans notre exemple, <code>㽠</code>
|
||
s'imprime sur 2 colonnes.</p>
|
||
<p>Pour plus de sémantique, nous avons accès à la méthode
|
||
<code>Hoa\Ustring\Ustring::isCharPrintable</code> qui permet de savoir si un
|
||
caractère est imprimable ou pas.</p>
|
||
<p>Si nous voulons calculer le nombre de colonnes pour tout une chaîne, il
|
||
faut utiliser la méthode <code>Hoa\Ustring\Ustring::getWidth</code>.
|
||
Ainsi :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
$french->getWidth(),
|
||
$arabic->getWidth(),
|
||
$japanese->getWidth()
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* int(9)
|
||
* int(4)
|
||
* int(18)
|
||
*/</code></pre>
|
||
<p>Essayez dans un terminal avec une police <strong>mono-espacée</strong>.
|
||
Vous verrez que le japonais demande 18 colonnes pour s'afficher. Cette mesure
|
||
est très utile si nous voulons connaître la largeur d'une chaîne pour la
|
||
positionner correctement.</p>
|
||
<p>La méthode <code>getCharWidth</code> est différente de
|
||
<code>getWidth</code> car elle prend en compte des caractères de contrôles.
|
||
Elle est destinée à être utilisée, par exemple, avec des terminaux (voir
|
||
<a href="@hack:chapter=Console">la bibliothèque
|
||
<code>Hoa\Console</code></a>).</p>
|
||
<p>Enfin, si cette fois nous ne nous intéressons pas aux caractères Unicode
|
||
mais aux caractères <strong>machines</strong> <code>char</code> (soit 1
|
||
octet), nous avons une opération supplémentaire. La méthode
|
||
<code>Hoa\Ustring\Ustring::getBytesLength</code> va compter la
|
||
<strong>taille</strong> de la chaîne en octets :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
$arabic->getBytesLength(),
|
||
$japanese->getBytesLength()
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* int(8)
|
||
* int(27)
|
||
*/</code></pre>
|
||
<p>Si nous comparons ces résultats avec ceux de la méthode
|
||
<code>Hoa\Ustring\Ustring::count</code>, nous comprenons que les caractères
|
||
arabes sont encodés sur 2 octets alors que les caractères japonais sont
|
||
encodés sur 3 octets. Nous pouvons également obtenir un octet précis à l'aide
|
||
de la méthode <code>Hoa\Ustring\Ustring::getByteAt</code>. Encore une fois,
|
||
l'index n'est pas borné.</p>
|
||
|
||
<h3 id="Code-point" for="main-toc">Code-point</h3>
|
||
|
||
<p>Chaque caractère est représenté en machine par un entier, appelé
|
||
<strong>code-point</strong>. Pour obtenir le code-point d'un caractère, nous
|
||
pouvons utiliser la méthode statique <code>Hoa\Ustring\Ustring::toCode</code>,
|
||
et pour obtenir un caractère à partir d'un code, nous pouvons utiliser la
|
||
méthode statique <code>Hoa\Ustring\Ustring::fromCode</code>. Nous avons aussi
|
||
la méthode statique <code>Hoa\Ustring\Ustring::toBinaryCode</code> qui
|
||
retourne la représentation sous forme binaire d'un caractère. Prenons un
|
||
exemple :</p>
|
||
<pre><code class="language-php">var_dump(
|
||
Hoa\Ustring\Ustring::toCode('Σ'),
|
||
Hoa\Ustring\Ustring::toBinaryCode('Σ'),
|
||
Hoa\Ustring\Ustring::fromCode(0x1a9)
|
||
);
|
||
|
||
/**
|
||
* Will output:
|
||
* int(931)
|
||
* string(32) "1100111010100011"
|
||
* string(2) "Σ"
|
||
*/</code></pre>
|
||
|
||
<h2 id="Search_algorithms" for="main-toc">Algorithmes de recherche</h2>
|
||
|
||
<p>La bibliothèque <code>Hoa\Ustring</code> propose des algorithmes de
|
||
<strong>recherches</strong> sophistiquées sur les chaînes de caractères à
|
||
travers la classe <code>Hoa\Ustring\Search</code>.</p>
|
||
<p>Nous allons étudier l'algorithme
|
||
<code>Hoa\Ustring\Search::approximated</code> qui fait une recherche d'une
|
||
sous-chaîne dans une chaîne avec au maximum <strong><em>k</em>
|
||
différences</strong> (une différence étant une insertion, une délétion ou une
|
||
modification). Prenons un exemple classique avec une représentation
|
||
ADN : nous allons chercher toutes les sous-chaînes s'approchant de
|
||
<code>GATAA</code> à 1 différence près (au maximum) dans
|
||
<code>CAGATAAGAGAA</code>. Pour cela, nous allons donc écrire :</p>
|
||
<pre><code class="language-php">$x = 'GATAA';
|
||
$y = 'CAGATAAGAGAA';
|
||
$k = 1;
|
||
$search = Hoa\Ustring\Search::approximated($y, $x, $k);
|
||
$n = count($search);
|
||
|
||
echo 'Try to match ', $x, ' in ', $y, ' with at most ', $k, ' difference(s):', "\n";
|
||
echo $n, ' match(es) found:', "\n";
|
||
|
||
foreach ($search as $position) {
|
||
echo ' • ', substr($y, $position['i'], $position['l'), "\n";
|
||
}
|
||
|
||
/**
|
||
* Will output:
|
||
* Try to match GATAA in CAGATAAGAGAA with at most 1 difference(s):
|
||
* 4 match(es) found:
|
||
* • AGATA
|
||
* • GATAA
|
||
* • ATAAG
|
||
* • GAGAA
|
||
*/</code></pre>
|
||
<p>Cette méthode retourne un tableau de tableaux. Chaque sous-tableau
|
||
représente un résultat et contient trois indexes : <code>i</code> pour la
|
||
position du premier caractère (octet) du résultat, <code>j</code> pour la
|
||
position du dernier caractère et <code>l</code> pour la taille du résultat
|
||
(tout simplement <code>j</code> - <code>i</code>).
|
||
Ainsi, nous pouvons calculer les résultats en utilisant notre chaîne initiale
|
||
(ici <code class="language-php">$y</code>) et ces indexes.</p>
|
||
<p>Avec notre exemple, nous avons quatre résultats. Le premier est
|
||
<code>AGATA</code>, soit <code>GATA<em>A</em></code> avec un caractère
|
||
déplacé, et <code>AGATA</code> existe bien dans
|
||
<code>C<em>AGATA</em>AGAGAA</code>. Le deuxième résultat est
|
||
<code>GATAA</code>, notre sous-chaîne, qui existe bel et bien dans
|
||
<code>CA<em>GATAA</em>GAGAA</code>. Le troisième résultat est
|
||
<code>ATAAG</code>, soit <code><em>G</em>ATAA</code> avec un caractère
|
||
déplacé, et <code>ATAAG</code> existe bien dans
|
||
<code>CAG<em>ATAAG</em>AGAA</code>. Enfin, le dernier résultat est
|
||
<code>GAGAA</code>, soit <code>GA<em>T</em>AA</code> avec un caractère
|
||
modifié, et <code>GAGAA</code> existe bien dans
|
||
<code>CAGATAA<em>GAGAA</em></code>.</p>
|
||
<p>Prenons un autre exemple, plus concret cette fois-ci. Nous allons
|
||
considérer la chaîne <code>--testIt --foobar --testThat --testAt</code> (qui
|
||
représente les options possibles d'une ligne de commande), et nous allons
|
||
chercher <code>--testot</code>, une option qu'aurait pu donner
|
||
l'utilisateur. Cette option n'existe pas telle quelle. Nous allons donc
|
||
utiliser notre algorithme de recherche avec 1 différence au maximum. Voyons
|
||
plutôt :</p>
|
||
<pre><code class="language-php">$x = 'testot';
|
||
$y = '--testIt --foobar --testThat --testAt';
|
||
$k = 1;
|
||
$search = Hoa\Ustring\Search::approximated($y, $x, $k);
|
||
$n = count($search);
|
||
|
||
// …
|
||
|
||
/**
|
||
* Will output:
|
||
* Try to match testot in --testIt --foobar --testThat --testAt with at most 1 difference(s)
|
||
* 2 match(es) found:
|
||
* • testIt
|
||
* • testAt
|
||
*/</code></pre>
|
||
<p>Les résultats <code>testIt</code> et <code>testAt</code> sont des vraies
|
||
options, donc nous pouvons les proposer à l'utilisateur. C'est un mécanisme
|
||
utilisé par <code>Hoa\Console</code> pour proposer des corrections à
|
||
l'utilisateur s'il se trompe.</p>
|
||
|
||
<h2 id="Conclusion" for="main-toc">Conclusion</h2>
|
||
|
||
<p>La bibliothèque <code>Hoa\Ustring</code> propose des facilités pour
|
||
manipuler des chaînes encodées au format Unicode, mais aussi pour effectuer
|
||
des recherches sophistiquées sur des chaînes.</p>
|
||
|
||
</yield>
|
||
</overlay>
|