Comment vbscript filesystemobject encode-t-il les caractères?

Oct 24 2020

J'ai ce code vbscript:

    Set fs = CreateObject("Scripting.FileSystemObject")
    Set ts = fs.OpenTextFile("tmp.txt", 2, True)

    for i = 128 to 255
        s = chr(i)
        if lenb(s) <>2 then
            wscript.echo i
            wscript.quit
        end if
        ts.write s
    next
    ts.close

Sur mon système, chaque entier est converti en un caractère à deux octets: il n'y a pas de nombres dans cette plage qui ne peuvent pas être représentés par un caractère, et aucun nombre ne nécessite plus de 2 octets. Mais quand je regarde le fichier, je ne trouve que 127 octets.

Cette réponse: https://stackoverflow.com/a/31436726/1335492suggère que le FSO crée des fichiers UTF et insère une nomenclature. Mais le fichier ne contient que 127 octets et aucune marque d'ordre d'octet.

Comment le FSO décide-t-il comment encoder du texte? Quel encodage autorise les caractères 8 bits à un octet? Quels encodages n'incluent pas 255 caractères à un octet de 8 bits?

(Les réponses sur la façon dont FSO lit les personnages peuvent également être intéressantes, mais ce n'est pas ce que je demande spécifiquement ici)

Edit: J'ai limité ma question aux caractères à bits élevés, pour clarifier la question. (Les réponses sur les caractères low-bit peuvent également être intéressantes, mais ce n'est pas ce que je demande spécifiquement ici)

Réponses

3 JosefZ Oct 24 2020 at 17:32

FSO décide comment encoder le texte lors de l'ouverture du fichier. Utilisez l' formatargument comme suit:

Set ts = fs.OpenTextFile("tmp.txt", 2, True, -1)
'                                            ↑↑ 

Ressource: méthode OpenTextFile

Syntaxe


object.OpenTextFile(filename[, iomode[, create[, format]]])

Arguments

object- Obligatoire. Object est toujours le nom d'un FileSystemObject.

filename- Obligatoire. Expression de chaîne qui identifie le fichier à ouvrir.

iomode- Optionnel. Peut - être l' une des trois constantes: ForReading, ForWritingou ForAppending.

create- Optionnel. Valeur booléenne qui indique si un nouveau fichier peut être créé si le nom de fichier spécifié n'existe pas. La valeur est Truesi un nouveau fichier est créé, Falses'il n'est pas créé. S'il est omis, un nouveau fichier n'est pas créé.

format- Optionnel. L'une des trois valeurs Tristate utilisées pour indiquer le format du fichier ouvert.

TristateTrue = -1 to open the file as Unicode,
TristateFalse = 0 to open the file as ASCII,
TristateUseDefault = -2 to open the file as the system default.

S'il est omis, le fichier est ouvert au format ASCII .

3 david Nov 08 2020 at 16:43

Réponse courte:

L'objet du système de fichiers mappe «Unicode» à «ASCII» à l'aide de la page de codes associée aux paramètres régionaux du système. (Chr et ChrW utilisent les paramètres régionaux de l'utilisateur.)

Application:

Il peut y avoir des erreurs de transposition silencieuse entre la page de codes système et la page de codes de thread (utilisateur). Il peut également y avoir des erreurs de codage et de décodage si des points de code sont manquants dans une page de codes, ou, comme avec le japonais et UTF-8, les pages de codes contiennent des caractères multi-octets.

VBscript ne fournit aucune méthode native pour détecter la page de codes utilisateur, thread ou système. La page de code Thread (utilisateur) peut être déduite de la locale définie par SetLocale ou retournée par GetLocale (il y a une liste ici:https://www.science.co.il/language/Locale-codes.php), mais il ne semble pas y avoir de documentation MS. Sur Win2K +, WMI peut être utilisé pour interroger la page de codes système. La commande CHCP interroge et modifie la page de codes OEM, qui n'est ni l'utilisateur ni la page de codes système.

La page de codes système peut être usurpée par un manifeste d'application. Il n'y a aucun moyen pour une application (telle que cscript ou wscript) ou un script (tel que VBScript ou JScript) de changer son système parent sauf en créant un nouveau processus avec un nouveau manifeste. ou redémarrer le système après une modification du registre.

En détail:

 s = chr(i) 
'creates a Unicode string, using the Thread Locale Codepage. 

Les points de code qui n'existent pas lorsque les caractères sont mappés en tant que caractères de contrôle: 127 devient U + 00FF (qui est un caractère de contrôle Unicode standard), et 128 devient U + 20AC (le symbole de l'euro) et 129 devient 0081 (qui est un point de code dans une zone de caractères de contrôle Unicode). Dans VBScript, Thread Locale peut être défini et lu par SetLocale et GetLocale

    createobject("Scripting.FileSystemObject").OpenTextFile(strOutFile, 2, True).write s
   'creates a 'code page' string, using the System Locale Codepage. 

Windows peut gérer les valeurs Unicode qu'il ne peut pas mapper de deux manières: il peut soit mapper sur un caractère par défaut, soit renvoyer une erreur. "Scripting.FileSystemObject" utilise le paramètre d'erreur et lève une exception.

Plus en détail:

Les paramètres régionaux de thread sont, par défaut, les paramètres régionaux de l'utilisateur, qui sont le paramètre de format de date et d'heure dans l'applet du panneau de configuration "Région et langue" (appelés choses différentes dans différentes versions de Windows). Il a une page de codes associée. Selon l'expert en internationalisation de MS Michka (Michael Kaplan, RIP), la raison pour laquelle il a une page de codes est que les mois et les jours de la semaine peuvent être écrits dans les caractères appropriés, et il ne doit pas être utilisé à d'autres fins.

Les utilisateurs d'ASP-Classic avaient clairement d'autres idées, puisque Response.CodePage est un thread-locale, et peut être contrôlé par vbscript GetLocale et SetLocale entre autres méthodes. Si les paramètres régionaux de l'utilisateur sont modifiés, tous les processus sont notifiés et tout thread qui utilise la valeur par défaut est mis à jour. (Je n'ai pas testé ce qui arrive à un thread utilisant actuellement une valeur non par défaut).

Les paramètres régionaux du système sont également appelés «Langue pour les programmes non Unicode» et se trouvent également dans l'applet «Région et langue», mais nécessitent un redémarrage pour être modifiés. Il s'agit de la valeur utilisée en interne par Windows («Le système») pour mapper entre l'API «A» et l'API «W». La modification de ce paramètre n’a aucun effet sur la langue de l’interface graphique Windows (ce n’est pas un «programme non Unicode»)

En supposant que le paramètre "Heure et date" correspond à la "Langue pour les programmes non Unicode" , tout Chr (i) qui peut créer un point de code Unicode valide (voir "erreurs de mappage" ci-dessous) sera mappé exactement d'Unicode vers " page de codes ". Notez que cela fonctionne pour les points de code qui sont des "caractères de contrôle": notez également que cela ne fonctionne pas dans l'autre sens: UTF-CodePage-UTF ne fait pas toujours exactement un aller-retour. Notamment (Character, Modifer) -CodePage- (Complex Character) ne va pas correctement, où Unicode définit plus d'une façon de construire une représentation de caractère de langage.

Si "Heure et date" ne correspond pas à la "Langue pour les programmes non Unicode" , toute traduction pourrait avoir lieu, par exemple U + 0101 est 0xE0 sur cp28594 et 0xE2 sur cp28603: Chr (224) passerait par U + 0101 à écrire comme 226.

Même s'il n'y a pas d' erreurs de transposition , si "Heure et date" ne correspond pas à la "Langue pour les programmes non Unicode", le programme peut échouer lors de la traduction vers les paramètres régionaux du système: si le point de code Unicode n'a pas de page de code correspondante point de code, il y aura une exception de FileSystemObject.

Il peut également y avoir des erreurs de mappage à Chr (i), passant de la page de code à Unicode. La page de codes 1041 (japonais) est une page de codes à deux octets (probablement Shift JIS). 0x81 est (uniquement) le premier octet d'une paire à deux octets. Pour être cohérent avec les autres pages de codes, 0x81 doit mapper sur le caractère de contrôle 0081, mais lorsqu'il est donné 81 et la page de codes 1041, Windows suppose que l'octet suivant dans la mémoire tampon, ou dans le BSTR, est le deuxième octet du double octet paire (je n'ai pas déterminé si l'erreur est commise avant ou après la conversion). Chr (& H81) est mappé à U + xx81 (81, xx). Quand je l'ai fait, j'ai eu U + 4581, qui est un idéographe unifié CJK (Brasenia purpurca): il n'est pas mappé par la page de code 1041.

Les erreurs de mappage à Chr (1) ne provoquent pas d'exceptions VBScript au moment de la création. Si le point de code UTF-16 créé n'est pas valide ou n'est pas sur la page de codes des paramètres régionaux du système, il y aura une exception FileSystemObject à .write. Ce problème particulier peut être évité en utilisant ChrW (i) au lieu de Chr (i). Sur la page de codes 1041, ChrW (129) devient le caractère de contrôle Unicode 0081 au lieu de xx81.

Contexte:

Un programme peut mapper entre Unicode et "codepage" en utilisant n'importe quelle page de codes installée: les fonctions Windows MultiByteToWideChar et WideCharToMultiByte prennent [UINT CodePage] comme premier paramètre. Ce mécanisme est utilisé en interne dans Windows pour mapper l'API «A» à l'API «W», par exemple GetAddressByNameA et GetAddressByNameW. Windows est "W", (large, 16 bits) en interne, et les chaînes "A" sont mappées aux chaînes "W" à l'appel, et de retour de "W" à "A" au retour. Lorsque Windows effectue le mappage, il utilise la page de codes associée à la "Paramètres régionaux du système", également appelée "Langue pour les programmes non Unicode".

La fonction API Windows WriteFile écrit des octets, pas des caractères, ce n'est donc pas une fonction «A» ou «W». Tout programme qui l'utilise doit gérer la conversion entre les chaînes et les octets. La fonction c fwrite écrit des caractères, donc elle peut gérer des caractères 16 bits, mais elle n'a aucun moyen de gérer des points de code de longueur variable comme UTF-8 ou UTF-16: encore une fois, tout programme qui utilise "fwrite" doit gérer la conversion entre les chaînes et des mots.

La fonction C ++ fwrite peut gérer UTF et la fonction de compilateur _fwrite fait de la magie qui dépend du compilateur. Vraisemblablement, sous Windows, si la traduction de la page de codes est requise, l'API MultiByteToWideChar et WideCharToMultiByte est utilisée.

Les pages de codes "A" et l'API "A" ont été appelées "ANSI" ou "ASCII" ou "OEM" et ont commencé comme des caractères 8 bits, puis sont devenues des caractères codés sur deux octets et sont maintenant devenues UTF-8. (1..3 octets). L'API "W" a commencé avec des caractères 16 bits, puis est devenue UTF-16 (1..6 octets). Les deux sont des codages de caractères multi-mots: la distinction est que pour l'API "A" et les pages de codes, la longueur du mot est de 8 bits: pour l'API "W" et UTF-16, la longueur du mot est de 16 bits. Parce qu'ils sont tous deux des mappages multi-octets, et parce que "octet" et "mot" et "char" et "caractère" signifient des choses différentes dans des contextes différents, et parce que "W" et en particulier "A" signifient des choses différentes des années il y a, je viens d'utiliser "A" et "W" et "page de code" et "Unicode".

«OEM» est la page de codes associée à un autre paramètre régional: l'API d'E / S de la console. C'est par processus (c'est une locale de thread), il peut être changé dynamiquement (en utilisant la commande CHCP) et sa valeur par défaut est définie à l'installation: il n'y a pas d'interface graphique fournie pour changer la valeur stockée dans le registre. La plupart des programmes de console n'utilisent pas l'API d'E / S de la console et, tel qu'il est écrit, utilisent soit les paramètres régionaux du système, soit les paramètres régionaux de l'utilisateur, ou, (parfois par inadvertance), un mélange des deux.

Les paramètres régionaux du système peuvent être usurpés à l'aide d'un manifeste et il y avait un utilitaire WinXP appelé «AppLocale» qui faisait la même chose.