vbscript文件系统对象如何编码字符?

问题描述

我有这个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

在我的系统上,每个整数都转换为双字节字符:在该范围内,没有数字不能用字符表示,没有数字需要超过2个字节。 但是当我查看该文件时,发现只有127个字节。

此答案:https://stackoverflow.com/a/31436726/1335492建议FSO创建UTF文件并插入BOM。但是该文件仅包含127个字节,没有字节顺序标记

FSO如何决定如何编码文本?什么编码允许8位单字节字符? 不包含255个8位单字节字符?

(有关FSO 读取字符的方式也可能很有趣,但这不是我在这里特别要问的内容

编辑:我将我的问题仅限于高位字符,以使问题清晰可见。 (有关低位字符的答案也可能很有趣,但这不是我在这里特别要问的)

解决方法

FSO决定文件打开过程中的文本编码方式。使用format参数,如下所示:

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

资源:OpenTextFile Method

语法


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

参数

object-必需。对象始终是FileSystemObject的名称。

filename-必需。字符串表达式,用于标识文件 打开。

iomode-可选。可以是三个常数之一:ForReadingForWritingForAppending

create-可选。布尔值,指示是否有新文件 如果指定的文件名不存在,则可以创建。值是 True(如果已创建新文件),False(未创建)。如果 省略,则不会创建新文件。

format-可选。三个Tristate值之一,用于指示 打开文件的格式。

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.

如果省略,文件将以ASCII码打开

,

简短答案:

文件系统对象使用与系统区域设置关联的代码页将“ Unicode”映射为“ ASCII”。 (Chr和ChrW使用用户区域设置。)

应用程序:

系统代码页和线程(用户)代码页之间可能存在静默转换错误。如果代码页中缺少代码点,或者与日语和UTF-8一样,代码页中包含多字节字符,则可能还会出现编码和解码错误。

VBscript没有提供检测用户,线程或系统代码页的本机方法。线程(用户)代码页可以从SetLocale设置的Locale推断出,也可以由GetLocale返回(这里有一个列表:https://www.science.co.il/language/Locale-codes.php),但是似乎没有任何MS文档。在Win2K +上,WMI可用于查询系统代码页。 CHCP命令查询并更改OEM代码页,它既不是用户代码也不是系统代码页。

系统代码页可能被应用程序清单欺骗。应用程序(例如cscript或wscript)或脚本(例如VBScript或JScript)无法更改其父系统,除非通过使用新清单创建新进程。或在更改注册表后重新启动系统。

详细信息:

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

不作为字符存在的代码点被映射为控制字符:127变为U + 00FF(这是标准的Unicode控制字符),128变为U + 20AC(欧元符号),而129变为0081(即Unicode控制字符区域中的代码点)。在VBScript中,可以通过SetLocale和GetLocale设置和读取线程语言环境

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

Windows可以处理两种无法映射的Unicode值:它可以映射到默认字符或返回错误。 “ Scripting.FileSystemObject”使用错误设置,并引发异常。

更详细:

默认情况下,线程语言环境是用户语言环境,它是“区域和语言”控制面板小程序中的日期和时间格式设置(在不同版本的Windows中称为不同的事物) 。它具有关联的代码页。根据MS国际化专家Michka(Michael Kaplan,RIP)的说法,它具有代码页的原因是可以用适当的字符来写星期和星期几,并且不得将其用于任何其他目的。

由于Response.CodePage是线程语言环境,并且可以由vbscript GetLocale和SetLocale等方法控制,因此,ASP古典人显然还有其他想法。如果更改了用户区域设置,则将通知所有进程,并且所有使用默认值的线程都会更新。 (我尚未测试当前使用非默认值的线程会发生什么情况。)

系统区域设置也称为“非Unicode程序的语言”,也可以在“区域和语言”小程序中找到,但需要重新启动才能进行更改。这是Windows(“系统”)内部在“ A” API和“ W” API之间映射的值。更改此设置不会影响Windows GUI的语言(即不是一个“非Unicode程序”)

假定“时间和日期”设置与“非Unicode程序的语言”相匹配,则可以创建有效Unicode代码点的所有Chr(i)(请参见下面的“映射错误”) ),将其从Unicode精确映射回“代码页”。请注意,这确实适用于“控制字符”的代码点:还请注意,它不能以其他方式起作用:UTF-CodePage-UTF并不总是精确地往返。著名的(Character,Modifer)-CodePage-(复杂字符)无法正确往返,其中Unicode定义了多种构造语言字符表示的方法。

如果“时间和日期”与“非Unicode程序的语言”不匹配,则可以进行任何翻译,例如cp28594上的U + 0101为0xE0,而cp28603上的U + 0101为0xE2: Chr(224)将通过U + 0101写入为226。

即使没有转换错误,如果“时间和日期”与“非Unicode程序的语言”不匹配,则该程序在转换为系统区域设置时也可能会失败: Unicode代码点没有匹配的代码页代码点,FileSystemObject会出现异常。

在Chr(i)处可能还会出现映射错误,从“代码”页面到Unicode。代码页1041(日语)是双字节代码页(可能是Shift JIS)。 (仅)0x81是双字节对的第一个字节。为了与其他代码页一致,0x81应该映射到控制字符0081,但是当给定81和代码页1041时,Windows假定缓冲区或BSTR中的下一个字节是双字节的第二个字节。配对(我尚未确定转换之前还是之后出错)。 Chr(&H81)映射到U + xx81(81,xx)。完成此操作后,我得到了U + 4581,它是CJK统一表意文字(Brasenia purpurca):代码页1041并未映射它。

Chr(1)处的映射错误在创建时不会导致VBScript异常。如果在系统区域设置代码页上创建的UTF-16代码点无效或无效,则.write处将出现FileSystemObject异常。通过使用ChrW(i)代替Chr(i)可以避免此特定问题。在代码页1041上,ChrW(129)成为Unicode控制字符0081,而不是xx81。

背景:

程序可以使用任何已安装的代码页在Unicode和“代码页”之间进行映射:Windows函数 MultiByteToWideChar WideCharToMultiByte 以[UINT CodePage]作为第一个参数。 Windows内部使用该机制将“ A” API映射到“ W” API,例如GetAddressByNameA和GetAddressByNameW。 Windows内部是“ W”(宽16位),并且在调用时将“ A”字符串映射到“ W”字符串,并在返回时从“ W”返回到“ A”。 Windows进行映射时,它将使用与“系统区域设置”(也称为“非Unicode程序的语言”)关联的代码页。

Windows API函数 WriteFile 写入字节而不是字符,因此它不是“ A”或“ W”函数。任何使用它的程序都必须处理字符串和字节之间的转换。 c函数 fwrite 可以写入字符,因此它可以处理16位字符,但是无法处理像UTF-8或UTF-16这样的可变长度代码点:同样,任何使用“ fwrite “必须处理字符串和单词之间的转换。

C ++函数 fwrite 可以处理UTF,而编译器函数 _fwrite 可以执行依赖于编译器的魔术。大概在Windows上,如果需要代码页转换,则使用MultiByteToWideChar和WideCharToMultiByte API。

“ A”代码页和“ A” API被称为“ ANSI”或“ ASCII”或“ OEM”,最初以8位字符开头,然后增长为双字节字符,现在已经发展为UTF-8(1..3字节)。 “ W” API最初是16位字符,然后增长到UTF-16(1..6字节)。两者都是多字字符编码:区别在于,对于“ A” API和代码页,字长为8位:对于“ W” API和UTF-16,字长为16位。因为它们都是多字节映射,并且因为“ byte”和“ word”以及“ char”和“ character”在不同上下文中的含义不同,并且因为“ W”尤其是“ A”的含义与年份不同以前,我只使用“ A”和“ W”以及“代码页”和“ Unicode”。

“ OEM”是与另一个语言环境关联的代码页:控制台I / O API。它是每个进程的(它是线程语言环境),可以动态更改(使用CHCP命令),并且其默认值是在安装时设置的:没有提供GUI来更改存储在注册表中的值。大多数控制台程序不使用控制台I / O API,而是按照书面说明,使用系统区域设置或用户区域设置,或者(有时不经意间)使用两者的混合。

可以使用清单来欺骗系统区域设置,并且有一个名为“ AppLocale”的WinXP实用程序也可以执行相同的操作。