NSString à l'envers
hdex
Membre
Je vous jure je fais pas expres de trouver des questions à la c.n :adios!:
Bon alors voila, je cherche à lire une NSString mais en partant de la fin.
Pour l'instant je fais une boucle ou je lis chaque caractère de la fin au début mais c'est pas super efficace
Ya-t-il un moyen plus simple ? En plan B, j'ai une solution en Python (print text[::-1]) mais ça veut dire integrer PyObjC et j'ai jamais fait
Merci d'avance
Bon alors voila, je cherche à lire une NSString mais en partant de la fin.
Pour l'instant je fais une boucle ou je lis chaque caractère de la fin au début mais c'est pas super efficace
Ya-t-il un moyen plus simple ? En plan B, j'ai une solution en Python (print text[::-1]) mais ça veut dire integrer PyObjC et j'ai jamais fait
Merci d'avance
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Le plus efficace, ça doit être ça :
ça utilise la représentation interne directement je crois.
Bon j'ai pas vérifié le code mais tu vois l'idée. En gros bosser direct sur les buffers C, bien plus rapide que via [tt]characterAtIndex:[/tt] qui, comme le dit la doc, n'est pas optimisé surtout si on doit l'appeler plusieurs fois. (voir Discussion sur la méthode [tt]getCharacters:[/tt])
[EDIT]
Bon, j'ai été grillé par schlum, mais je poste quand même car mon code a une grande différence : j'ai fait exprès de ne pas utiliser [tt]getCharacters:[/tt] puisque, comme le dit la doc : . Donc autant utiliser getCharacterAtIndex: dans ce cas, qui est sans doute ce que hdex faisait déjà et ne doit pas être le plus optimisé...
Je pensais qu'il récupérait directement le buffer, ce qui aurait été plus logique vu qu'en interne il bosse en unicodeÂ
Il y a un petit problème dans ton code... Que fais-tu des caractères qui ne sont pas convertibles en "isoLatin" ? Il me semble que ça shoute une exception si l'on utilise pas "allowLossyConversion"Â :-\\
Et il y aura le même problème pour tous les "one byte per char" qui ne permettent de coder par définition que 256 caractères...Â
Il faudrait donc travailler en UTF-8 en faisant gaffe, car du coup la fonction d'inversion devient sacrement plus complexe.
Ah oui, mais attends ! Ils parlent de l'implémentation abstraite ; pas de l'implémentation telle qu'elle est faite dans NSString !
Si je mets un point d'arrêt sur [NSString getCharacterAtIndex:], ça ne s'arrête pas dans mon code, alors que le point d'arrêt sur [NSString getCharacters:] le stoppe bienÂ
Ceci dit sinon si besoin, il y a aussi un autre moyen : utiliser l'encodage UTF-16, qui correspond exactement aux codepoints unicode (représentés par le type "unichar") pour la page 1 de Unicode (codepoints entre 0x0000 et 0xFFFF)... qui se trouve être la seule page que l'on sache gérer en interne (unichar = unsigned short = sur 16 bits)
Du coup en demandant la CStringWithEncoding:UTF16, on récupère le buffer sous sa forme canonique UTF16 qui se trouve être donc la forme unicode brute pour le coup. Il suffit de l'interprêter comme "const unichar*" et de travailler sur le tableau de unichar (et non plus sur un tableau de char)...
Y'a donc que l'encodage à changer dans mon code, + remplacer "char*" par "unichar*" et ça devrait aussi le faire.
Mais bon si getCharacters n'est pas si mauvais en perfs que ne le laisse penser la doc, alors faut pas s'en priver
Il faudrait utiliser l'UTF-32, qui est toujours sur 32 bits.
[Edit] Ceci dit, tu as raison sur le fait que "unichar" = 2 octets
[Re-Edit] En fait, tu as raison, de 0x0000 à 0xFFFF ça tient sur une fois 16 bits
Mais dans ce cas je ne vois pas comment ils peuvent intégrer le type d'endian utilisé
http://fr.wikipedia.org/wiki/UTF-16
- pour la page 1 de unicode, chaque codepoint unicode (chaque caractère) est codé sur 16 octets.
- ce n'est que pour les pages supérieures (donc les codepoints unicode > 0xFFFF) que les caractères sont alors codés sur 2 mots de 16 bits... mais je ne suis pas sûr que NSString gère les pages unicode supérieures à la page 1, si ?
D'autant plus que la définition d'un "unichar" c'est "unsigned short", soit un mot de 16 bits... qui n'est pas suffisant pour stocker le codepoint unicode des caractères autres que ceux de la page 1 de Unicode. Or la représentation d'une chaà®ne unicode n'est qu'un tableau des codepoints de chacun de ses caractères (ce qui la rend indépendant de toute transformation unicode UTF* et de tout encodage de caractère)
Donc certes il n'y a pas bijection entre la représentation UTF16 d'une chaà®ne et sa représentation Unicode... sauf pour la page 1 de Unicode... qui est à ma connaissance la seule utilisée (et gérée ?) par NSString (même pour les chaà®nes utilisant les diacritiques combinatoires, et pour les katanas&hiraganas, qui sont eux aussi dans la page 1... finalement ultra rares sont les applications sachant gérer autre chose que la page 1 d'ailleurs)
Ah ben tiens je viens de lire ta page sur Wikipedia : C'est exactement ce dont je parle depuis le début : l'UCS-2 définit des codepoints pour chaque caractères (un "identifiant" unique si tu veux). Et il se trouve que vu la façon dont UTF-16 est défini, même si les deux ne sont pas synonymes, ils sont en bijection pour les caractères dont le codepoint est entre 0x0000 et 0xFFFF.
Je commence à connaà®tre par coeur toutes ces notions d'unicode à force de les avoir manipulés en bas niveau, moi
Je viens de relire plus attentivement et d'éditer mes messages au dessus (bizarrement, tes réponses n'étaient pas affichées quand j'ai édité avec la fonction d'édition rapide, et le forum ne m'a pas averti :-\\)
J'ai du mal à saisir cette histoire d'Endian avec l'UTF-16 là Â ???
Il me semble que tous les caractères chinois ne sont pas dans la page 1, si ?
Pourtant NSString semble les supporter ???
Moi j'ai compris ça pour l'utilisation qu'on pouvait en faire
Par contre la taille est faussée, il se croit avec un NSString de taille 4 pour deux caractères idéogrammes unifiés CJK d'extension B (unicode 2000B et 20021)
Et évidemment quand j'utilise ma routine d'inversion, ça donne n'importe quoi
On doit pouvoir le faire directement avec getCharacters si on comprend comment fonctionne l'encodage interne de NSString (probablement de l'UTF-16 du coup), mais je ne m'y connais pas assez en encodages pour le faire
Peut-être que AliGator pourrait nous le faire
Maintenant, c'est bizarre que la méthode "length" ne gère pas ça alors qu'en interne, il est tout à fait capable de se débrouiller pour le convertir en UTF-8 par exemple :-\\
On peut choisir l'encodage de la NSString, voici les encodages disponibles depuis Mac OS X 10.0 :
Donc déjà ça ça vous donne une bonne indication de ce dont il s'agit, sachant qu'il n'est précisé aucune taille de caractères pour NSUnicodeStringEncoding, il est seulement dit qu'il s'ait de la forme canonique.
Mais depuis Leopard, nous avons quelques types en plus pour la gestion des encodages plus grands :
Donc avec ces encodages, aucune ambiguà¯té à priori.
"getCharacters" remplit un tableau de "unicode", et ce type fait 16 bits.
De plus, la méthode "length" répond faux dès que le NSString a des caractères hors de la table 0x0000-0xFFFF
[Edit] Ceci-dit, si tu peux confirmer ou infirmer que "length" renvoie toujours une réponse fausse sous Leopard...
The number returned includes the individual characters of composed character sequences, so you cannot use this method to determine if a string will be visible when printed or how long it will appear.
Donc la réponse 4 est tout à fait normale avec deux caractères chinois... Puisque ceux-ci sont chacun composés de deux unichar. Si on met un caractère ASCII avec un caractère chinois, tu auras une longueur de 3, un unichar pour contenir le a et 2 pour le caractère chinois.
En revanche il y a pas mal de méthode qui permettent d'obtenir une NSString ou une chaà®ne C avec la garantie que les caractères auront tous une forme spécifique, on peut donc, par exemple, récupérer une chaà®ne de caractères avec un encodage 32 bits, l'inverser 32 bit spar 32 bits et la remettre dans une NSString ou la gérer telle quelle.
L'UTF-8 est un codage fortement variable en longueur. Pour la page 1, un caractère peut être encodé en 1, 2 ou 3 octets (4 octets pour les caractères >0xFFFF), et ces octets suivent un codage bien précis, que ton code reprend en gros (je n'ai fait que le lire en diagonale mais à première vue ça ressemble aux codes habituels de manipulation bas niveau de l'UTF8 ;D)
Alors qu'en UTF-16, du moment que tu ne traites que les caractères 0x0000≤c≤0xFFFF ce qui est le cas de 99.9% des usages (et les seuls autres usages ne correspondent plus à des langages forcément à base de caractères dans lesquels une inversion de l'ordre des caractères serait d'autant plus bizarre), ça te permet de travailler directement avec les codepoints unicode.
Mais, et c'est sans doute encore mieux, je n'avais pas vu "l'encodage" (qui n'en n'est pas vraiment un stricto sensu d'ailleurs) "NSUnicodeStringEncoding", qui retourne non pas une représentation formattée des caractères (au sens des Unicode Transformation Formats) mais une représentation binaire mémoire de la chaà®ne... et donc directement les valeurs des codepoints, et non leur représentation telle qu'elle doit être envoyée à un flux de sortie (dans un fichier, à l'écran, ou autre)
Donc au final sans doute que reprendre mon code mais en remplaçant l'encodage par NSUnicodeStringEncoding, et traiter le buffer retourné come un tableau de "unichar", est sans doute la solution idéale... si on ne veut pas passer par getCharacters.
Sinon pour répondre à ton interrogation de l'utilisation des caractères >0xFFFF, de toute façon la méthode getCharacterAtIndex: (qui est celle à utiliser si on veut être sûr de respecter l'interface minimale fournie par NSString, au risque de ne pas être optimisée, quoique) renvoie un "unichar". Donc qu'en est-il des katanas&hiraganas et autres caractères dont le codepoint est >0xFFFF avec cette méthode ? C'est exactement le même problème qu'avec le cas particulier que tu évoques, et qui, entre nous, n'arrive jamais, ou s'il arrive, je ne suis pas sûr qu'il soit correctement géré par les NSString.
----
L'endianness pour l'UTF-16, quant-à elle pour répondre à ta question, n'a un sens que lorsqu'on a des caractères codés sur deux mots (de 2 octets chacun), pour savoir dans quel ordre ces 2 mots sont (LE ou BE).
- La transformation unicode "UTF-8" spécifie pour sa part un ordre. On ne travaille pas vraiment avec des octets mais avec des entités représentant un codepoint, entités pouvant se trouver coder sur 8,16,24 ou 32 bits, dans un ordre donné.
- La transformation UTF-16, elle, parle de mots de 16 bits. Un caractère unicode de codepoint >0xFFFF est transformé en UTF16 en 2 "shorts" (2 mots) : et c'est là que l'endianness intervient, pour indiquer si c'est le mot de poids fort qui est en premier ou celui de poids faible.
Tu obtiendras en revanche 4 octets par caractères avec NSUTF32StringEncoding.
En clair lisez la doc tout y est écrit.
Pour obtenir des chaà®nes unicodes avec des normalisations spécifiques, on peut utiliser les méthodes suivantes :
Du coup il faut donc idéalement transformer la NSString en sa forme C (le KC altérerait la chaà®ne), puis récupérer les codepoints, inverser leur ordre, et recomposer une NSString... même si la precomposition n'est pas forcément une opération optimale, donc faut voir l'utilisation... Après, pour récupérer les codepoints, je suggère de récupérer ça en utilisant l'encodage "Unicode" soulevé par psychoh13, ou à défaut l'UTF-32 même si sa représentation est inefficace, ou si on sait qu'on utilisera le plan basique multilangue (BMP, que j'appelle plan 1 plus haut), UTF16...
Mais comme quoi, c'est pas si évident que ça...
[*]Si on veut être simpliste, on prend les caractères un par un dans l'ordre inverse, point barre.
[*]Si on veut être plus rigoureux, et pouvoir gérer les chaà®nes éventuellement sous forme unicode décomposée (NFD), faut appliquer les méthodes citées par psychoh13 pour passser en forme NFC.
[*]Et si on veut pouvoir gérer toutes les pages unicode, ça commence à se compliquer, parce qu'il faut gérer le cas des formes décomposées qui n'ont pas d'équivalent précomposé (comme par exemple un caractère auquel est affecté deux ou trois diacritiques, qui sont plus rarement présents en précomposé...), et le cas des idéogrammes CJK qui sont dans les pages supérieures (cp>0xFFFF) et ont des comportement parfois spéciaux (oui, un codepoint unicode ce n'est pas forcément un "vrai caractère", ça peut être un "modifieur de caractère" par exemple, comme justement les signes diacritiques entre autres... et là , ça commence à être sérieusement costaud... et entre nous je suis pas sûr que beaucoup d'applis ayant à travailler sur de l'unicode gèrent ces cas si particuliers
J'en reste cependant à penser qu'en effet il ne faut pas oublier de convertir la chaà®ne en forme precomposed, mais qu'ensuite à moins d'utiliser les idéogrammes CJK, l'UTF16 reste le plus logique à manipuler, puisqu'il retournera de toute façon un mot de 2 octets, donc un unichar, comme le fait [tt]getCharactersAtIndex:[/tt] qui a donc le même comportement sur ce plan alors que c'est la méthode préconisée...
[EDIT] codepoints bidi ?? (nooann...)
Tiens, idée, à moins qu'on puisse faire mumuse avec les codepoints spéciaux de Unicode permettant de faire du "bidi" (écriture right-to-left au lieu de left-to-right)...
Mais de même, ce n'est alors pas la représentation interne qu'on change mais une information ajoutée pour la couche de rendu des chaà®nes, ce qui n'est pas du tout pareil. D'ailleurs getCharacterAtIndex: sur une chaà®ne ainsi "renversée" par ajout d'infos "bidi" n'inverserait pas l'ordre des caractères, ce n'est qu'à l'interprétation lors du dessin de la chaà®ne à l'écran que ça sera visible...
Conclusion : tout dépend du besoin exact en fait Qu'est ce qu'on veut inverser ? l'ordre des caractères ? des glyphes ? des codepoints ? (qui sont des choses distinctes ! comme quoi le texte c'est pas si simple ;D) ou le sens dans lequel la chaà®ne est rendue quand dessinée à l'écran ?... Ca change tout ;D
Effectivement Renaud, pas si idiote Bon ben maintenant j'ai 2 pages de lecture de retard
Encore merci a tous, j'ai du boulot maintenant !!
Euh ouais, mais il est quand même indiqué :
Or un caractère chinois est un caractère unicode, il n'y a aucun doute là dessus, même s'il tient sur 2 unichar !
Ce n'est pas un "composed character sequences" comme peuvent l'être certains caractères arabes par exemple il me semble (ou les accents avec certains encodages dont l'UTF-16 décomposé de HFS+, ou les ligatures).
Ma fonction "reverseUTF8" gère tout ça, non ? Y a-t-il des cas dans lesquels elle va se planter ?
Parce que somme toutes, elle est assez simple, même si on doit pouvoir faire mieux avec des encodages 32 bits fixes.
Bah pour commencer, faut déjà voir que dans 0xFFFF caractères une grosse parties des caractères chinois sont encodés.
Tu vas par exemple trouver les Katakana et Hiragana japonais qui couvrent seulement 192 caractères. Ou encore les "idéogrammes unifiés CJK d'extension A" qui couvre 6592, ça fait déjà une proportion. Et encore, dans 0xFFFF 65536 caractères sont encodés.
Et ensuite, tu peux aussi remarquer que beaucoup de caractères chinois sont en fait une composition d'autres caractères, j'ignore cependant s'ils peuvent être composés par une séquence comme les diacritiques, mais en tout cas ça peut s'envisager.
Cependant, par "caractères unicode" je pense qu'Apple sous-entends l'UTF-16 , et c'est sans doute aussi pour ça qu'ils précisent la valeur de retour dans la discussion...
[EDIT] D'ailleurs quand tu utilises un caractère dans la série que je viens d'indiquer (de 0x3400 à 0x4DC0) tu verras que la méthode -length retourne bel et bien 1, c'est-à -dire 1 unichar.
C'est vraiment quand on passe au dessus qu'il y a un hic. Et pourtant ils sont très bien gérés par NSString, tous les logiciels basés sur Cocoa les acceptent sans problème !
C'est pour ça que je pense que la représentation interne est de l'UTF-16 effectivement, et que le coup du "length" et un petit bug ; ou alors un choix mal documenté, car la discussion concerne vraiment les caractères décomposés, donc à priori ça pas les vrais caractères unicodes qui tiennent sur 4 octets en UTF-16 (qui sont les mêmes que ceux qui prennent 4 octets en UTF-8).
Il y a le même problème avec la méthode "getCharacter:" d'ailleurs, dont la définition est :
Ils parlent vraiment de "caractère", et pas de "unichar", alors que c'est inexact.
Il y a l'alphabet phonétique "deseret" des mormons par exemple entre 0x10400 et 0x1044FÂ
Quelques symboles musicaux aussi, et les monogrammes, diagrammes et tétragrammes "Tà i Xuà¡n JÄ«ng" (entre 0x1D300 et 0x1D356).
À mon avis aussi, on parle surtout de "caractères composés" comme étant simplement plusieurs codes UTF-16 devant être utilisés comme un seul caractère.
De plus, il ne faut pas oublier que ces méthodes ne sont là que pour garantir l'utilisabilité du reste des méthodes, mais que finalement, toutes les sous-classes privées font ce qu'elles veulent.
Il est tout à fait possible de stocker sa chaà®ne de caractères dans un simple char * et de retourné des "char" castés en unichar...
La seule exigence de -length et -characterAtIndex: c'est que : [maStr characterAtIndex:[maStr length] - 1] ne fasse pas planter l'application.
Donc pour revenir au sujet, la solution pour gérer tous les cas, c'est :
1 - Le passer en précomposé canonique
2a - Utiliser la forme UTF-8 (c'est le seul encodage accepté sur Tiger pour l'export des caractères unicodes > 0xFFFF ; même NSUnicodeStringEncoding ne fonctionne pas)
2b - Utiliser getCharacters ; si l'unichar est entre D800 et DFFF (zone interdite), le caractère est sur 32 bits, sinon, sur 16 bits
La solution 2a donne ce que j'ai mis au-dessus avec juste une petite modif :
La solution 2b donne un truc du genre :
ça doit être la plus optimisée...
(Et effectivement, si la chaà®ne est en "decomposedStringWithCompatibilityMapping", et qu'on oublie les "precomposedStringWithCanonicalMapping", l'accent saute sur la lettre d'après à l'inversion).
- La plage 0xD800 à 0xDFFF est inutilisée
- 0x10000 = 0xD800+0xDC00
...
- 0x103FF = 0xD800+0xDFFF
- 0x10400 = 0xD801+0xDC00
...
Par contre, on en peut pas dire que ce sont des caractères composés quand même, parce que les méthodes "precomposedStringWith..." et "decomposeStringWith..." ne jouent pas dessus.
Et je me suis rendu compte après coup que c'est le standard UTF-16
Donc NSString utilise en interne l'UTF-16 que ça soit les caractères sur 16 ou 32 bits, mais les caractères sur 32 bits sont comptés doubles par les fonctions utilisant "unichar" (et "length").
A la composition ou au rendu des caractères, c'est autre chose, il peut tout à fait récupérer getCharacterAtIndex et voir que c'est un codepoint unicode particulier indiquant que le caractère est sur 2 mots de 16 bits, donc faire un rendu différent à l'écran (mise en place de l'accent, etc), mais ça ne l'empêche pas de voir en interne les chaà®nes comme un tableau de unichar, dont la méthode "length" retourne la longueur (donc nombre de unichars, et pas nombre de caractères)
La question reste donc : faut-il utiliser la représentation interne (utilisée par Apple) et juste l'inverser, au risque d'avoir... finalement les mêmes erreurs que celles introduites par Apple autour de length et getCharacterAtIndex:
Quoique... en fait c'est la grande différence entre caractèrse et glyphs : si on veut inverser l'ordre des caractères, c'est pas trop compliqué, mais si on veut inverser l'ordre des glyphs, il faut prendre en compte toutes les règles de composition qui s'appliquent à Unicode (accents, mais aussi tous les unicode non-character codepoints...)
Schlum dans ce cas je pense que ton code ne fait qu'inverser les codepoints unicode sans tenir compte des glyphes qu'ils représentent (et penser à tous les cas risque d'être casse gueule...), en particulier tu ne tiens pas compte des caractères qui ne peuvent pas être précomposés (genre imagine un e avec un ^ et un ¨ et une cédille... bon c'est un cas tiré par les cheveux mais au moins je suis sûr qu'il n'existe pas en précomposé celui-là ;D) et pour lesquels il faut donc inverser l'ordre des codes de composition. De même pour les caractères LTR et RTL indiquant le sens d'écriture de ta chaà®ne unicode, il faut convertir l'un en l'autre et vice-versa...
Et je suis pas sûr qu'on soit capable de créer un code qui inverse les glyphes (ordre de représentation visuelle des caractères rendus à l'écran) au lieu des caractères ou codepoints unicode en pensant à tous les cas (surtout pour les caractères que nous n'avons pas l'habitude d'utiliser dans nos langues). Et je ne parle même pas des ligatures...