1

Тема: WSH: Определение кодировки файла

Доброго дня.

В папке лежат 2 файла, которые нужно прочесть.

Один файл у ANSII другой в UTF-16

Как узнать что применять при открытии

READ = fso.OpenTextFile(file, 1, false, -1);
или
READ = fso.OpenTextFile(file, 1, false, 0);

Ведь если открыть неправильно получаются кракозябры.

PS Как правильно писать - "Кракозябры", не знаю. )

Нас невозможно сбить с пути, нам пофигу куда идти.

2

Re: WSH: Определение кодировки файла

Если размер файла нечётный, то точно не UTF-16, т.к. у него ведь каждый символ 2 байта. С другой стороны, у последнего в начале бывает BOM: байты FF и FE, что в русской кодировке видно как "яю".

3

Re: WSH: Определение кодировки файла

Ну, да. Если есть BOM - то по нему, если нет - проверять наличие байтов 00 или 04 через один (при открытии как ANSI)

4

Re: WSH: Определение кодировки файла

DnsIs, основных вопросов два: наличествует ли в Вашем Unicode-файле BOM, и планируется ли последующая запись в эти файлы?

5

Re: WSH: Определение кодировки файла

Никогда ранее не смотрел внутренности Unicode-файлов, даже не представлял, что такое BOM.
А ведь он есть в моих файлах, причем все Unicode-файлы созданы одной программой, поэтому все будет просто.
А писать в Unicode не буду.

Лежит пара сотен файлов (ANSII, UTF-16 LE BOM), надо чуток отсортировать вытащить куски.

Спасибо всем.

Нас невозможно сбить с пути, нам пофигу куда идти.

6

Re: WSH: Определение кодировки файла

Ну и как быть?

FILE_TO_READ = 'test.txt';
BYTES_FOR_READ = 1;

fso = new ActiveXObject("Scripting.FileSystemObject");
stream = new ActiveXObject("ADODB.stream");

stream.Type = 1;
stream.Open();
stream.LoadFromFile(FILE_TO_READ);
DATA = stream.Read(BYTES_FOR_READ);
stream.Close();

if (DATA == '0xFF' ) {
    WScript.Echo("OK")
}
Нас невозможно сбить с пути, нам пофигу куда идти.

7 (изменено: Rumata, 2013-07-20 08:07:41)

Re: WSH: Определение кодировки файла

Два. Два байта. Не менее. А лучше сразу 4.
BOM

( 2 * b ) || ! ( 2 * b )

8

Re: WSH: Определение кодировки файла

Да не, суть не в количестве байт.

typeOf(DATA) = unknown

Не могу понять как обработать бинарные данные.
(DATA == '0xFF') не пашет.

Нас невозможно сбить с пути, нам пофигу куда идти.

9

Re: WSH: Определение кодировки файла

А-а-а. Не обратил внимание - Ваш пример на JScript, а он коряво работает с файлами. Лучше прочитать на VBScript.

( 2 * b ) || ! ( 2 * b )

10

Re: WSH: Определение кодировки файла

А зачем бинарные данные? FF — это буква "я". FE — буква "ю". Если читать файл как ANSI.

11

Re: WSH: Определение кодировки файла

DnsIs, превосходно! Если Вам не будет потребно в них ничего писать — просто используйте в методе открытия файла последним параметром «TristateUseDefault», кодировка будет распознана автоматически:

Option Explicit

' IOMode Enum
Const ForReading   = 1
Const ForWriting   = 2
Const ForAppending = 8

' Format Enum
Const TristateUseDefault = -2
Const TristateTrue       = -1
Const TristateFalse      =  0


With WScript.CreateObject("Scripting.FileSystemObject")
    With .OpenTextFile("E:\Песочница\0299\ANSI.txt", ForReading, False, TristateUseDefault)
        WScript.Echo .ReadAll()
        .Close
    End With
    
    With .OpenTextFile("E:\Песочница\0299\UTF-16 with BOM.txt", ForReading, False, TristateUseDefault)
        WScript.Echo .ReadAll()
        .Close
    End With
End With

WScript.Quit 0
Мама мыла раму.
Мама мыла раму.

12

Re: WSH: Определение кодировки файла

YMP пишет:

А зачем бинарные данные? FF — это буква "я". FE — буква "ю". Если читать файл как ANSI.

Потому что

stream.Type = 2;
stream.Charset = 'ISO-8859-1';
stream.Open();
stream.LoadFromFile(FILE_TO_READ);
DATA = stream.ReadText(1);
stream.Close();
if (DATA == "я" ) {
    WScript.Echo(DATA)
}

тоже не пашет.

Нас невозможно сбить с пути, нам пофигу куда идти.

13

Re: WSH: Определение кодировки файла

А что, в кодировке ISO-8859-1 есть буква "я"? Она из windows-1251.

14 (изменено: DnsIs, 2013-07-20 10:51:16)

Re: WSH: Определение кодировки файла

Но ведь код выше, строкой WScript.Echo(DATA) то как раз выводит "я".

Нас невозможно сбить с пути, нам пофигу куда идти.

15

Re: WSH: Определение кодировки файла

2alexii, круто, то что надо без всяких велосипедов.

Нас невозможно сбить с пути, нам пофигу куда идти.

16

Re: WSH: Определение кодировки файла

Ничего он у меня не выводит, пока не заменю на windows-1251.

17

Re: WSH: Определение кодировки файла

И все таки, если читать бинарные данные скриптом, то как получить шестнадцатеричный код символа?

Нас невозможно сбить с пути, нам пофигу куда идти.

18

Re: WSH: Определение кодировки файла

Rumata пишет:

Лучше прочитать на VBScript.

19

Re: WSH: Определение кодировки файла

FILE_TO_READ = "test.txt"
BYTES_FOR_READ = 1

Set fso = CreateObject("Scripting.FileSystemObject")
Set stream = CreateObject("ADODB.stream")

stream.Type = 1
stream.Open()
stream.LoadFromFile(FILE_TO_READ)
DATA = stream.Read(BYTES_FOR_READ)
stream.Close

if DATA = &Hff then
  MsgBox(DATA)
end if

Несоответствие типов. (

Нас невозможно сбить с пути, нам пофигу куда идти.

20

Re: WSH: Определение кодировки файла

DATA — байтовый массив. Видать, такой тип VBS не поддерживает. Проще FSO использовать для чтения файла.

21

Re: WSH: Определение кодировки файла

Естественно. Вы пытаетесь сравнить массив и скаляр.

Option Explicit

Const ForReading = 1
Const TristateFalse = 0


With WScript.CreateObject("Scripting.FileSystemObject")
    With .OpenTextFile("C:\Песочница\022\UTF-16LE with BOM.txt", ForReading, False, TristateFalse)
        If .Read(2) = Chr(&HFF) & Chr(&HFE) Then
            WScript.Echo "Is a UTF-16LE with BOM"
        Else
            WScript.Echo "Is not a UTF-16LE with BOM"
        End If
        
        .Close
    End With
End With

WScript.Quit 0

22

Re: WSH: Определение кодировки файла

Кстати, оцените код определения кодировки файла :-).


var GlobalObject = this;
var FSO = fso = new ActiveXObject("Scripting.FileSystemObject");
var WshShell = new ActiveXObject("WScript.Shell");

Function.prototype.GetResource = function(ResourceName) {
    if (!this.Resources) {
        var UnNamedResourceIndex = 0,
            _this = this;
        this.Resources = {};

        function f(match, resType, Content) {
            _this.Resources[(resType == "[[") ? UnNamedResourceIndex++:resType.slice(1, -1)] = Content;
        }
        this.toString().replace(/\/\*(\[(?:[^\[]+)?\[)((?:[\r\n]|.)*?)\]\]\*\//gi, f);
    }
    return this.Resources[ResourceName];
}

function Resourses() {
    /*[vbs[
Function [_MidB](v, s, l):[_MidB]=MidB(v, s+1, l):End Function
Function [_Asc](v):[_Asc]=Asc(v):End Function
Function [_Chr](v):[_Chr]=Chr(v):End Function
Function [_VarType](v):[_VarType]=VarType(v):End Function
Function [_TypeName](v):[_TypeName]=TypeName(v):End Function
Function [_ChrB](v):[_ChrB]=ChrB(v):End Function
Function [_AscB](v):[_AscB]=AscB(v):End Function
Function [_LeftB](v, l):[_LeftB]=LeftB(v, l):End Function
Function [_RightB](v, l):[_RightB]=RightB(v, l):End Function
Function [_InstrB](start, stringtosearch, stringtofind, comparemode):[_InstrB]=InstrB(start, stringtosearch, stringtofind, comparemode):End Function

Sub ConvertBinaryToJSArray(arrByteArray, js_arr)
Dim i
For i = 1 To LenB(arrByteArray)
    js_arr.push(AscB(MidB(arrByteArray, i, 1)))
Next
End Sub
]]*/
}

var VBSEngine = new ActiveXObject('ScriptControl');
VBSEngine.Language = 'VBScript';
VBSEngine.AddCode(Resourses.GetResource("vbs"));

WshShell.CurrentDirectory = GetScriptDir();

Array.prototype.map = function(mapper, that) {
    var other = new Array(this.length);
    for (var i = 0, n = this.length; i < n; i++)
    other[i] = mapper.call(that, this[i], i, this);
    return other;
}

function BinaryToArray(binary) {
    var ar = [];
    VBSEngine.CodeObject.ConvertBinaryToJSArray(binary, ar);
    return ar;
}

function TypeOf(o) {
    var type = typeof o;
    if (type != "object" && type != "unknown") return type;
    if (o === null || o === undefined) return "empty";
    if (Object.prototype.toString.call(o) === '[object Array]') return "array";
    if (Object.prototype.toString.call(o) === '[object Number]') return "number";
    if (Object.prototype.toString.call(o) === '[object String]') return "string";
    if (VBSEngine.CodeObject._VarType(o) == (8192 + 17)) return "bytearray";
    return type;
}

function Exit() {
    if (this.WScript) WScript.Quit();
    if (this.window) window.close();
}

function MsgBox(strText, strTitle, nType) {
    return WshShell.Popup(strText, 0, strTitle, nType);
}

function GetScriptDir() {
    return GetScriptFullPath().replace(/\\+[^\\]+$/g, "");
}

function GetScriptFullPath() {
    if (this.location && this.location.href) return location.href.replace(/^.+\/\/\//, "").replace(/\//g, "\\");
    if (this.WScript) return WScript.ScriptFullName;
    return "";
}

function StringToASCIIArray(string, start, end) {
    start = start || 0;
    end = end || string.length;
    string = string.slice(start, end);
    return string.split("").map(function a(s) {
        return Asc(s);
    })
}

function Asc(str) {
    return VBSEngine.CodeObject._Asc(str);
}

function Hex(expression, length) {
    var res = expression.toString(16);
    length = Math.max(length || 0, res.length);
    return new Array(length + 1).join("0").replace(new RegExp(".{" + res.length + "}$"), res).toUpperCase();
}

function Bin(expression) {
    var res = expression.toString(2);
    var length = Math.floor(res.length / 8) + ((res.length % 8 > 0) ? 1 : 0);
    length *= 8;
    return new Array(length + 1).join("0").replace(new RegExp(".{" + res.length + "}$"), res).toUpperCase();
}

function FileGetSize(path) {
    try {
        return FSO.GetFile(path).Size;
    } catch(e) {
        return 0;
    }
}

function FileGetEncoding(path) {
    var f = FSO.OpenTextFile(WshShell.ExpandEnvironmentStrings(path), 1, 0, 0);
    var n = FSO.GetFile(path).Size;
    n = n <= 60 ? n : 60;
    var val = StringToASCIIArray(f.Read(n));
    f.Close();
    return EncodingDetect(val);
}

function EncodingDetect(str) {
    var ar, type = TypeOf(str);
    if (type == "string") ar = StringToASCIIArray(str);
    if (type == "bytearray") ar = BinaryToArray(str);
    if (type == "array") ar = (str);
    if (!ar) return 0;
    var head = ar.map(function(x) {
        return Hex(x, 2)
    }).join("");
    if (/^EFBBBF/.test(head)) return "UTF-8";
    if (/^0000FEFF/.test(head)) return "UTF-32BE";
    if (/^FFFE0000/.test(head)) return "UTF-32LE";
    if (/^2B2F76(38|39|2B|2F)/.test(head)) return "UTF-7";
    if (/^F7644C/.test(head)) return "ISO-10646-UTF-1";
    if (/^DD736673/.test(head)) return "UTF-EBCDIC";
    if (/^0EFEFF/.test(head)) return "SCSU";
    if (/^FBEE28/.test(head)) return "BOCU-1";
    if (/^84319533/.test(head)) return "GB-18030";
    if (/^FEFF0000/.test(head)) return "ISO-10646-UCS-4";
    if (/^0000FFFE/.test(head)) return "ISO-10646-UCS-4";
    if (/^FEFF/.test(head)) return "UTF-16BE";
    if (/^FFFE/.test(head)) return "UTF-16LE";
    var head2 = ar.map(function(x) {
        return Bin(x)
    }).join("");
    
    if (/^(0.{7})+$/.test(head2)) return "ASCII"; 
    
    if (/^(0.{7}|110.{5}10.{6}|1110.{4}10.{6}10.{6}|11110.{3}10.{6}10.{6}10.{6}|111110.{2}10.{6}10.{6}10.{6}10.{6}|1111110.110.10.{6}10.{6}10.{6}10.{6}10.{6})+$/.test(head2)) return "UTF-8 without BOM";
    return "ASCII";
}

MsgBox(FileGetEncoding("123.txt"));
Exit();

23 (изменено: Serge Yolkin, 2014-07-20 14:59:12)

Re: WSH: Определение кодировки файла

На JS читать что попало, может и не так удобно, как на VBS, но существует вполне удобный и технологичный костыль (мне его когда-то Flasher подсказал, с тех пор пользуюсь; хотя, на этом форуме и раньше упоминался).
Создаём Microsoft.XMLDOM и в нём какой нибудь элемент с типом данных bin.hex, грузим в nodeTypedValue данные, прочитанные ADODB.Stream (type=1) и получаем в свойстве text текстовый шестнадцатеричный дамп. А с ним уже всё просто: можно BOM'ы ловить, можно другие сигнатуры (я так исполняемые анализировал).

P.S. Про bin.hex подсказал smaharbA. Sorry.

24

Re: WSH: Определение кодировки файла

Serge Yolkin такой вариант, на мой взгляд, гораздо хуже.

Причины:

1) ADODB.Stream грузит весь файл в память, а значит время срабатывания скрипта напрямую зависит от размера файла. К тому же получается, что скрипт выполняет бесполезные действия, т.к для анализа требуется небольшой "кусок" данных, а не весь массив.

2) Конвертация в bin.hex опять же производится над всем объёмом данных, которые попали в элемент DOM Document-а, а следовательно и на это бесполезно тратятся усилия процессора и памяти.

3) К тому же в результате конвертации выделяется память и под загруженный массив и под результат конвертации в bin.hex. Допустим файл занимает 1 Гб. Результат будет занимать 2 Гб. Итого 3 гига оперативы просто так в помойку. А комп пыхтит. Конечно в реальности наверное будет кэширование на диск, но..... Это ещё больше ухудшит ситуацию.


Так что в этом случае решение JSman-а, на мой взгляд, гораздо предпочтительнее.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

25 (изменено: Serge Yolkin, 2014-07-13 21:20:36)

Re: WSH: Определение кодировки файла

Э-э-э... Я думал... Нет, я был уверен, что 18 строка примера, ссылку на который я привёл, грузт в память не более 64 байт...

Добавлено: я и сейчас в этом уверен... (проверил на файле ~20Gb)

26

Re: WSH: Определение кодировки файла

И я сейчас так же проверил. Использовал следующий скрипт:


var s = new ActiveXObject("ADODB.Stream");
s.type = 1;
WScript.Echo("Before load");
s.open();
s.loadFromFile("D:\\file.wmv");
WScript.Echo("After load");

Файл file.wmv занимает 812 мб.

Размер процесса WScript до загрузки файла и после:

Я не думаю, что у нас по разному работает объект ADODB.Stream. )

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

27

Re: WSH: Определение кодировки файла

Вы правы.
Но показания винды меня несколько озадачили:
В первом случае - i5 >2GHz 8Gb W8.1x64 SSD 256Gb свободно >100 файл ~20Gb (киношка) - память процесса ~800Mb
Дома на нотнике - Celeron 2GHz 1Gb W7SP1x32 HDD 256Gb свободно ~40 на том же файле вообще всё зависло, а на файле 700Mb - память процесса 527Mb
Показывает только физическую?

28

Re: WSH: Определение кодировки файла

JSman, а в функции TypeOf проверки на undefined, Number и String нужны? Вроде, typeof их корректно распознаёт, или есть какие подводные камни?

29

Re: WSH: Определение кодировки файла

Serge Yolkin

Serge Yolkin пишет:

... на том же файле вообще всё зависло, ...

Предполагаю, что не зависло, а гораздо дольше обрабатывалось.

Serge Yolkin пишет:

Но показания винды меня несколько озадачили:

Показатели разные т.к и объём оперативки разный + тип жёсткого разный, следовательно и кэширование на диск происходит в разный момент.

Как мы с Вами понимаем - логично, что если на компе оперативы меньше чем тот объём, который вы хотите загрузить в память ( в Вашем случае файл около 20 Гб ), то на жёстком диске выделяется кэш куда будет сбрасываться "остаток" после того как "сожрётся" вся память. В итоге ADODB.Stream сначала заполняет всю выданную ему оперативку, а потом читает с диска и тут же пишет. В первом случае SSD крайне шустро отрабатывает этот момент + писать на него приходится меньше, т.к 8 гиг уже помогли в этом. А во втором случае 1 гиг. Он заполнился и дальше пошёл тот самый процесс "тяни толкай" - с харда читаем, на хард пишем. Жёсткий диск это делает медленнее чем SSD. Итог - Приходится ждать, когда ADODB.Stream перераспределит копию этих ~ 19 гиг на диске. Собственно тот самый косяк о котором я и говорил. )

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

30 (изменено: Serge Yolkin, 2014-07-14 11:37:24)

Re: WSH: Определение кодировки файла

Xameleon
Озадачило то, что в обоих случаях память процесса меньше размера файла (отсюда и моё заблуждение). А на счёт зависло - минут 10 комп на мои действия не реагировал, только курсор отзывался, но даже диспетчер задач вызвать не смог. Не дождался - ребутнул.

JSman
Дошло. Это, видимо, фреймворк. Потому и код избыточный.

И на счёт длинной регулярки: а если файл с латиницы начинается? Ну, например: "Windows - офигенная система." Независимо от кодировки (ANSI, OEM) получаем UTF-8 without BOM

31

Re: WSH: Определение кодировки файла

Serge Yolkin пишет:

Xameleon
Озадачило то, что в обоих случаях память процесса меньше размера файла (отсюда и моё заблуждение). А на счёт зависло - минут 10 комп на мои действия не реагировал, только курсор отзывался, но даже диспетчер задач вызвать не смог. Не дождался - ребутнул.

1) Могу ошибаться, но кажется где-то читал, что у Windows есть свои ограничения на выделение памяти под процесс и определение порога с которого начинается кэширование. Поэтому результат не совсем понятный. Думаю спецы в этой области, к примеру YMP, меня поправит, если ошибаюсь и прольёт свет на реальность бытия. )

2) Логично. Комп почти все ресурсы бросил на выполнение задачи и попросил его не отвлекать всякими ненужными вызовами диспетчера задач.  )

Serge Yolkin пишет:

Дошло. Это, видимо, фреймворк. Потому и код избыточный.

Он самый. JSman продвигает своё детище в массы. ) Крайне интересный проект. Я даже слегка успел поучаствовать в его развитии, а заодно и для себя позаимствовал часть кода.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

32

Re: WSH: Определение кодировки файла

Да Бог с ними, с процессами: свою ощибку я понял, а всё остальное представляет чисто академический интерес. А фреймворки - штука хорошая, но как же тяжело такой код читать. Пока разбирался с примером - получил файлик ~700байт, правда, без самого детектора - той кучи ифов, зато работает и понятно...

33 (изменено: Serge Yolkin, 2014-07-14 13:19:08)

Re: WSH: Определение кодировки файла

Если ограничиться только латиницей и кириллицей, то можно так:

var
 b={
   Asc:'Function VB_ASC(E):VB_ASC=Asc(E):End Function'
 },
 c=new ActiveXObject('ScriptControl'),
 f=new ActiveXObject('Scripting.FileSystemObject');

c.language='VBScript';
c.addCode(b.Asc);

function FileGetEncoding(e) {
  if(!f.getFile(e).size)return('empty');
  var
   u=f.OpenTextFile(e),
   v=u.Read(2048).split(''),
   w=[];
  for(var i=0;i<v.length;i++)w.push(c.run('VB_ASC',v[i]));
  u.Close();
  return EncodingDetect(w);
}

function EncodingDetect(e){
  var u='';
  for(var i=0;i<e.length;i++)u+=('0'+e[i].toString(16)).slice(-2);

  if(/^efbbbf/.test(u))return('UTF-8 with BOM');
  else if(/^(..)*(d[01][89ab].){3}/.test(u))return('UTF-8 without BOM');
  else if(/^fffe/.test(u))return('UTF-16 LE with BOM');
  else if(/^(....)*((?!00)..0[0-4]){3}/.test(u))return('UTF-16 LE without BOM');
  else if(/^feff/.test(u))return('UTF-16 BE with BOM');
//  else if(/^(....)*(0[0-4](?!00)..){3}/.test(u))return('UTF-16 BE without BOM');
  else if(/^(..)*(?=([ae].){3})(e.)*(a.)+(e.)*(a.)?/.test(u))return('ASCII');
  else if(/^(..)*(?=([ef].){3})(e.)*(f.)+(e.)*(f.)?/.test(u))return('ANSI');
  else return('LATIN');  // ANSI, ASCII, UTF-8 without BOM — без кириллицы
}

WScript.echo(FileGetEncoding(WScript.ScriptFullName));

Мой только детектор, остальное - слегка укороченный код примера JSman. Не строго, на допущениях, но анализируются 2K символов (писалось для html/hta, где в начале может не быть русских букв довольно долго).

Кстати, у JSman в head массив обрезается до 4 элементов, а анализируется больше - многие сигнатуры никак в 4 не укладываюся...

34

Re: WSH: Определение кодировки файла

Serge Yolkin значит есть повод для "допиливания". ) Его (JSman-а) я проинформировал. Буду ждать комментариев.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

35

Re: WSH: Определение кодировки файла

Правильно ли я понял, что в текстовом потоке нельзя вернуться выше? Вперёд - пожалуйста: skip(100500), а назад - только переоткрывать...

Имею в виду аналог свойства position из ADODB.Stream

36

Re: WSH: Определение кодировки файла

+ открыть спойлер

Желающие автоопределения идут на farmanager - Far Manager - Google Project Hosting, смотрят там его код автоопределения кодовой страницы и пробуют перевести под WSH.

Правильно ли я понял, что в текстовом потоке нельзя вернуться выше? Вперёд - пожалуйста: skip(100500), а назад - только переоткрывать...

Вроде как.

37

Re: WSH: Определение кодировки файла

Serge Yolkin пишет:

JSman, а в функции TypeOf проверки на undefined, Number и String нужны? Вроде, typeof их корректно распознаёт, или есть какие подводные камни?

Скажу честно, что над этой функцией еще надо думать. Она явно не доработана. Главное ее научить понимать типы данных в js и vbs.

Нужна ли проверка на String и Number? Вот ответ:


WScript.Echo(typeof new String("123"));
Serge Yolkin пишет:

Кстати, у JSman в head массив обрезается до 4 элементов, а анализируется больше - многие сигнатуры никак в 4 не укладываюся...

А, точно. Спасибо. Исправил в посте с кодом.

Serge Yolkin пишет:

Дошло. Это, видимо, фреймворк. Потому и код избыточный.

Да, но на написание примера потратил минуту . Неплохо же? С избыточностью пытаюсь бороться. Задача не из легких. Думаю, скоро продемонстирую решение.

И на счёт длинной регулярки: а если файл с латиницы начинается? Ну, например: "Windows - офигенная система." Независимо от кодировки (ANSI, OEM) получаем UTF-8 without BOM

Вообще однобайтовая UTF-8 фактически является ANSI. В исправленном коде я учел выявленные недостатки. Попробуйте протестировать. Вроде бы теперь все корректно.

38

Re: WSH: Определение кодировки файла

На счёт new String, new Number - забавно, не знал.

Работает исправленный код гораздо точнее, а про UTF без BOM - в курсе, но я про то, что проверять надо не только начало - мало ли, что там в начале... Сколько символов читать для проверки - вопрос спорный, я остановился на 2k.

39 (изменено: Serge Yolkin, 2014-07-20 15:36:35)

Re: WSH: Определение кодировки файла

Типа, отчёт.
Погонял два скрипта на двух наборах файлов. Оба скрипта ищут кодовую страницу в первых 2k каждого файла тремя способами: в XML declaration, в <meta ... charset=, если они есть, и по контенту - BOM и разбор имеющихся символов (цель - выявить файлы, сохранённые "не в той" кодировке). Первый использует текстовый поток fso, второй (старый) - ADODB.Stream. В одном каталоге ~100 файлов размером <30kb, в другом - ~30 файлов размером 100-300kb. Все файлы HTML/HTA, размеры обеих папок ~2Mb
Так вот, если на больших файлах текстовый поток fso - однозначный фаворит (работает в разы быстрее, не жрёт память и, главное, не виснет), то на папке с маленькими файлами ADODB не только не проигрывает, а даже выходит вперёд на 5-10% (по скорости).

Вывод: если для универсальной функции (типа той, что предложил JSman) использовать текстовый поток очень даже правильно, то для конкретного применения в реальном скрипте всё зависит от предполагаемого размера обрабатываемых файлов.

40

Re: WSH: Определение кодировки файла

Serge Yolkin, в скрипт еще необходимо добавить распознавание UTF-16 без сигнатуры. Как это сделать, надо думать. Гугл пока не помогает.

3F 04 40 04 38 04 32 04 35 04 42 04 00 00

Есть идея: тупо перебирать кодировки и смотреть, попадает ли большинство символов в интервал алфавита либо печатных символов. Если да, то кодировка правильно определена.

Что касается выбора между FSO и ADODB.Stream, то это выходит за рамки темы: верным ответом будет использование компонента "SAPI.spFileStream". Впрочем решение покажу в отдельной теме.

41

Re: WSH: Определение кодировки файла

В AutoHotkey тоже как то обсуждалось, и зависло на уровне: кто же их знает, они все такие разные. Но как бы не смеялись над AutoHotkey, Я новых методов тут не увидел.

Как то ведь продвинутые блокноты определяют кодировку? И когда в них открыто сразу уйма файлов, на первый взгляд не долго (наверное) парятся для её безошибочного (наверное) определения.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
Win10x64 v20H2, AutoHotkey_L v1.1.33.09 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui

42

Re: WSH: Определение кодировки файла

JSman
UTF-16 без BOM для кириллицы в моём примере есть. С комментарием

serzh82saratov
безошибочных ещё не встречал. Они используют статистический метод - по частоте некоторых символов. Для текста работает, а если килобайт кода (латиница) и русский комментарий - пара слов - ближе к концу, то для однобайтовых - как повезёт.

43

Re: WSH: Определение кодировки файла

а если килобайт кода (латиница) и русский комментарий - пара слов - ближе к концу, то для однобайтовых - как повезёт.


То есть, в жестоком мире разных кодировок, нет никакой уверености в натоящем && будущем?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
Win10x64 v20H2, AutoHotkey_L v1.1.33.09 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui

44

Re: WSH: Определение кодировки файла

Опа! Самое смешное, что слово SAPI мне попадалось, но подумал: при чём здесь спич? У меня и звук, обычно, выключен... А оно вона как.

45

Re: WSH: Определение кодировки файла

serzh82saratov
В общем случае - никаких гарантий. В каждом конкретном - можно сделать какие-то предположения и допущения, тогда проще.

46

Re: WSH: Определение кодировки файла

предположения и допущения

И как же существовать дальше? Как то ненаучно, или тут вывод: что в принципе этого не может быть, потому что быть не может.

Опа! Самое смешное, что слово SAPI мне попадалось, но подумал: при чём здесь спич? У меня и звук, обычно, выключен... А оно вона как.

А в чём радость? Все из 4+ годовалого топика тут высказались. А где панацея?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru
Win10x64 v20H2, AutoHotkey_L v1.1.33.09 (Unicode 32-bit). AhkSpy, Hotkey, ClockGui

47

Re: WSH: Определение кодировки файла

serzh82saratov
Не утверждаю, что этот случай Вам интересен, но чисто для примера: пишу .bat файл, кодировка, естественно, ASCII (OEM). Для оформления вывода использую псевдографику - целые строки из чёрточек и уголков. Так вот, эти символы не попадают в диапазон русских для ASCII, но попадают для ANSI. И что должен делать детектор кодировки? Угадать, что я псевдографику раз в сто лет использовал?

А на счёт SAPI - прошу прощения, это я JSman'у писал. К кодировкам отношения не имеет.

48

Re: WSH: Определение кодировки файла

Проверяйте Код снизу читайте. Определение кодировок с BOM и без.


var GlobalObject = this;
var FSO = fso = new ActiveXObject("Scripting.FileSystemObject");
var WshShell = new ActiveXObject("WScript.Shell");

Function.prototype.GetResource = function(ResourceName) {
    if (!this.Resources) {
        var UnNamedResourceIndex = 0,
            _this = this;
        this.Resources = {};

        function f(match, resType, Content) {
            _this.Resources[(resType == "[[") ? UnNamedResourceIndex++:resType.slice(1, -1)] = Content;
        }
        this.toString().replace(/\/\*(\[(?:[^\[]+)?\[)((?:[\r\n]|.)*?)\]\]\*\//gi, f);
    }
    return this.Resources[ResourceName];
}

Array.prototype.forEach = function (action, that /*opt*/ ) {
    for (var i = 0, n = this.length; i < n; i++)
    //if (i in this)
        action.call(that, this[i], i, this);
};

var VBSEngine = new ActiveXObject('ScriptControl');
VBSEngine.Language = 'VBScript';
VBSEngine.AddCode(Resourses.GetResource("vbs"));

function Resourses() {
    /*[vbs[
Function [_MidB](v, s, l):[_MidB]=MidB(v, s+1, l):End Function
Function [_Asc](v):[_Asc]=Asc(v):End Function
Function [_Chr](v):[_Chr]=Chr(v):End Function
Function [_LenB](v):[_LenB]=LenB(v):End Function
Function [_VarType](v):[_VarType]=VarType(v):End Function
Function [_TypeName](v):[_TypeName]=TypeName(v):End Function
Function [_ChrB](v):[_ChrB]=ChrB(v):End Function
Function [_AscB](v):[_AscB]=AscB(v):End Function
Function [_LeftB](v, l):[_LeftB]=LeftB(v, l):End Function
Function [_RightB](v, l):[_RightB]=RightB(v, l):End Function
Function [_InstrB](start, stringtosearch, stringtofind, comparemode):[_InstrB]=InstrB(start, stringtosearch, stringtofind, comparemode):End Function

Sub ConvertBinaryToJSArray(arrByteArray, js_arr)
Dim i
For i = 1 To LenB(arrByteArray)
    js_arr.push(AscB(MidB(arrByteArray, i, 1)))
Next
End Sub

Function MultiByteToBinary(MultiByte)
  ' 2000 Antonin Foller, http://www.motobit.com
  ' MultiByteToBinary converts multibyte string To real binary data (VT_UI1 | VT_ARRAY)
  ' Using recordset
  Dim RS, LMultiByte, Binary
  Const adLongVarBinary = 205
  Set RS = CreateObject("ADODB.Recordset")
  LMultiByte = LenB(MultiByte)
  If LMultiByte>0 Then
    RS.Fields.Append "mBinary", adLongVarBinary, LMultiByte
    RS.Open
    RS.AddNew
      RS("mBinary").AppendChunk MultiByte & ChrB(0)
    RS.Update
    Binary = RS("mBinary").GetChunk(LMultiByte)
  End If
  MultiByteToBinary = Binary
End Function

Function RSBinaryToString(xBinary)
'1999 Antonin Foller, Motobit Software
'This function converts binary data (VT_UI1 | VT_ARRAY or MultiByte string)
'to string (BSTR) using ADO recordset
'The fastest way - requires ADODB.Recordset
'Use this function instead of MBBinaryToString if you have ADODB.Recordset installed
'to eliminate problem with PureASP performance

Dim Binary
'MultiByte data must be converted to VT_UI1 | VT_ARRAY first.
if vartype(xBinary) = 8 then Binary = MultiByteToBinary(xBinary) else Binary = xBinary

Dim RS, LBinary
Const adLongVarChar = 201
Set RS = CreateObject("ADODB.Recordset")
LBinary = LenB(Binary)

if LBinary>0 then
RS.Fields.Append "mBinary", adLongVarChar, LBinary
RS.Open
RS.AddNew
RS("mBinary").AppendChunk Binary
RS.Update
RSBinaryToString = RS("mBinary")
Else
RSBinaryToString = ""
End If
End Function
]]*/
}
WshShell.CurrentDirectory = GetScriptDir();;

function BinaryMid(s, l, c) {
    return VBSEngine.CodeObject._MidB(s, l, c);
}

function BinaryLen(s) {
    return VBSEngine.CodeObject._LenB(s);
}

function ArrayToBinary(ar) {
    var ms = new ActiveXObject("SAPI.spMemoryStream");
    for (var i = 0, l = ar.length; i < l; i++) {
        ms.Write(ar[i]);
        ms.Seek(i + 1);
    }
    s = ms.GetData();
    var l = BinaryLen(s);
    s = BinaryMid(s, 0, l - 3)
    return MultiByteToBinary(s);
}

function Binary(s) {
    if (!s) s = 0;
    var value;
    var type = TypeOf(s);
    if (type == "array") value = ArrayToBinary(s);
    if (type == "bytearray") value = s;
    if (type == "function" || type == "object" || type == "empty") return null;
    try {
        var ms = new ActiveXObject("SAPI.spMemoryStream");
        ms.SetData(s.valueOf());
        value = ms.GetData();
    } catch(e) {}
    if (arguments.callee != this.constructor) return value;
    this.value = value;
}

function MultiByteToBinary(x) {
    return VBSEngine.CodeObject.MultiByteToBinary(x);
}

function BinaryToHex(binary) {
    var xml = new ActiveXObject('Microsoft.XMLDOM').createElement('Binary');
    xml.dataType = 'bin.hex';
    xml.nodeTypedValue = binary;
    return xml.text.toUpperCase();
}

function HexToBinary(text)
{
    var xml = new ActiveXObject('Microsoft.XMLDOM').createElement('Binary');
    xml.dataType = 'bin.hex';
    xml.text = text;
    
    return xml.nodeTypedValue;
}

function StringToBinary(str, dstCharset) { ! dstCharset ? dstCharset = "UTF-8" : 0;
    var stream = new ActiveXObject("ADODB.Stream");
    stream.type = 2;
    stream.open();
    stream.charset = dstCharset;
    stream.writeText(str);
    stream.position = 0;
    stream.type = 1;
    var result = stream.read();
    stream.close();
    return result;
}

//BinaryToString Converts a binary variant into a string.
function BinaryToString(binary, destCharset) {
    !destCharset ? destCharset = "UTF-8" : 0;
    
    var stream = new ActiveXObject("ADODB.Stream");
    stream.type = 1;
    stream.open();
    stream.write(binary);
    stream.position = 0;
    stream.type = 2;
    stream.charset = destCharset;
    var result = stream.readText();
    stream.close();
    return result;
}

function Asc(str) {
    return VBSEngine.CodeObject._Asc(str);
}

function TypeOf(o) {
    var type = typeof o;
    if (type != "object" && type != "unknown") return type;
    if (o === null || o === undefined) return "empty";
    if (Object.prototype.toString.call(o) === '[object Array]') return "array";
    if (Object.prototype.toString.call(o) === '[object Number]') return "number";
    if (Object.prototype.toString.call(o) === '[object String]') return "string";
    if (VBSEngine.CodeObject._VarType(o) == (8192 + 17)) return "bytearray";
    return type;
}

function Exit() {
    if (this.WScript) WScript.Quit();
    if (this.window) window.close();
}

function MsgBox(strText, strTitle, nType) {
    return WshShell.Popup(strText, 0, strTitle, nType);
}

function GetScriptDir() {
    return GetScriptFullPath().replace(/[^\\]+$/g, "");
}

function GetScriptFullPath() {
    if (this.location && this.location.href) return location.href.replace(/^.+\/\/\//, "").replace(/\//g, "\\");
    if (this.WScript) return WScript.ScriptFullName;
    return "";
}

function FileGetEncoding(path) {
    var f = FSO.OpenTextFile(WshShell.ExpandEnvironmentStrings(path), 1, 0, 0);
    var n = FSO.GetFile(path).Size;
    n = n <= 60 ? n : 60;
    var val = MultiByteToBinary(f.Read(n));
    f.Close();
    return EncodingDetect(val);
}

function EncodingDetect(bin) {
    var head = BinaryToHex(bin);
    if (/^EFBBBF/.test(head)) return "UTF-8";
    if (/^0000FEFF/.test(head)) return "UTF-32BE";
    if (/^FFFE0000/.test(head)) return "UTF-32LE";
    if (/^2B2F76(38|39|2B|2F)/.test(head)) return "UTF-7";
    if (/^F7644C/.test(head)) return "ISO-10646-UTF-1";
    if (/^DD736673/.test(head)) return "UTF-EBCDIC";
    if (/^0EFEFF/.test(head)) return "SCSU";
    if (/^FBEE28/.test(head)) return "BOCU-1";
    if (/^84319533/.test(head)) return "GB-18030";
    if (/^FEFF0000/.test(head)) return "ISO-10646-UCS-4";
    if (/^0000FFFE/.test(head)) return "ISO-10646-UCS-4";
    if (/^FEFF/.test(head)) return "UTF-16BE";
    if (/^FFFE/.test(head)) return "UTF-16LE";
    var o = {
        "UTF-32": 0,
        "UTF-16": 0,
        "UTF-8": 0,
        "ASCII": 0
    },
        re = /[-A-Za-z0-9А-Яа-я\?!=<>"\\\/\s\r\n]/,
        max = 0,
        maxP = "ASCII";

    function _tmp(p) {
        try {
            BinaryToString(bin, p).split("").forEach(function(s) {
                if (Asc(s) == 0) o[p] -= 0.1;
                if (re.test(s)) o[p]++
            });
            if (max <= o[p]) {
                maxP = p;
                max = o[p]
            }
        } catch(e) {}
    }
    for (i in o) _tmp(i);
    
    //MsgBox([o["UTF-32"], o["UTF-16"], o["UTF-8"], o["ASCII"]]);
    return maxP;
}
//*
MsgBox(EncodingDetect(Binary("привет")));
MsgBox(EncodingDetect(StringToBinary("привет", "UTF-8")));
MsgBox(EncodingDetect(StringToBinary("привет", "ascii")));
MsgBox(EncodingDetect(Binary("hello")));
MsgBox(EncodingDetect(StringToBinary("hello", "UTF-8")));
MsgBox(EncodingDetect(StringToBinary("hello", "ascii")));

MsgBox(FileGetEncoding("12345.txt"));
Exit();

49

Re: WSH: Определение кодировки файла

Что касается ASCII (OEM, Win1251 и т.д.). Думаю надо отличать понятия encoding и charset (или чушь говорю?). В указанном выше коде речь идет только о способе хранения данных, то есть о encoding.

50

Re: WSH: Определение кодировки файла

Serge Yolkin пишет:

serzh82saratov
Не утверждаю, что этот случай Вам интересен, но чисто для примера: пишу .bat файл, кодировка, естественно, ASCII (OEM). Для оформления вывода использую псевдографику - целые строки из чёрточек и уголков. Так вот, эти символы не попадают в диапазон русских для ASCII, но попадают для ANSI. И что должен делать детектор кодировки? Угадать, что я псевдографику раз в сто лет использовал?

Да все не так сложно. Надо каждой категории символов присваивать вес. Обратите внимание на изменения в коде. Там я как раз и использовал принцип весов. Вообще, так и отличают OEM, Win1251, KOI8-R. Но я думаю, это уже будет вопрос к функции CharsetDetect, а не EncodingDetect

51

Re: WSH: Определение кодировки файла

serzh82saratov пишет:

А в чём радость? Все из 4+ годовалого топика тут высказались. А где панацея?

Ну, я только сейчас довольно близко подошел к SAPI. Только недавно разобрался как работать с бинарными данными на комфортном уровне (в частности научился читать и создавать байтовые массивы).

Указанные ниже функции будут работать без ADODB.Stream и FSO:

FileClose Closes a previously opened text file.
FileFlush Flushes the file's buffer to disk.
FileGetPos Retrieves the current file position.
FileOpen Opens a text file for reading or writing.
FileRead Read in a number of characters from a previously opened text file.
FileReadLine Read in a line of text from a previously opened text file.
FileSetPos Sets the current file position.
FileWrite Append a text/data to the end of a previously opened file.
FileWriteLine Append a line of text to the end of a previously opened text file.

52

Re: WSH: Определение кодировки файла

JSman
а маленький пример - как работать с тем, что возвращает поток SAPI из JS - мона? А то ранее опрбованные методы здесь не помогают...

53

Re: WSH: Определение кодировки файла

Serge Yolkin, в коде Binary, ArrayToBinary, BinaryToArray и т.п. Все там есть.

54

Re: WSH: Определение кодировки файла

JSman
Да, экспериментировал и не обновлял страничку...

А в различия терминов не вникал: в тех программах, которыми я пользуюсь это называется "Кодировка" и Кириллица Windows там отличается от кириллицы DOS (так же, как от UTF). Возможно, это дилетантский подход.

55

Re: WSH: Определение кодировки файла

14044004430436043804490435042C0020005B0062005D0058\
0061006D0065006C0065006F006E005B002F0062005D002E00\
20001F043E04370434044004300432043B044F044E042000420\
4350431044F0420004104200034043D0435043C04200040043\
E043604340435043D0438044F0421002000160435043B043004\
4E04200032044104350433043E042000410430043C043E0433\
043E0420003B04430447044804350433043E0421000D000A00\
0D000A0020001E0434043D043E043204400435043C0435043D0\
43D043E04200032044B0440043004360430044E0420003E0433\
0440043E043C043D0443044E04200031043B04300433043E043\
404300440043D043E04410442044C042000370430042000430\
44704300441044204380435042000320420003F0440043E0435\
043A04420435042000440440043504390432043E0440043A0430042100

56

Re: WSH: Определение кодировки файла

JSman
А как?

57

Re: WSH: Определение кодировки файла

Serge Yolkin, подсказываю. Из HEX-данных переводим в вдвоичные, далее определяем кодировку, после чего смотрим результат. Секретное сообщение как никак. Как ознакомитесь, предлагаю присоединиться

58

Re: WSH: Определение кодировки файла

Присоединяюсь!
Там, в конце, перед 0432 надо ещё 043C добавить (кодировку на глаз определил)

А на счёт участия, всё-таки я дилетант-самоучка. Потестить что-нибудь интересное - с удовольствием, но по возможности, а всерьёз - левел не тот.