Ne jamais utiliser telle quelle la valeur décodée d'un QR code — la réussite de la correction d'erreurs ne garantit pas la valeur
· Go Komura · Code QR, Code-barres, Correction d'erreurs, Validation des entrées, Qualité des données, Systèmes métier, C#, Conception, Exploitation terrain
Le terminal de contrôle de l’entrepôt émet un « bip ». C’est le signal que le QR code du bordereau a été lu. La chaîne de caractères lue est transmise telle quelle au système de gestion des stocks, le bordereau est affecté, et l’instruction d’expédition est validée. Le QR code dispose d’une correction d’erreurs, donc même un peu sali, il renvoie la bonne valeur — les systèmes construits sur ce postulat sont loin d’être rares.
La première moitié de cette affirmation est vraie. Le QR code est conçu pour permettre de récupérer les données malgré des salissures ou des dégradations, et selon le niveau de correction d’erreurs, de L à H, il peut restaurer environ 7 % à 30 % des mots de code.1 Le problème se situe dans la seconde moitié : le raisonnement « donc la valeur renvoyée est correcte ».
Cet article s’appuie sur des images d’exemple de QR codes réellement générés et sur des mesures réalisées avec deux décodeurs différents pour préciser pourquoi la réussite de la correction d’erreurs ne garantit pas la valeur, et ce que le système métier doit vérifier de son côté. Tous les QR codes présentés dans cet article sont réels. Ceux qui sont lisibles peuvent être directement vérifiés avec le lecteur QR que vous avez sous la main (certains exemples servent au contraire à démontrer qu’ils ne peuvent pas être lus ; cela est précisé à chaque fois). Nous avons aussi préparé un outil de comparaison de lecture de QR code qui permet de basculer entre jsQR et OpenCV.js directement dans le navigateur. Les mesures de cet article peuvent y être reproduites.
1. Conclusion d’abord
- La correction d’erreurs est une « restauration », pas une « vérification ». Le standard lui-même indique qu’un module sali entraîne un « décodage erroné vers un mot de code apparemment valide mais différent ».2
- Avec une salissure aléatoire, cela bascule quasiment toujours vers « illisible ». Sur 9 700 essais mesurés, aucun cas n’a renvoyé une valeur erronée.
- Mais si le dommage se concentre à un seul endroit, on obtient à coup sûr une valeur différente. Il suffit que 7 des 26 mots de code soient altérés pour que
004873se lise104873, et deux décodeurs indépendants ont renvoyé exactement la même erreur. - En dehors de la correction d’erreurs, il existe des pièges bien plus simples. QR divisé, codage des caractères, autre code présent dans le champ de l’image. Tous peuvent se produire sans qu’aucune erreur ne soit levée. Certains décodeurs affichent parfois un avertissement ou une exception, mais cela dépend entièrement de l’implémentation, et des combinaisons qui basculent du côté silencieux existent bel et bien.
- C’est pourquoi il faut traiter une valeur lue comme une entrée non vérifiée. La forme de base consiste à la recevoir à travers trois étages : contrôle de format → clé de contrôle → validation métier.
2. Exemple — deux QR codes qui se ressemblent presque à l’identique
Commençons par regarder les originaux. Les deux images suivantes se lisent toutes les deux sans erreur.
Un mot avant d’essayer : il se peut que l’appareil photo standard d’iOS ne réagisse pas. Si ça ne fonctionne pas, essayez avec une application de lecture de QR code (la raison est expliquée dans la remarque ci-dessous).
- A (haut) →
NO:20260725-004873 - B (bas) →
NO:20260725-104873
Ce sont de vrais QR codes : vous pouvez les tester directement avec le lecteur que vous avez sous la main. Ce numéro de bordereau suit le format « date de commande sur 8 chiffres + numéro séquentiel sur 5 chiffres + 1 clé de contrôle », donc ce qui a changé, c’est la partie séquentielle — 00487 est devenu 10487, ce qui désigne un bordereau différent, décalé de 10 000 unités.
Il se peut que l’appareil photo standard d’iOS ne réagisse pas. L’appareil photo standard est conçu pour privilégier les contenus qu’il peut « ouvrir », comme une URL ; pour un QR ne contenant qu’une chaîne de texte, comme dans cet article, il se peut qu’il ne se passe rien. Une application de lecture de QR code, elle, le lira. Une même image dont le comportement change selon l’implémentation du lecteur — le sujet même de cet article se laisse ainsi vivre dès le premier exemple.
La seule différence entre B et A tient à 31 modules situés dans une seule bande verticale, entre les colonnes 10 et 14 en partant de la gauche. Cela représente 15 % des 208 modules de la zone de mots de code.
Et le point essentiel, c’est que le décodeur ne renvoie d’erreur pour aucun des deux. Il n’y a même pas de notification du type « une correction a été appliquée ». Du point de vue de l’application, les deux lectures sont des succès parfaitement équivalents.
Précisons d’emblée que ce dommage a été construit intentionnellement : la probabilité qu’il survienne par hasard n’est pas élevée. La façon dont il a été fabriqué, et son écart avec une salissure réelle, sont traités à la section 4.
3. Pourquoi cela se produit — à l’intérieur de la correction d’erreurs
La correction d’erreurs du QR code repose sur un code de Reed-Solomon, qui agit par mot de code (unité de 8 bits). Voici la répartition pour la version 1, niveau de correction d’erreurs M (21×21 modules), utilisée ici.2
| Nombre total de mots de code | Mots de code de données | Mots de code de correction d’erreurs | Capacité de correction selon le standard |
|---|---|---|---|
| 26 | 16 | 10 | 4 mots de code |
Ce qui attire l’attention, c’est la dernière colonne. Avec 10 mots de code de correction d’erreurs, un code de Reed-Solomon devrait pouvoir en corriger jusqu’à 5. Or la capacité de correction fixée par le standard est de 4. L’écart d’une unité est délibérément mis de côté sous forme de mots de code p destinés à réduire le risque de mauvais décodage (pour la version 1-M, p = 2). Une note de bas de page du tableau 13 du standard précise explicitement que « pour réduire la probabilité de mauvais décodage, la capacité de correction est fixée à moins de la moitié du nombre de mots de code de correction d’erreurs ».2
Autrement dit, le standard lui-même est conçu en partant du principe que la correction d’erreurs peut produire une valeur erronée. La même section précise également ceci :
Le QR code étant un symbole matriciel, un défaut qui change un module de sombre à clair (ou inversement) peut avoir pour conséquence que le caractère symbolique correspondant soit décodé de manière erronée en un mot de code apparemment valide mais différent.2
La raison tient au principe même de la correction. Ce que fait le décodage de Reed-Solomon, c’est chercher, à une certaine distance (la capacité de correction) du motif reçu, s’il existe un mot de code. S’il en trouve un, il le renvoie comme réponse ; sinon, cela se termine par « illisible ». Ce n’est pas un mécanisme qui recherche coûte que coûte le mot de code le plus proche, aussi endommagé soit-il.
De cette propriété découlent deux issues possibles. Un dommage aléatoire se disperse loin de tout mot de code, si bien que la plupart du temps, cela bascule vers « aucun trouvé = illisible ». Ce qui est dangereux, c’est quand le dommage se retrouve, par hasard, au voisinage d’un autre mot de code. Le décodeur juge alors que ce mot de code est la bonne réponse et le renvoie. Comme le mot de code renvoyé est parfaitement cohérent en lui-même, rien ne permet de savoir qu’il s’agit d’une erreur.
4. Jusqu’où la correction va, et à partir de quand c’est dangereux
À partir d’ici, place aux mesures. Pour donner la conclusion d’emblée : ce qui est dangereux, ce n’est pas la « quantité » de salissure, mais l’« endroit » qu’elle touche.
Avant de lire les chiffres, récapitulons les conditions expérimentales de ce chapitre. Ce sont les hypothèses à connaître pour reproduire l’expérience chez vous.
| Élément | Contenu |
|---|---|
| Symbole cible | Version 1-M / 21×21 (26 mots de code = 16 de données + 10 de correction d’erreurs) |
| Génération | segno 1.6.6 / Python 3.11 |
| Décodeur 1 | OpenCV 5.0.0 cv2.QRCodeDetector (Python 3.11) |
| Décodeur 2 | jsQR 1.4.0 (Node.js 22) |
| Mode de dommage (section 4.1) | Sélection aléatoire dans les 208 modules de la zone de mots de code puis inversion / corruption aléatoire par mot de code / flou, bruit et baisse de contraste uniformes sur l’ensemble du symbole |
| Mode de dommage (section 4.2) | Essai exhaustif des combinaisons de mots de code rapprochées de la valeur cible B |
| Nombre d’essais | Inversion de modules : 3 900 essais (300 par palier) ; corruption de mots de code : 1 800 essais (200 par palier) + 4 000 essais complémentaires au-delà de la capacité de correction ; dégradation de la qualité d’image : 1 000 essais (200 par condition). Pour la section 4.2 : totalité des 792 et 495 combinaisons |
| Critère de classement | La valeur renvoyée par le décodeur est classée « correctement lue » si elle correspond à la valeur attendue, « illisible » si aucune valeur n’est obtenue, et « valeur erronée » si une valeur différente et non vide est renvoyée |
L’environnement complet de l’article, y compris les exemples de la section 5, est récapitulé dans « Environnement de vérification » à la fin de l’article.
4.1. Une salissure aléatoire bascule vers « illisible »
Pour un symbole de version 1-M, nous avons inversé aléatoirement un certain nombre de modules parmi les 208 de la zone de mots de code, et classé les résultats (300 essais par palier, 3 900 au total).
En haut, 6 modules ont été inversés ; en bas, 9. Celui du haut se lit correctement, celui du bas ne se lit pas du tout. Une différence de 3 modules est quasiment invisible à l’œil nu. La frontière se situe là où rien ne le laisse deviner visuellement.
| Modules inversés | Correctement lu | Illisible | Valeur erronée |
|---|---|---|---|
| 0 à 5 | 1 799 | 1 | 0 |
| 6 | 131 | 169 | 0 |
| 7 | 32 | 268 | 0 |
| 8 | 11 | 289 | 0 |
| 9 à 12 | 0 | 1 200 | 0 |
La lisibilité s’effondre entre 5 et 6 modules, et à partir de 9, c’est l’échec total. Et aucune valeur erronée n’est apparue. Un dommage qui dépasse la capacité de correction bascule vers « illisible » — voilà la bonne nouvelle, sans détour. Les chiffres étaient quasiment identiques avec jsQR (les 1 800 essais de 0 à 5 étaient tous corrects, et à partir de 6, l’écart avec OpenCV était d’au plus 1 cas).
En observant la même chose au niveau des mots de code, la frontière devient encore plus nette (200 essais par palier, 1 800 au total).
| Mots de code corrompus | Correctement lu | Illisible | Valeur erronée |
|---|---|---|---|
| 0 à 5 | 1 194 | 6 | 0 |
| 6 à 8 | 0 | 600 | 0 |
La correction va jusqu’à 5 mots de code. Comme vu à la section précédente, la capacité de correction fixée par le standard est de 4 ; le reste était censé servir uniquement à « détecter sans corriger ». jsQR a lu correctement les 1 200 essais de 0 à 5, ce qui montre que les deux implémentations épuisent intégralement cette marge pour corriger. La marge que le standard réserve contre les erreurs de lecture ne peut pas être considérée comme fiable au niveau des implémentations.
Nous avons également refait l’essai 2 000 fois pour chaque cas de corruption dépassant clairement la capacité de correction (6 et 8 mots de code), et là encore, aucune valeur erronée — tout s’est soldé par « illisible ».
La dégradation liée à la prise de vue suit la même tendance. Voici les résultats après application d’un flou, d’un bruit et d’une baisse de contraste uniformes sur l’ensemble du symbole (200 essais par condition, 1 000 au total).
| Flou σ | Bruit σ | Contraste | Correctement lu | Illisible | Valeur erronée |
|---|---|---|---|---|---|
| 0 | 0 | 1,00 | 200 | 0 | 0 |
| 1,5 | 10 | 0,90 | 183 | 17 | 0 |
| 3,0 | 20 | 0,70 | 1 | 199 | 0 |
| 4,5 | 30 | 0,50 | 0 | 200 | 0 |
| 6,0 | 40 | 0,35 | 0 | 200 | 0 |
Le résultat est soit « correctement lu », soit « illisible » : il n’y a pas d’entre-deux. Le taux de réussite baisse à mesure que la dégradation progresse, mais tout ce qui est perdu se transforme intégralement en échec de lecture.
Cela dit, ceci ne vaut que pour une dégradation uniforme. Un flou de bougé réel a une direction, et une prise de vue en angle ou un éclairage inégal ne dégradent qu’une partie de l’image. Comme nous le verrons, ce qui est dangereux, c’est que le dommage soit concentré ; ne généralisez donc pas en vous disant que « un problème de qualité d’image ne provoque pas d’erreur de lecture ».
4.2. Quand l’endroit touché est le mauvais, on obtient à coup sûr une autre valeur
L’exemple B de la section 2 a justement été construit pour reproduire ce « dommage concentré ».
En alignant les 26 mots de code de NO:20260725-004873 (A) et de NO:20260725-104873 (B), on trouve 12 emplacements différents : 2 mots de code de données, et les 10 mots de code de correction d’erreurs qui en découlent.
En rapprochant 7 de ces 12 emplacements de la valeur de B, le motif obtenu se retrouve à 7 mots de code de distance de A, et à 5 mots de code de distance de B. Avec une capacité de correction de 5, le décodeur interprète cela comme « B, sali à 5 endroits », et corrige vers B.
| Condition | Combinaisons testées | Erreurs de lecture vers B |
|---|---|---|
| 7 mots de code rapprochés de B (distance 5 par rapport à B) | 792 combinaisons | 792 combinaisons (100 %) |
| 8 mots de code rapprochés de B (distance 4 par rapport à B) | 495 combinaisons | 495 combinaisons (100 %) |
Sans exception, chaque combinaison a produit une erreur de lecture. La ligne du bas correspond à une distance de 4 par rapport à B, c’est-à-dire à l’intérieur même de la capacité de correction fixée par le standard. Même une implémentation qui respecte le mot de code p réservé contre le mauvais décodage aboutit au même résultat dès que le dommage augmente d’un mot de code. p ne fait que réduire la probabilité, il n’empêche rien.
Le B présenté à la section 2 correspond, parmi ces combinaisons, à celle où le dommage se regroupe en une seule bande verticale (31 modules). En le minimisant, on pouvait descendre jusqu’à 23 modules. Deux implémentations totalement indépendantes, OpenCV 5.0.0 et jsQR 1.4.0, renvoient toutes les deux NO:20260725-104873.
4.3. Comment interpréter cet écart
Pour être honnête, ce type de dommage a peu de chances de survenir par hasard. Avec une salissure aléatoire, nous avons obtenu zéro erreur de lecture sur 9 700 essais. Il ne s’agit pas d’un risque qui « pourrait se réaliser dès demain ».
Il y a pourtant trois raisons de ne pas l’ignorer.
- Les dommages réels ne sont pas aléatoires. Un pli suit une ligne droite, le frottement pendant le transport se concentre sur un même bord, et l’encrassement d’une tête d’impression forme des rayures verticales. La bande de dommage utilisée ici est un exemple de ce type de « dommage concentré dans l’espace ». Cela dit, B combine 17 modules passés de blanc à noir et 14 modules passés de noir à blanc, un changement dans les deux sens qu’une simple perte d’encre ne peut pas reproduire. Les deux sens se produisent simultanément dans des cas comme un décalage du seuil de binarisation dû à l’ombre d’un pli, un chevauchement entre salissure et effacement, ou l’apposition partielle d’une autre étiquette par-dessus.
- Le nombre de scans est d’un tout autre ordre de grandeur. Une probabilité négligeable pour un seul essai change de nature sur un site qui effectue des dizaines de milliers de lectures par jour. De plus, comme une erreur de lecture ne génère aucune erreur, elle ne laisse aucune trace, et finit traitée comme un écart d’inventaire d’origine inconnue.
- Pouvoir le construire intentionnellement signifie que quelqu’un d’autre le peut aussi. Le motif utilisé ici a été construit mécaniquement à partir d’une valeur cible choisie à l’avance. Pour un QR où il existe une motivation à le falsifier — étiquette de prix, coupon —, cela devient une méthode d’attaque.
5. Le « lu mais faux » qui n’a rien à voir avec la correction d’erreurs
C’est ce que l’on rencontre le plus souvent en pratique. Cela se produit même quand la correction d’erreurs fonctionne parfaitement, et ce n’est même plus une question de probabilité.
5.1. Ne lire que le premier symbole d’un QR divisé
Le QR code dispose d’un mécanisme (Structured Append) permettant de répartir des données longues sur plusieurs symboles, à charge pour le lecteur de les concaténer. Voici le premier des 3 symboles obtenus en divisant NO:20260725-004873/LOT:AB-77/QTY:120/EXP:20270131.
Il a l’apparence d’un QR code ordinaire, sans aucun indice qu’il ne s’agit que d’un symbole parmi trois. Lu isolément, OpenCV renvoie ceci :
NO:20260725-00487
Pas d’erreur, pas d’avertissement. Un numéro de bordereau tout à fait plausible, dont il manque simplement la clé de contrôle finale 3. Les deuxième et troisième symboles donnent respectivement 3/LOT:AB-77/QTY: et 120/EXP:20270131. La même image donnée à jsQR renvoie une chaîne vide. Ce qui se passe lorsqu’une application qui ne prévoit pas le QR divisé scanne par hasard le premier symbole dépend entièrement du décodeur.
Voici comment cela se manifeste en pratique. Sur un écran qui recherche le numéro de bordereau par correspondance de préfixe ou avec LIKE, NO:20260725-00487, amputé de son dernier chiffre, correspond quand même au bordereau d’origine, et comme « ça a lu, et le bordereau est sorti », personne ne remarque l’anomalie. Tant que la longueur n’est pas vérifiée de manière stricte, ce fragment continue de circuler jusqu’au bout comme une lecture parfaitement normale.
5.2. Codage des caractères et ECI
Voici un QR code contenant 部品番号 東-004873 encodé en Shift_JIS, sans indication d’ECI.
Celui-ci aussi est réel. En le lisant avec le lecteur que vous avez sous la main, vous obtiendrez soit 部品番号 東-004873, soit une chaîne mal encodée, soit rien du tout — ce qui vous indiquera quelle interprétation applique votre lecteur.
En faisant lire cette image par l’outil de comparaison de lecture de QR code, vous verrez apparaître côte à côte la séquence d’octets brute extraite par jsQR, ainsi que le résultat de sa réinterprétation en UTF-8, Shift_JIS, EUC-JP, etc. Vous pouvez constater directement comment la même séquence d’octets devient quelque chose de complètement différent selon le codage de caractères choisi.
Voici le résultat obtenu en générant le même contenu avec différentes combinaisons de codage de caractères et d’indication d’ECI, puis en le faisant lire par les deux décodeurs. Les symboles correspondant à ces 4 conditions sont tous inclus dans les exemples de l’outil. Les valeurs du tableau ont toutefois été mesurées avec le cv2 de Python, et seule la troisième ligne donne un résultat différent avec la version navigateur (voir les détails juste après ce tableau). Comme l’outil ne permet de vérifier que le comportement de la version navigateur, reproduire le « échec par exception » de la troisième ligne nécessite le cv2 de Python.
| Condition de génération | OpenCV 5.0.0 | jsQR 1.4.0 |
|---|---|---|
| Shift_JIS / sans ECI | Renvoie une chaîne mal encodée comme un succès | Chaîne vide |
| UTF-8 / sans ECI | 部品番号 東-004873 |
部品番号 東-004873 |
| Shift_JIS / avec ECI | Échec du décodage avec avertissement | Chaîne vide |
| UTF-8 / avec ECI | 部品番号 東-004873 |
部品番号 東-004873 |
La première ligne est le pire cas. OpenCV a interprété la séquence d’octets comme du Latin-1 et a renvoyé la chaîne corrompue \x95\x94\x95i... sans aucune erreur. Du point de vue de l’application, il s’agit d’une lecture normale, et si elle est insérée telle quelle en base de données, on obtient un enregistrement au contenu mal encodé.
Voici comment cela se manifeste en pratique. Une chaîne mal encodée est enregistrée dans le champ désignation d’un mouvement d’entrée en stock, et le lendemain arrive une demande du type « la recherche par ce numéro de pièce ne remonte rien ». Comme relire la même étiquette produit à nouveau la même valeur, le terrain rapporte que « la recherche du système est cassée », et les allers-retours se poursuivent jusqu’à ce que la cause — le codage de caractères côté lecture — soit identifiée.
Il ne s’agit pas d’un bug d’OpenCV : c’est un comportement conforme à l’interprétation par défaut définie par le standard actuel (ISO/IEC 8859-1).3 Ce qui s’écarte du standard, c’est le fait d’avoir encodé du Shift_JIS sans indication d’ECI.
La troisième ligne mérite elle aussi qu’on s’y arrête. Bien que l’ECI (le mécanisme qui déclare explicitement le codage de caractères) ait été correctement spécifié, OpenCV a affiché l’avertissement QR: ECI is not supported properly et a échoué par exception faute de pouvoir interpréter la valeur renvoyée comme de l’UTF-8. Un renversement bien réel se produit ici : le QR construit fidèlement au standard est celui qui ne se lit pas.
Autre point : cette même troisième ligne donne un résultat différent selon le binding de langage, alors qu’il s’agit du même OpenCV. Le tableau ci-dessus donne le résultat obtenu avec le cv2 de Python, mais en soumettant la même image à la version navigateur (opencv.js 5.0.0), aucune exception n’apparaît : elle renvoie comme un succès la chaîne ���i��� ��-004873, criblée de caractères de remplacement. La raison est qu’Emscripten, en convertissant un std::string en UTF-8, remplace les octets invalides par U+FFFD au lieu de lever une exception. Version et image identiques, seul le langage d’appel a changé. Python, qui permet au moins de s’en apercevoir grâce à l’exception, est encore le moins pire des deux : la version navigateur, elle, prétend avoir « réussi » tout en renvoyant une valeur corrompue. Vous pouvez le vérifier vous-même avec les exemples de l’outil.
5.3. Plusieurs QR codes dans le même cadre
Plusieurs QR codes imprimés sur un même bordereau, l’étiquette du carton voisin qui entre dans le champ — des situations banales. Nous avons testé avec trois QR codes alignés horizontalement.
Tout d’abord, sur cette image, l’API de lecture unique d’OpenCV n’a rien renvoyé. Cela ne garantit toutefois pas que « l’API rejette d’elle-même la présence de plusieurs codes ». L’API de lecture unique est seulement documentée comme détectant et décodant un seul QR code ; rien n’indique qu’elle rejette plusieurs symboles, et selon la disposition, elle pourrait très bien en renvoyer un seul. Ne vous servez pas de l’absence de valeur renvoyée par l’API de lecture unique comme substitut à une véritable détection de codes multiples.
Qu’en est-il alors de l’API de lecture multiple ? C’est là que les choses se compliquent.
Voici le résultat obtenu en réutilisant l’image ci-dessus (NO: / ITEM: / LOT: de gauche à droite) telle quelle, en ne modifiant que le nombre de pixels. Cela correspond à des situations réelles où la distance à la cible ou la résolution de la caméra varient sur un scanner.
| Largeur de l’image | Ordre de retour |
|---|---|
| 1 001 px | NO: / LOT: / ITEM: |
| 1 502 px | Rien n’est renvoyé |
| 2 002 px | NO: / LOT: / ITEM: |
| 3 003 px | NO: / ITEM: / LOT: |
| 4 004 px | LOT: / NO: / ITEM: |
La même image, mais un simple changement de résolution suffit à modifier l’ordre. Ce n’est ni un ordre de gauche à droite, ni un ordre par taille. Il existe même une résolution pour laquelle rien n’est lisible. L’ordre est déterminé par des considérations internes à l’algorithme de détection, et comme rien ne le fixe, c’est ce qui se produit.
Autrement dit, un code qui suppose que « le premier élément est forcément le numéro de bordereau » et utilise l’index 0 peut fonctionner aujourd’hui par pur hasard, et dès demain, un simple rapprochement de la caméra suffira à lui faire attraper un autre code. C’est le genre de défaut difficile à reproduire et difficile à diagnostiquer.
Voici comment cela se manifeste en pratique. Sur le poste de contrôle, le bordereau visé et l’étiquette du carton voisin entrent simultanément dans le champ. L’application qui utilise l’index 0 pointe vers le mauvais bordereau, et l’instruction d’expédition est validée pour celui-ci. L’opérateur, persuadé d’avoir visé la bonne étiquette, ne s’aperçoit de rien jusqu’à ce qu’on lui signale, après l’expédition, qu’un « produit différent » est arrivé.
Côté réception, il faut choisir en fonction du contenu, pas de l’ordre. Récupérer tous les résultats avec l’API de lecture multiple, ne retenir que ceux dont le préfixe et le format correspondent, et lever une erreur s’il y en a zéro ou plusieurs — voilà la façon sûre de procéder.
5.4. Le contenu lui-même n’est de toute façon pas garanti correct
Il existe une couche de problèmes qu’un traitement d’image ne peut, par principe, jamais détecter : les données à l’origine de l’impression sont erronées, une ancienne étiquette est restée sur le carton avant remplacement, l’étiquette d’un autre partenaire s’est mélangée, l’étiquette a été copiée.
Le QR code ne vous dit que « ce qui y est écrit ». « Si c’est correct » ou « si c’est bien nous qui l’avons émis » ne peut être vérifié que par celui qui le reçoit.
Voici comment cela se manifeste en pratique. Sur le côté d’un carton réutilisé, l’ancienne étiquette est restée en place, et c’est elle qui est lue au lieu de la nouvelle étiquette sur le dessus. Comme il s’agit d’un QR parfaitement correct en tant que valeur, le contrôle de format, la clé de contrôle et le rapprochement avec les données de référence passent tous, et le colis part vers l’ancienne destination. Cette seule couche-là échappe entièrement à toute vérification portant sur la valeur lue.
6. Comment traiter la valeur reçue
La parade tient en une phrase : empiler sa propre validation en dehors de la correction d’erreurs.
| Étage | Contenu de la vérification | Ce qu’il permet de détecter |
|---|---|---|
| 1. Contrôle de format | Correspondance exacte de la longueur, du type de caractères, des séparateurs et du préfixe | Lecture d’un autre code, fragment de QR divisé, texte mal encodé |
| 2. Auto-vérification | Clé de contrôle | Détecte à coup sûr un changement d’un seul caractère ; peut manquer un changement de plusieurs caractères |
| 3. Validation métier | Rapprochement avec les données de référence, et correspondance avec l’objet en cours de traitement | Ancienne étiquette, étiquette d’un autre partenaire, confusion avec un autre bordereau |
Le numéro de bordereau utilisé en exemple suit le format NO: + date de commande sur 8 chiffres + numéro séquentiel sur 5 chiffres + 1 clé de contrôle, cette dernière suivant la même méthode modulo 10, poids 3 que GS1. L’erreur de lecture NO:20260725-104873 de la section 2 est arrêtée à ce stade : la clé de contrôle correcte pour 2026072510487 est 0, ce qui ne correspond pas au 3 figurant sur l’étiquette.
Avant d’écrire du code, fixez d’abord vos hypothèses sur le codage des caractères. L’implémentation ci-dessous part du principe que le numéro de bordereau n’est composé que de chiffres ASCII, et calcule la clé de contrôle par « caractère − '0' ». Si des chiffres pleine chasse ou la séquence d’octets mal encodée vue en 5.2 atteignent ce calcul, le résultat perd tout son sens. C’est pourquoi l’expression régulière du contrôle de format utilise [0-9] plutôt que \d, dans un ordre qui empêche tout chiffre non-ASCII d’atteindre le calcul de la clé de contrôle. Fixer les hypothèses vient en premier, les garantir par le contrôle de format vient ensuite, et le calcul vient après. Si cet ordre est bousculé, tous les contrôles qui suivent tournent à vide.
using System.Linq;
using System.Text.RegularExpressions;
public sealed record ScanOutcome(bool Accepted, string? SlipNo, string Reason);
public static class SlipScanValidator
{
// NO: + date de commande sur 8 chiffres + '-' + numéro séquentiel sur 5 chiffres + 1 clé de contrôle
// Terminer par \z, pas par $. En .NET, $ correspond aussi juste avant un retour
// à la ligne final, ce qui laisserait passer "NO:20260725-004873\n".
// Utiliser [0-9] plutôt que \d pour les chiffres. En .NET, \d correspond à tous
// les chiffres Unicode, y compris les chiffres pleine chasse, alors que le calcul
// de la clé de contrôle en aval suppose de l'ASCII
private static readonly Regex Format =
new(@"\ANO:(?<date>[0-9]{8})-(?<seq>[0-9]{5})(?<cd>[0-9])\z", RegexOptions.Compiled);
public static ScanOutcome Validate(string? raw, ISlipRepository repo)
{
// 1. Ne pas transformer « n'a pas pu être lu » en « succès vide ».
// Selon l'implémentation, un décodeur signale l'échec par null, une chaîne vide ou une exception
if (string.IsNullOrEmpty(raw))
return new(false, null, "Lecture impossible. Veuillez rescanner");
// 2. Contrôle de format. Vérifier une correspondance exacte incluant la longueur, pas un préfixe.
// Le fragment de QR divisé "NO:20260725-00487" est rejeté ici
var m = Format.Match(raw);
if (!m.Success)
return new(false, null, $"Ce n'est pas le format d'un QR de bordereau ({Describe(raw)})");
// 3. Auto-vérification. Un chiffre corrompu par une correction erronée est détecté ici
var body = m.Groups["date"].Value + m.Groups["seq"].Value;
if (Modulus10Weight3(body) != m.Groups["cd"].Value[0] - '0')
return new(false, null, "La clé de contrôle ne correspond pas. Vérifiez si l'étiquette est sale");
// 4. Validation métier. Le bordereau existe-t-il, et son état permet-il de le traiter maintenant ?
var slip = repo.Find(raw);
if (slip is null)
return new(false, null, "Aucun bordereau correspondant");
if (slip.Status != SlipStatus.WaitingForShipment)
return new(false, null, $"Ce bordereau est à l'état « {slip.Status} ». Il n'est pas prêt pour l'expédition");
return new(true, raw, "OK");
}
// Même méthode modulo 10, poids 3 que GS1. Applique les poids 3,1,3,1... depuis la droite
private static int Modulus10Weight3(string body)
{
var sum = 0;
for (var i = 0; i < body.Length; i++)
{
var weight = (body.Length - i) % 2 == 1 ? 3 : 1;
sum += (body[i] - '0') * weight;
}
return (10 - sum % 10) % 10;
}
// La valeur lue est une entrée externe. Ne conserver que l'ASCII imprimable avant
// de l'afficher à l'écran ou de l'écrire dans un journal. char.IsControl ne filtre
// que la catégorie Unicode Control (Cc) et laisse passer les caractères Format (Cf)
// comme U+202E (inversion du sens d'écriture). Écrire une liste blanche, pas une liste noire
private static string Describe(string raw)
{
var kept = raw.Where(c => c >= ' ' && c <= '~').Take(40).ToArray();
if (kept.Length == 0) return "(chaîne non affichable)";
var safe = new string(kept);
return kept.Length < raw.Length ? safe + "…(certains caractères ont été retirés)" : safe;
}
}
Le design des clés de contrôle elles-mêmes — les formules et le choix du modèle — est présenté dans Conception des codes pour les systèmes métier et clés de contrôle. L’intérêt de ce contrôle, dans le contexte de cet article, est qu’il fonctionne aussi bien contre les erreurs de frappe humaines que contre les erreurs de lecture d’une machine.
Ce que ces trois étages ne protègent pas
Même avec les trois étages en place, trois failles subsistent. Chacune peut être comblée en poussant la conception de la validation un cran plus loin ; voyons-les aussi.
La clé de contrôle laisse passer les changements portant sur plusieurs caractères. Ce que la méthode modulo 10, poids 3 détecte à coup sûr, c’est une erreur sur un seul caractère. En pratique, 2026072500487 et 2026072517487 donnent tous deux une clé de contrôle de 3, si bien que NO:20260725-174873 traverse le deuxième étage sans encombre. Une correction erronée ne change pas nécessairement un seul caractère : on ne peut donc pas faire l’impasse sur le troisième étage.
Ne faites pas s’arrêter le rapprochement avec les données de référence à une simple « vérification d’existence ». Le repo.Find() et le contrôle d’état du code ci-dessus ne confirment qu’une chose : « un bordereau utilisable existe quelque part ». Si un opérateur lit l’étiquette du carton voisin, celle-ci satisfait elle aussi le format, la clé de contrôle et l’état « en attente d’expédition », et passe donc sans problème. La valeur lue doit être rapprochée de l’objet en cours de traitement : correspond-elle à l’élément suivant de la liste de picking, est-elle rattachée à l’identifiant de conteneur déjà scanné, la destination d’expédition est-elle la même que celle du départ en cours ? Ce avec quoi on effectue ce rapprochement varie selon le métier, si bien que ce point-là ne peut pas se réduire à du code générique.
La validation ne peut pas empêcher un double traitement. Si deux terminaux lisent presque simultanément la même étiquette, chacun vérifie l’état « en attente d’expédition » avant de le mettre à jour, si bien que les deux passent. C’est au niveau de l’exécution qu’il faut s’en prémunir, en rendant la transition d’état conditionnelle — du type UPDATE ... WHERE status = 'WaitingForShipment' — atomique en une seule opération, ou en absorbant les réexécutions avec une clé d’idempotence. La validation est un jugement à l’entrée, elle ne remplace pas un contrôle d’exclusion mutuelle.
« Accident » et « attaque » sont deux problèmes différents
Ce que protègent les trois étages vus jusqu’ici, ce sont les accidents. Erreur de lecture due à une salissure, fragment de QR divisé lu par erreur, texte mal encodé, ancienne étiquette mélangée — les erreurs sans intention malveillante sont arrêtées ici.
En revanche, pour les usages où il existe une motivation à falsifier (étiquettes de prix, coupons, billets d’entrée, paiement), cela n’offre aucune protection. Un attaquant peut respecter le format, recalculer la clé de contrôle, et construire librement un QR pointant vers un autre numéro bien réel. Comme le rapprochement avec les données de référence ne vérifie que l’existence, il passe sans problème.
Si la possession du QR équivaut à une valeur ou à un droit, la valeur elle-même doit porter une authenticité vérifiable. Il faut soit utiliser un jeton imprévisible émis par le serveur (un nombre aléatoire suffisamment long) pour qu’on ne puisse pas déduire un numéro à partir d’un autre, soit joindre au contenu un MAC à clé ou une signature électronique que le destinataire vérifie avec la clé. Dans les deux cas, l’état « utilisé » doit être géré côté serveur pour empêcher la double utilisation d’une copie.
Cela dit, l’authenticité seule n’arrête pas la « substitution ». Si l’on décolle un QR authentique d’un produit bon marché pour le recoller sur un produit cher, le jeton et la signature restent parfaitement valides. Dans les situations où le support est réutilisable, comme une étiquette de prix qu’on peut décoller et recoller, même la vérification de l’état « utilisé » ne suffit plus. Ce qui fonctionne ici relève de la même logique que le troisième étage : vérifier, par une voie indépendante de la valeur, si ce QR correspond bien à l’objet que l’on a sous les yeux — récupérer l’identification du produit par un autre moyen et la recouper, la confronter au contexte de la transaction (détail de caisse, créneau horaire d’entrée), ou la lier physiquement avec une étiquette qui se déchire au décollement.
Ni la clé de contrôle ni le rapprochement avec les données de référence ne garantissent quoi que ce soit sur l’authenticité. Et l’authenticité elle-même ne garantit pas que cette valeur correspond bien à l’objet qu’on a sous les yeux. L’essentiel est de ne pas chercher à couvrir « la protection contre les erreurs de lecture », « la protection contre la contrefaçon » et « la protection contre la substitution » avec un seul et même mécanisme.
7. Ce qu’il faut décider côté exploitation
Il reste des points que le code seul ne peut pas couvrir.
- Toujours faire figurer, sous le QR, une chaîne de caractères lisible par un humain. C’est le même principe que le HRI (Human Readable Interpretation) défini par GS1.4 Cela laisse un moyen de vérification humaine en cas de suspicion d’erreur de lecture. En pratique, il n’est pas rare que ce soit le seul moyen de détection disponible. L’exploitation terrain des codes-barres en général est traitée dans Les bases des normes de codes-barres GS1 et les précautions d’exploitation sur le terrain.
- Définir à l’avance la procédure en cas de « lecture impossible ». Nombre maximal de tentatives de rescan, repli sur la saisie manuelle, personne habilitée à l’approuver. Si ce point reste flou, le terrain aura tendance à « essayer encore et encore en changeant l’angle jusqu’à ce que ça lise », ce qui augmente justement la probabilité d’une erreur de lecture.
- Conserver dans un journal les valeurs rejetées. Si elles se concentrent sur une étiquette ou un terminal particulier, cela permet de détecter tôt un dysfonctionnement de l’imprimante ou du scanner. N’écrivez toutefois jamais la chaîne brute telle quelle dans un journal orienté ligne : une valeur contenant des retours à la ligne ou des caractères de contrôle peut falsifier des lignes de journal ou casser l’affichage. Enregistrez l’original en le tronquant à une longueur donnée et en l’échappant, dans un champ de journal structuré ou une colonne de base de données, et ne faites apparaître dans les lignes lisibles par un humain qu’une représentation neutralisée — mettez cette séparation en place dès le départ. Conservez aussi l’image lue si possible.
- Ne pas utiliser de QR divisé. Si les données ne tiennent pas, augmentez la version, ou ne mettez dans le QR qu’un identifiant et récupérez le reste depuis les données de référence. Cette seconde approche permet en plus de réduire la taille de l’étiquette et de corriger le contenu sans avoir à la réimprimer.
- Résoudre le problème du codage des caractères en « ne l’utilisant pas ». Limitez les QR à usage professionnel à la plage ASCII. En mode octet, sans indication d’ECI, aucun codage de caractères n’est déclaré, et l’interprétation par défaut change selon la version du standard.3 Écrire simplement en UTF-8 ne garantit pas l’interopérabilité : un scanner appliquant une autre interprétation produira du texte mal encodé. Si vous devez absolument inclure des caractères non-ASCII, la solution correcte selon le standard est de déclarer l’UTF-8 avec une indication d’ECI, mais comme on l’a vu à la section 5.2, certaines implémentations gèrent l’ECI de manière douteuse : on ne peut donc pas se dispenser d’une vérification sur les appareils réellement visés.
- Insérer une confirmation avant toute opération irréversible. Pour les opérations coûteuses à annuler — validation d’expédition, déduction de stock, lettrage de paiement —, affichez à l’écran, pour vérification humaine, le nom de l’article ou le montant obtenu à partir de la valeur lue. Une erreur de lecture a beau paraître valide en tant que valeur, elle paraît souvent incohérente dans le contexte métier.
Le niveau de validation à mettre en œuvre dépend de l’ampleur des dégâts en cas d’erreur.
| Usage | Contrôle de format | Clé de contrôle | Rapprochement données de référence | Vérification humaine |
|---|---|---|---|---|
| Emplacement interne / numéro d’étagère | Obligatoire | Facultatif | Recommandé | Non nécessaire |
| Entrées/sorties de stock, inventaire | Obligatoire | Recommandé | Obligatoire | Non nécessaire |
| Validation d’expédition, déduction de stock | Obligatoire | Obligatoire | Obligatoire | Recommandé |
| Facturation, lettrage de paiement | Obligatoire | Obligatoire | Obligatoire | Obligatoire |
| Prévention des erreurs sur médicaments / matières dangereuses | Obligatoire | Obligatoire | Obligatoire | Obligatoire |
8. En résumé
La correction d’erreurs d’un QR code est un mécanisme conçu pour restaurer le mot de code d’origine à partir du motif imprimé. Elle remplit bien ce rôle. Les mesures le confirment aussi : face à une salissure aléatoire ou à une dégradation uniforme de la qualité d’image, elle lit correctement dans les limites de sa capacité de correction, et renonce sagement à la lecture au-delà.
Mais cela n’a rien à voir avec le fait que la chaîne reçue par l’application soit correcte du point de vue métier. Selon l’endroit touché par le dommage, la correction peut aboutir à une autre valeur, tout aussi valide en apparence. Quant au QR divisé, au codage des caractères ou à un autre code présent dans l’image, ce sont des problèmes qui se situent en dehors de la correction d’erreurs, et qui ne relèvent même pas de la probabilité.
Le fait que le standard lui-même écrive qu’il en résulte un « décodage erroné vers un mot de code apparemment valide mais différent », et qu’il réserve délibérément des mots de code pour s’en prémunir, résume bien cette structure. Même avec tout cela, on ne fait que réduire la probabilité — c’est là le point d’arrivée du standard. Seule l’application qui reçoit la valeur peut combler ce qui reste.
Une valeur lue est une entrée externe non vérifiée. La traiter exactement comme une chaîne saisie au clavier — c’est, à mon sens, la bonne façon de composer avec un QR code.
Trois étapes pour tester votre propre QR
Tout ce qui précède peut être vérifié directement avec les étiquettes de votre propre entreprise.
- Générez. Prenez une valeur au format que vous utilisez réellement en production, et transformez-la en QR avec un outil de génération que vous avez sous la main (les exemples de cet article ont été générés avec segno 1.6.6). Vérifiez d’abord qu’elle se lit correctement à l’état intact.
- Endommagez-la. Avec un logiciel de retouche d’image, tracez une bande verticale unique, censée représenter un pli ou un encrassement de tête d’impression. Ce qui compte, c’est la concentration, pas la quantité : ne dispersez pas un léger bruit sur toute la surface, mais regroupez-le dans une zone étroite.
- Comparez avec deux décodeurs. Faites-la lire par l’outil de comparaison de lecture de QR code, et comparez les résultats de jsQR et d’OpenCV.js. Vous pourrez y constater directement des comportements tels qu’un seul des deux renvoyant une valeur, ou les deux renvoyant la même valeur mais différente de l’originale.
Si les résultats divergent entre les deux décodeurs, cette condition est de celles que la conception de validation de votre entreprise doit impérativement prendre en charge. Cela vaut la peine de tester une fois, sur son propre bureau, si « le scanner de mon site tient le coup ».
Environnement de vérification
Les sections 2 à 4, qui observent le comportement de la correction d’erreurs, utilisent uniformément la version 1-M (21×21 modules). Les exemples de la section 5 changent de version selon le volume de données ; ils sont donc précisés section par section.
| Élément | Contenu |
|---|---|
| Décodeur 1 | OpenCV 5.0.0 cv2.QRCodeDetector |
| Décodeur 2 | jsQR 1.4.0 (Node.js 22) |
| Génération | segno 1.6.6 / Python 3.11 |
| Symboles des sections 2 à 4 | Version 1-M / 21×21 (26 mots de code = 16 de données + 10 de correction d’erreurs) |
| Section 5.1 (QR divisé) | Les 3 symboles en version 1-M / 21×21 |
| Section 5.2 (codage des caractères et ECI) | Version 2-Q pour Shift_JIS, version 2-M pour UTF-8 (25×25 dans les deux cas). Le japonais, sur 18 à 23 octets, ne tient pas dans les 16 mots de code de données de la version 1-M |
| Section 5.3 (codes multiples) | NO: et ITEM: en version 1-M ; LOT:AB-77, dont les données sont courtes, passe en version 1-H car segno relève le niveau de correction d’erreurs (21×21 dans tous les cas) |
-
DENSO WAVE Incorporated, À propos de la fonction de correction d’erreurs | QRcode.com ↩
-
ISO/IEC 18004 Information technology — Automatic identification and data capture techniques — QR code bar code symbology specification. La formule de la capacité de correction d’erreurs
e + 2t ≦ d - p, la valeur du mot de codepréservé contre le mauvais décodage, ainsi que la mention « décodage erroné vers un mot de code apparemment valide mais différent » figurent en 8.5.1 Error correction capacity ; le(26,16,4)de la version 1-M et la note de bas de page « pour réduire la probabilité de mauvais décodage, la capacité de correction est fixée à moins de la moitié du nombre de mots de code de correction d’erreurs » figurent dans le Table 13 (citations basées sur l’édition de 2000). L’édition en vigueur est ISO/IEC 18004:2024. ↩ ↩2 ↩3 ↩4 -
L’interprétation par défaut du mode octet en l’absence d’indication d’ECI a changé selon les éditions du standard. La section 8.3.1 d’ISO/IEC 18004:2000 stipulait « The default interpretation for QR Code is ECI 000020 representing the JIS8 and Shift JIS character sets », mais depuis l’édition de 2006 (QR Code 2005), c’est ECI 000003, c’est-à-dire ISO/IEC 8859-1, qui fait office de défaut. S’appuyer sur un défaut non déclaré signifie aussi que l’interprétation peut changer du simple fait d’un changement d’édition du standard. ↩ ↩2
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
La conception des codes dans les systèmes métier — Comment définir les codes produit et client, et la clé de contrôle
Un guide pratique pour définir le système de codes des systèmes métier — codes produit, codes client, etc. Il couvre le tableau de décisi...
Bonnes pratiques de multithreading en pratique — édition .NET : ce qu'il faut décider avant d'ajouter des threads
Un ensemble de bonnes pratiques de conception pour .NET/C# afin d'éviter que « démarrer un thread » ne fasse planter ou geler l'applicati...
Comment choisir la communication inter-processus sous Windows ── Tableau de décision : tubes nommés / TCP / gRPC / mémoire partagée / COM
Comment choisir le moyen de faire communiquer des applications Windows entre elles ? Cet article organise les tubes nommés, le TCP local,...
Qu'est-ce que le .NET Generic Host ? - Le socle de la DI, de la configuration et des logs
Cet article clarifie le rôle du Generic Host à travers ses liens avec la DI, la configuration, les logs, IHostedService et BackgroundServ...
Qu'est-ce que .NET Native AOT ? - Les différences avec JIT et le trimming
Ce qu'est Native AOT en .NET, expliqué à travers ses différences avec JIT, ReadyToRun, self-contained, single-file, trimming et les sourc...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Conseil technique et revue de conception
Clarification de la stratégie de modification, de la conception et du traitement des actifs existants.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Passer le niveau de correction d'erreurs à H permet-il d'éviter les erreurs de lecture ?
- Non. Augmenter le niveau accroît la quantité de salissure que l'on peut corriger, mais cela ne fait pas disparaître le phénomène en lui-même : « la correction s'active et produit une autre valeur ». Le décodage de Reed-Solomon consiste à « chercher, à l'intérieur de la capacité de correction, s'il existe un mot de code proche du motif reçu » ; si le dommage se retrouve au voisinage d'un autre mot de code, c'est celui-ci qui est renvoyé comme réponse correcte (un dommage trop éloigné se solde simplement par « illisible »). L'ISO/IEC 18004 précise explicitement que cela aboutit à un « décodage erroné vers un mot de code apparemment valide mais différent », et réserve à part un jeu de mots de code destiné à réduire ce risque de mauvais décodage — mais il s'agit là aussi d'une mesure qui abaisse la probabilité, pas d'une garantie. Seule l'application qui reçoit la valeur peut détecter une erreur de lecture.
- Concrètement, à quelle fréquence les erreurs de lecture de QR code se produisent-elles ?
- Avec une salissure aléatoire, cela n'arrive quasiment jamais. Dans les mesures réalisées pour cet article, sur 3 900 essais avec inversion aléatoire de modules et 5 800 essais avec corruption aléatoire de mots de code, aucun cas n'a renvoyé une valeur erronée. Un dommage qui dépasse la capacité de correction bascule du côté de « illisible ». Cela dit, les dommages réels ne sont pas aléatoires : ils présentent un biais de position, comme les plis, les frottements ou l'encrassement d'une tête d'impression. De plus, l'omission d'un fragment de QR divisé ou la confusion entre plusieurs codes ne relèvent même pas de la probabilité : dès que les conditions sont réunies, cela se produit à tous les coups.
- Le QR code dispose déjà d'une correction d'erreurs : la clé de contrôle (check digit) est-elle vraiment nécessaire ?
- Oui. Ce sont des couches de protection différentes. La correction d'erreurs traite la cohérence interne du symbole, et elle ne garantit la restauration que dans les limites de sa capacité de correction. Au-delà, un dommage peut se retrouver « restauré » vers un autre mot de code parfaitement valide mais différent. La clé de contrôle, elle, vérifie que « la chaîne reçue par l'application est bien conforme au système de codage ». Elle détecte à coup sûr un changement d'un seul caractère, mais peut passer à côté d'un changement portant sur plusieurs caractères. Une valeur de contrôle en modulo 10 n'a que dix possibilités, et cet article donne justement un exemple où un changement de deux chiffres laisse la clé de contrôle inchangée. C'est pourquoi il faut considérer la clé de contrôle comme la couche qui arrête l'essentiel des erreurs de lecture, et confier la décision finale au rapprochement avec les données de référence et à la vérification métier. L'avantage est que ce même contrôle protège aussi bien la saisie manuelle que la retranscription ou l'import venant d'une autre source.
- Le smartphone a réussi à le lire avec son appli caméra : cela ne veut-il pas dire que la valeur est correcte ?
- « Avoir réussi à lire » signifie seulement que le décodeur a renvoyé une chaîne non vide ; cela ne dit rien sur l'exactitude de son contenu. La façon de signaler un échec diffère aussi selon l'implémentation — exception, chaîne vide ou null sont tous possibles —, si bien qu'utiliser « aucune exception n'a été levée » comme critère de succès est également dangereux. Dans les mesures de cet article, il y a eu plusieurs cas où OpenCV et jsQR renvoyaient des résultats différents pour la même image. Pour un QR contenant du japonais en Shift_JIS, l'un a renvoyé une chaîne mal encodée comme un succès, tandis que l'autre a renvoyé une chaîne vide. Les résultats ont aussi divergé sur le premier symbole d'un QR divisé. Le fait d'avoir pu lire ne dit rien sur l'exactitude de la valeur.
- Vaut-il mieux éviter le QR divisé (Structured Append) en usage professionnel ?
- Sauf raison particulière, il est plus prudent de l'éviter. Le QR divisé est un mécanisme qui répartit les données sur plusieurs symboles, à charge pour le lecteur de les rassembler et de les concaténer, mais le comportement d'un décodeur qui ne le prend pas en charge et ne reçoit que le premier symbole dépend de l'implémentation. Dans les mesures de cet article, OpenCV a renvoyé sans erreur un numéro de bordereau tronqué en fin de chaîne, alors que jsQR a renvoyé une chaîne vide. Puisque certaines implémentations laissent passer un fragment comme s'il s'agissait d'une valeur plausible, si vous n'anticipez pas le QR divisé, il faut une validation qui rejette les résultats incomplets. Si les données ne tiennent pas dans un seul symbole, il est plus sûr d'augmenter la version du QR, ou de raccourcir le code et de vous appuyer davantage sur un renvoi vers les données de référence.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.