NSString à  l'envers

hdexhdex Membre
09:15 modifié dans API AppKit #1
Je vous jure je fais pas expres de trouver des questions à  la c.n  :o :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  o:)

Merci d'avance
«13

Réponses

  • schlumschlum Membre
    09:15 modifié #2
    Bah ouais, c'est une question à  la c**  :P

    Le plus efficace, ça doit être ça :

    NSString *reverseString(NSString *str)<br />{<br />	unsigned int lg = [str length];<br />	int i;<br />	unichar buffer[lg],tmp;<br />	[str getCharacters:buffer];<br />	for(i=0;i&lt;lg/2;++i) {<br />		tmp = buffer[i];<br />		buffer[i] = buffer[lg-i-1];<br />		buffer[lg-i-1] = tmp;<br />	}<br />	return [NSString stringWithCharacters:buffer length:lg];<br />}
    


    ça utilise la représentation interne directement je crois.
  • AliGatorAliGator Membre, Modérateur
    décembre 2007 modifié #3
    Essaye d'opérer directement sur le buffer.

    (NSString*)reverseString:(NSString*)aString<br />{<br />&nbsp; const char * chars = [aString cStringUsingEncoding:NSISOLatin1StringEncoding]; // ISOLatin1 ou tout autre qui soit &quot;one byte per char&quot; (donc ne pas utiliser UTF8)<br />&nbsp; unsigned int i,L = [aString length];<br /><br />&nbsp; char * rev = (char*)malloc( (L+1)*sizeof(char) );<br />&nbsp; for(i=0;i&lt;L;i++)<br />&nbsp; &nbsp; rev[i] = chars[L-i-1]; // reverse the chars<br />&nbsp; rev[L] = 0; // NULL terminator<br />&nbsp; NSString * ret = [NSString stringWithCString:rev encoding:NSISOLatin1StringEncoding]; // utiliser le même qu&#39;au dessus, évidemment<br />&nbsp; free(rev);<br /><br />&nbsp; return ret;<br />}
    
    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 :
    The abstract implementation of this method uses characterAtIndex: repeatedly, correctly extracting the characters, though very inefficiently. Subclasses should override it to provide a fast implementation.
    . 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é...
  • schlumschlum Membre
    décembre 2007 modifié #4
    OK, j'avais pas vu pour getCharacters  :o
    Je pensais qu'il récupérait directement le buffer, ce qui aurait été plus logique vu qu'en interne il bosse en unicode  :o

    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...  B)


    Il faudrait donc travailler en UTF-8 en faisant gaffe, car du coup la fonction d'inversion devient sacrement plus complexe.
  • schlumschlum Membre
    09:15 modifié #5
    dans 1197469855:

    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 :
    The abstract implementation of this method uses characterAtIndex: repeatedly, correctly extracting the characters, though very inefficiently. Subclasses should override it to provide a fast implementation.
    . 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é...


    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  ;)
  • AliGatorAliGator Membre, Modérateur
    09:15 modifié #6
    Ah ouais en effet (j'ai pas pu faire le test, je suis au taf sous Windaube là  :P), bon test et bonne remarque ;)

    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 :)
  • schlumschlum Membre
    décembre 2007 modifié #7
    Raté, l'UTF-16 c'est une ou deux fois 16 bits par caractères  ;)

    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
  • schlumschlum Membre
    décembre 2007 modifié #8
    *** à  supprimer ***
  • AliGatorAliGator Membre, Modérateur
    09:15 modifié #9
    dans 1197472246:
    Raté, l'UTF-16 c'est une ou deux fois 16 bits par caractères  ;)
    Ben oui et non, c'est ce que je disais plus haut :
    - 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)
  • AliGatorAliGator Membre, Modérateur
    décembre 2007 modifié #10
    dans 1197472769:
    En fait, je viens de retrouver la raison pour l'UTF-16 ; il ne peut tenir sur une seule fois 16 bits pour des histoires d'endian-safe  ;)
    Ben non, si tu veux représenter des caractères unicodes au delà  de 0xFFFF cela tient bien comme tu disais sur 2 mots de 16 bits et non plus un seul... mais dans ce cas il faut préciser si c'est de l'UTF16-LE ou UTF16-BE (LittleEndian ou BigEndian). C'est d'ailleurs un des rôles du BOM (Byte-Order Mark) qu'on a au début des fichiers encodés en UTF16.


    Ah ben tiens je viens de lire ta page sur Wikipedia :
    L'UTF-16 n'est pas l'UCS-2 qui est le codage, plus simple, de chaque caractère sur deux octets. Ces deux normes sont pourtant appelées toutes les deux Unicode, car le codage est le même tant que l'on utilise pas les plages U+D800 à  U+DFFF (en principe réservées) et les plages après U+FFFF (peu utilisées en occident).
    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 :D
  • 09:15 modifié #11
    dans 1197464106:
    Je vous jure je fais pas expres de trouver des questions à  la c.n
    Question idiote? À voir les réponses je ne dirais pas.
  • schlumschlum Membre
    09:15 modifié #12
    dans 1197473119:

    Ah ben tiens je viens de lire ta page sur Wikipedia :
    L'UTF-16 n'est pas l'UCS-2 qui est le codage, plus simple, de chaque caractère sur deux octets. Ces deux normes sont pourtant appelées toutes les deux Unicode, car le codage est le même tant que l'on utilise pas les plages U+D800 à  U+DFFF (en principe réservées) et les plages après U+FFFF (peu utilisées en occident).
    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 :D


    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à Â  ???
  • schlumschlum Membre
    09:15 modifié #13
    dans 1197473031:

    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)


    Il me semble que tous les caractères chinois ne sont pas dans la page 1, si ?
    Pourtant NSString semble les supporter  ???

    dans 1197473235:

    dans 1197464106:
    Je vous jure je fais pas expres de trouver des questions à  la c.n
    Question idiote? À voir les réponses je ne dirais pas.


    Moi j'ai compris ça pour l'utilisation qu'on pouvait en faire :D
  • schlumschlum Membre
    09:15 modifié #14
    Bon, après test il les gère, il peut même les afficher avec NSLog !
    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  :o
  • schlumschlum Membre
    09:15 modifié #15
    La routine qui inverse tous les caractères :

    NSString *reverseStringUTF8(NSString *str)<br />{<br />	const char *buff = [str UTF8String];<br />	int lg = strlen(buff);<br />	char buff2[lg+1];<br />	int i = 0, j = lg;<br />	while(i&lt;lg) {<br />		// 1 byte<br />		if((buff[i]&amp;0x80)==0x00)<br />			buff2[--j] = buff[i++];<br />		// 2 bytes<br />		else if((buff[i]&amp;0xE0)==0xC0) {<br />			j -= 2;<br />			memcpy(&amp;buff2[j],&amp;buff[i],2);<br />			i += 2;<br />		// 3 bytes<br />		} else if((buff[i]&amp;0xF0)==0xE0) {<br />			j -= 3;<br />			memcpy(&amp;buff2[j],&amp;buff[i],3);<br />			i += 3;<br />		// 4 bytes<br />		} else if((buff[i]&amp;0xF8)==0xF0) {<br />			j -= 4;<br />			memcpy(&amp;buff2[j],&amp;buff[i],4);<br />			i += 4;<br />		// error, skip<br />		} else <br />			buff2[--j] = buff[i++];<br />	}<br />	buff2[lg] = &#39;&#092;0&#39;;<br />	return [NSString stringWithCString:buff2<br />							&nbsp; encoding:NSUTF8StringEncoding];<br />}
    


    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 :-\\
  • psychoh13psychoh13 Mothership Developer Membre
    09:15 modifié #16
    Je vais vous répondre pour l'encodage, il suffit de regarder la documentation pour comprendre. :D

    On peut choisir l'encodage de la NSString, voici les encodages disponibles depuis Mac OS X 10.0 :

    enum {<br /> &nbsp; NSASCIIStringEncoding = 1,<br /> &nbsp; NSNEXTSTEPStringEncoding = 2,<br /> &nbsp; NSJapaneseEUCStringEncoding = 3,<br /> &nbsp; NSUTF8StringEncoding = 4,<br /> &nbsp; NSISOLatin1StringEncoding = 5,<br /> &nbsp; NSSymbolStringEncoding = 6,<br /> &nbsp; NSNonLossyASCIIStringEncoding = 7,<br /> &nbsp; NSShiftJISStringEncoding = 8,<br /> &nbsp; NSISOLatin2StringEncoding = 9,<br /> &nbsp; NSUnicodeStringEncoding = 10,<br /> &nbsp; NSWindowsCP1251StringEncoding = 11,<br /> &nbsp; NSWindowsCP1252StringEncoding = 12,<br /> &nbsp; NSWindowsCP1253StringEncoding = 13,<br /> &nbsp; NSWindowsCP1254StringEncoding = 14,<br /> &nbsp; NSWindowsCP1250StringEncoding = 15,<br /> &nbsp; NSISO2022JPStringEncoding = 21,<br /> &nbsp; NSMacOSRomanStringEncoding = 30,<br /> &nbsp; NSProprietaryStringEncoding = 65536<br />};
    


    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 :
    <br /> &nbsp; NSUTF16BigEndianStringEncoding = 0x90000100,<br /> &nbsp; NSUTF16LittleEndianStringEncoding = 0x94000100,<br /> &nbsp; NSUTF32StringEncoding = 0x8c000100,<br /> &nbsp; NSUTF32BigEndianStringEncoding = 0x98000100,<br /> &nbsp; NSUTF32LittleEndianStringEncoding = 0x9c000100,
    


    Donc avec ces encodages, aucune ambiguà¯té à  priori. :D
  • schlumschlum Membre
    décembre 2007 modifié #17
    Oui, on a bien compris, là  n'est pas le problème ; on parle de la représentation interne là  ; ton "enum" ne sert que pour les imports / exports...
    "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... :)
  • psychoh13psychoh13 Mothership Developer Membre
    09:15 modifié #18
    J'infirme que length renvoie toujours une réponse fausse sur Leopard... Il faut juste relire la documentation. Voilà  ce qui est dit sur -length :

    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. :D

    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.
  • AliGatorAliGator Membre, Modérateur
    09:15 modifié #19
    Ben là  t'es en train de descendre bas niveau dans l'UTF-8, alors qu'en UTF-16 ce serait plus simple à  gérer.
    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.
  • psychoh13psychoh13 Mothership Developer Membre
    09:15 modifié #20
    Si tu utilises la méthode -lengthOfBytesUsingEncoding: tu auras la taille de ta chaà®ne de caractères en octets selon l'encodage employé. Donc avec NSUnicodeStringEncoding tu obtiendras 2 octets pour un caractères inférieur à  0xFFFF et 4 avec des caractères au-dessus.
    Tu obtiendras en revanche 4 octets par caractères avec NSUTF32StringEncoding.
    En clair lisez la doc tout y est écrit. :D

    Pour obtenir des chaà®nes unicodes avec des normalisations spécifiques, on peut utiliser les méthodes suivantes :
    • -[NSString decomposedStringWithCanonicalMapping] : Returns a string made by normalizing the receiver's contents using Form D.
    • -[NSString decomposedStringWithCompatibilityMapping] : Returns a string made by normalizing the receiver's contents using Form KD.
    • -[NSString precomposedStringWithCanonicalMapping] : Returns a string made by normalizing the receiver's contents using Form C.
    • -[NSString precomposedStringWithCompatibilityMapping] : Returns a string made by normalizing the receiver's contents using Form KC.

  • AliGatorAliGator Membre, Modérateur
    décembre 2007 modifié #21
    dans 1197492715:

    Pour obtenir des chaà®nes unicodes avec des normalisations spécifiques, on peut utiliser les méthodes suivantes :
    • -[NSString decomposedStringWithCanonicalMapping] : Returns a string made by normalizing the receiver's contents using Form D.
    • -[NSString decomposedStringWithCompatibilityMapping] : Returns a string made by normalizing the receiver's contents using Form KD.
    • -[NSString precomposedStringWithCanonicalMapping] : Returns a string made by normalizing the receiver's contents using Form C.
    • -[NSString precomposedStringWithCompatibilityMapping] : Returns a string made by normalizing the receiver's contents using Form KC.

    Pas con ça, c'est vrai qu'il faut mieux mettre en en forme C (precomposed) parce que sinon, les caractères decomposed mis à  l'envers ça va donner des trucs marrants, genre les accents qui sont sur la lettre précédente puisqu'on aura inversé la chaà®ne ^^

    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
  • hdexhdex Membre
    09:15 modifié #22
    dans 1197473235:

    dans 1197464106:
    Je vous jure je fais pas expres de trouver des questions à  la c.n
    Question idiote? À voir les réponses je ne dirais pas.


    Effectivement Renaud, pas si idiote  ;) Bon ben maintenant j'ai 2 pages de lecture de retard  :o

    Encore merci a tous, j'ai du boulot maintenant !!
  • schlumschlum Membre
    décembre 2007 modifié #23
    dans 1197489515:

    J'infirme que length renvoie toujours une réponse fausse sur Leopard... Il faut juste relire la documentation. Voilà  ce qui est dit sur -length :

    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. :D

    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 (ou les accents dans certains encodages, donc celui du FileSystem HFS+).


    Euh ouais, mais il est quand même indiqué :

    Return Value

    The number of Unicode characters in the receiver.


    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).
  • schlumschlum Membre
    09:15 modifié #24
    dans 1197495546:

    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 ;)


    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.
  • psychoh13psychoh13 Mothership Developer Membre
    décembre 2007 modifié #25
    dans 1197497065:
    Euh ouais, mais il est quand même indiqué :

    Return Value

    The number of Unicode characters in the receiver.


    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).


    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.
  • schlumschlum Membre
    décembre 2007 modifié #26
    Oui, bien sûr, tant que c'est en dessous de 0xFFFF, aucun problème  ;)
    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 :
    Return Value

    The character at the array position given by index.


    Ils parlent vraiment de "caractère", et pas de "unichar", alors que c'est inexact.
  • schlumschlum Membre
    décembre 2007 modifié #27
    Et sinon, il n'y a pas que les idéogrammes unifiés CJK d'extension B dans les > 0xFFFF
    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).
  • psychoh13psychoh13 Mothership Developer Membre
    09:15 modifié #28
    À mon avis dans la documentation, le problème vient à  la fois d'un respect du standard de base et aussi d'un héritage plus ancien. Les méthodes -characterAtIndex: et -length sont les méthodes primitives de NSString et sont donc là  depuis l'origine.
    À 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. :D
  • schlumschlum Membre
    décembre 2007 modifié #29
    Oui, effectivement...

    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 :
    NSString *reverseStringUTF8(NSString *str)<br />{<br />	const char *buff = [[str precomposedStringWithCanonicalMapping] UTF8String];<br />	int lg = strlen(buff);<br />	char buff2[lg+1];<br />	int i = 0, j = lg;<br />	while(i&lt;lg) {<br />		// 1 byte<br />		if((buff[i]&amp;0x80)==0x00)<br />			buff2[--j] = buff[i++];<br />		// 2 bytes<br />		else if((buff[i]&amp;0xE0)==0xC0) {<br />			j -= 2;<br />			memcpy(&amp;buff2[j],&amp;buff[i],2);<br />			i += 2;<br />			// 3 bytes<br />		} else if((buff[i]&amp;0xF0)==0xE0) {<br />			j -= 3;<br />			memcpy(&amp;buff2[j],&amp;buff[i],3);<br />			i += 3;<br />			// 4 bytes<br />		} else if((buff[i]&amp;0xF8)==0xF0) {<br />			j -= 4;<br />			memcpy(&amp;buff2[j],&amp;buff[i],4);<br />			i += 4;<br />			// error, skip<br />		} else <br />			buff2[--j] = buff[i++];<br />	}<br />	buff2[lg] = &#39;&#092;0&#39;;<br />	return [NSString stringWithCString:buff2 encoding:NSUTF8StringEncoding];<br />}
    


    La solution 2b donne un truc du genre :

    NSString *reverseString(NSString *str)<br />{<br />	NSString *str2 = [str precomposedStringWithCanonicalMapping];<br />	unsigned int lg = [str2 length];<br />	unichar buff[lg],buff2[lg];<br />	[str2 getCharacters:buff];<br />	int i = 0, j = lg;<br />	while(i&lt;lg) {<br />		// 1 car<br />		if((buff[i]&amp;0xD800)!=0xD800)<br />			memcpy(&amp;buff2[--j],&amp;buff[i++],sizeof(unichar));<br />		// 2 cars<br />		else {<br />			j -= 2;<br />			memcpy(&amp;buff2[j],&amp;buff[i],2*sizeof(unichar));<br />			i += 2;<br />		}<br />	}<br />	return [NSString stringWithCharacters:buff2 length:lg];<br />}
    


    ç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).
  • schlumschlum Membre
    décembre 2007 modifié #30
    En fait, en interne, la décomposition se fait de cette manière :

    - 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 :)

    Tout point de code qui n'est pas un non-caractère, et dont la valeur ne dépasse pas 2 octets (16 bits), c'est-à -dire tous les points de code U+0000 à  U+D7FF et U+E000 à  U+FFFD, est stocké sur un seul mot de 16 bits, dont les 5 bits de poids fort ne peuvent pas être égaux à  11011 (puisque la plage de non-caractères U+D800 à  U+DFFF est exclue).


    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").
  • AliGatorAliGator Membre, Modérateur
    décembre 2007 modifié #31
    Donc finalement, les bugs auxquels on a pensé et qu'on peut avoir quand on ne gère pas les caractères >0xFFFF (codés sur 2 mots = 4 octets en UTF-16) sont aussi présents chez Apple, puisque la méthode length ou getCharacterAtIndex n'en tiens pas compte...

    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...
Connectez-vous ou Inscrivez-vous pour répondre.