In che modo vbscript filesystemobject codifica i caratteri?

Oct 24 2020

Ho questo codice 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

Sul mio sistema, ogni numero intero viene convertito in un carattere a doppio byte: non ci sono numeri in quell'intervallo che non possono essere rappresentati da un carattere e nessun numero richiede più di 2 byte. Ma quando guardo il file, trovo solo 127 byte.

Questa risposta: https://stackoverflow.com/a/31436726/1335492suggerisce che l'UST crei file UTF e inserisca una BOM. Ma il file contiene solo 127 byte e nessun Byte Order Mark.

In che modo UST decide come codificare il testo? Quale codifica consente caratteri a byte singolo a 8 bit? Quali codifiche non includono 255 caratteri a byte singolo a 8 bit?

(Anche le risposte su come FSO legge i caratteri possono essere interessanti, ma non è quello che sto chiedendo specificamente qui)

Modifica: ho limitato la mia domanda ai caratteri high-bit, per chiarire qual è la domanda. (Anche le risposte sui caratteri low-bit possono essere interessanti, ma non è quello che sto chiedendo specificamente qui)

Risposte

3 JosefZ Oct 24 2020 at 17:32

L'UST decide come codificare il testo durante l'apertura del file. Usa l' formatargomento come segue:

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

Risorsa: metodo OpenTextFile

Sintassi


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

argomenti

object- Necessario. Object è sempre il nome di un file FileSystemObject.

filename- Necessario. Espressione stringa che identifica il file da aprire.

iomode- Facoltativo. Può essere uno dei tre costanti: ForReading, ForWritingo ForAppending.

create- Facoltativo. Valore booleano che indica se è possibile creare un nuovo file se il nome file specificato non esiste. Il valore è Truese viene creato un nuovo file, Falsese non viene creato. Se omesso, non viene creato un nuovo file.

format- Facoltativo. Uno dei tre valori Tristate utilizzati per indicare il formato del file aperto.

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.

Se omesso, il file viene aperto come ASCII .

3 david Nov 08 2020 at 16:43

Risposta breve:

L'oggetto del file system associa "Unicode" a "ASCII" utilizzando la tabella codici associata alle impostazioni internazionali del sistema. (Chr e ChrW utilizzano le impostazioni internazionali dell'utente.)

Applicazione:

Potrebbero esserci errori di trasposizione invisibile tra la tabella codici di sistema e la tabella codici Thread (utente). Potrebbero inoltre verificarsi errori di codifica e decodifica se mancano punti di codice da una tabella codici o, come con il giapponese e UTF-8, le pagine codici contengono caratteri multibyte.

VBscript non fornisce alcun metodo nativo per rilevare la tabella codici utente, thread o sistema. La codepage del thread (utente) può essere dedotta dalle impostazioni locali impostate da SetLocale o restituita da GetLocale (c'è un elenco qui:https://www.science.co.il/language/Locale-codes.php), ma non sembra esserci alcuna documentazione MS. Su Win2K +, WMI può essere utilizzato per interrogare la tabella codici di sistema. Il comando CHCP interroga e modifica la tabella codici OEM, che non è né l'utente né la tabella codici di sistema.

La tabella codici di sistema potrebbe essere falsificata da un manifesto dell'applicazione. Non c'è modo per un'applicazione (come cscript o wscript) o uno script (come VBScript o JScript) per cambiare il proprio sistema genitore se non creando un nuovo processo con un nuovo manifest. o riavviare il sistema dopo aver apportato una modifica al registro.

In dettaglio:

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

I punti di codice che non esistono poiché i caratteri vengono mappati come caratteri di controllo: 127 diventa U + 00FF (che è un carattere di controllo Unicode standard) e 128 diventa U + 20AC (il simbolo dell'Euro) e 129 diventa 0081 (che è un punto di codice in un'area di caratteri di controllo Unicode). In VBScript, Thread Locale può essere impostato e letto da SetLocale e GetLocale

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

Esistono due modi in cui Windows può gestire i valori Unicode che non può mappare: può eseguire il mapping a un carattere predefinito o restituire un errore. "Scripting.FileSystemObject" utilizza l'impostazione di errore e genera un'eccezione.

Più in dettaglio:

Le impostazioni internazionali del thread sono, per impostazione predefinita, le impostazioni locali dell'utente, ovvero l'impostazione del formato di data e ora nell'applet del pannello di controllo "Regione e lingua" (denominate cose diverse nelle diverse versioni di Windows). Ha una tabella codici associata. Secondo Michka (Michael Kaplan, RIP), esperta di internazionalizzazione di MS, il motivo per cui ha una code page è che i mesi e i giorni della settimana possono essere scritti con caratteri appropriati e non devono essere usati per altri scopi.

Le persone ASP-classic avevano chiaramente altre idee, poiché Response.CodePage è thread-locale e può essere controllato da vbscript GetLocale e SetLocale tra gli altri metodi. Se le impostazioni internazionali dell'utente vengono modificate, tutti i processi vengono informati e qualsiasi thread che utilizza il valore predefinito viene aggiornato. (Non ho testato cosa succede a un thread che attualmente utilizza un valore non predefinito).

Le impostazioni internazionali di sistema sono anche chiamate "Lingua per programmi non Unicode" e si trovano anche nell'applet "Regione e lingua", ma richiedono un riavvio per essere modificate. Questo è il valore utilizzato internamente da Windows ("The System") per eseguire il mapping tra l'API "A" e l'API "W". La modifica di questo non ha alcun effetto sulla lingua della GUI di Windows (che non è un "programma non Unicode")

Supponendo che l'impostazione "Ora e data" corrisponda alla "Lingua per programmi non Unicode" , qualsiasi Chr (i) che può creare un punto di codice Unicode valido (vedi "errori di mappatura" di seguito), verrà mappato esattamente da Unicode a " code page ". Si noti che questo funziona per i punti di codice che sono "caratteri di controllo": si noti inoltre che non funziona nell'altro modo: UTF-CodePage-UTF non sempre di andata e ritorno esattamente. Notoriamente (Character, Modifer) -CodePage- (Complex Character) non esegue correttamente il round trip, dove Unicode definisce più di un modo di costruire una rappresentazione dei caratteri del linguaggio.

Se "Ora e data" non corrispondono alla "Lingua per programmi non Unicode" , potrebbe essere eseguita qualsiasi traduzione, ad esempio U + 0101 è 0xE0 su cp28594 e 0xE2 su cp28603: Chr (224) passerebbe attraverso U + 0101 da scrivere come 226.

Anche se non ci sono errori di trasposizione , se "Ora e data" non corrispondono alla "Lingua per programmi non Unicode" il programma potrebbe non riuscire durante la traduzione nelle impostazioni internazionali di sistema: se il punto di codice Unicode non ha una tabella codici corrispondente punto di codice, ci sarà un'eccezione da FileSystemObject.

Potrebbero anche esserci errori di mappatura in Chr (i), passando dalla Code page a Unicode. La tabella codici 1041 (giapponese) è una tabella codici a doppio byte (probabilmente Shift JIS). 0x81 è (solo) il primo byte di una coppia a doppio byte. Per essere coerente con altre tabelle codici, 0x81 dovrebbe mappare al carattere di controllo 0081, ma quando viene fornito 81 e la tabella codici 1041, Windows presume che il byte successivo nel buffer, o nel BSTR, sia il secondo byte del doppio byte coppia (non ho determinato se l'errore è stato commesso prima o dopo la conversione). Chr (& H81) è mappato su U + xx81 (81, xx). Quando l'ho fatto, ho ottenuto U + 4581, che è un ideogramma unificato CJK (Brasenia purpurca): non è mappato dalla tabella codici 1041.

Gli errori di mappatura in Chr (1) non causano eccezioni VBScript al momento della creazione. Se il punto di codice UTF-16 creato non è valido o non si trova nella codepage delle impostazioni locali del sistema, ci sarà un'eccezione FileSystemObject in .write. Questo particolare problema può essere evitato utilizzando ChrW (i) invece di Chr (i). Nella tabella codici 1041, ChrW (129) diventa il carattere di controllo Unicode 0081 invece che xx81.

Sfondo:

Un programma può mappare tra Unicode e "codepage" utilizzando qualsiasi tabella codici installata: le funzioni di Windows MultiByteToWideChar e WideCharToMultiByte accettano [UINT CodePage] come primo parametro. Questo meccanismo viene utilizzato internamente in Windows per mappare l'API "A" all'API "W", ad esempio GetAddressByNameA e GetAddressByNameW. Windows è "W", (wide, 16 bit) internamente e le stringhe "A" vengono mappate alle stringhe "W" su chiamata e indietro da "W" a "A" al ritorno. Quando Windows esegue la mappatura, utilizza la tabella codici associata alle "Impostazioni internazionali di sistema", chiamata anche "Lingua per programmi non Unicode".

La funzione API di Windows WriteFile scrive byte, non caratteri, quindi non è una funzione "A" o "W". Qualsiasi programma che lo utilizza deve gestire la conversione tra stringhe e byte. La funzione c fwrite scrive caratteri, quindi può gestire caratteri a 16 bit, ma non ha modo di gestire punti di codice a lunghezza variabile come UTF-8 o UTF-16: di nuovo, qualsiasi programma che usa "fwrite" deve gestire la conversione tra stringhe e parole.

La funzione C ++ fwrite può gestire UTF e la funzione del compilatore _fwrite fa magie che dipendono dal compilatore. Presumibilmente, su Windows, se è richiesta la traduzione della code page, vengono utilizzate le API MultiByteToWideChar e WideCharToMultiByte.

Le code page "A" e l'API "A" erano chiamate "ANSI" o "ASCII" o "OEM" e inizialmente erano caratteri a 8 bit, poi sono cresciute fino a caratteri a doppio byte e ora sono diventate UTF-8 (1..3 byte). L'API "W" è iniziata con caratteri a 16 bit, poi è cresciuta fino a UTF-16 (1..6 byte). Entrambe sono codifiche di caratteri multi-word: la distinzione è che per l'API "A" e le code page, la lunghezza della parola è di 8 bit: per l'API "W" e UTF-16, la lunghezza della parola è di 16 bit. Perché sono entrambe mappature multibyte, e poiché "byte" e "parola" e "carattere" e "carattere" significano cose diverse in contesti diversi, e perché "W" e in particolare "A" significano cose diverse rispetto agli anni fa, ho usato solo "A" e "W", "code page" e "Unicode".

"OEM" è la tabella codici associata a un'altra lingua: l'API I / O della console. È per processo (è una lingua del thread), può essere modificata dinamicamente (utilizzando il comando CHCP) e il suo valore predefinito è impostato al momento dell'installazione: non è fornita alcuna GUI per modificare il valore memorizzato nel registro. La maggior parte dei programmi di console non utilizza l'API I / O della console e, come scritto, utilizza la locale del sistema o la locale dell'utente o, a volte inavvertitamente, una combinazione di entrambe.

Le impostazioni internazionali del sistema possono essere contraffatte utilizzando un manifesto e c'era un'utilità WinXP chiamata "AppLocale" che faceva la stessa cosa.