<?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('#(?:(?&amp;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])(?&amp;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>
