1

Тема: JS: Сравнение бинарных файлов

Для сравнения двух бинарных файлов использую скрипт (Использование: Compare.js <файл1> <файл2>):

var Title = 'Сравнение mp3 файлов',
    Stream = new ActiveXObject('ADODB.Stream'),
    FSO = WScript.CreateObject('Scripting.FileSystemObject'),
    objArgs = WScript.Arguments;
Stream.Type = 2;
Stream.Open();
Stream.LoadFromFile(FSO.GetAbsolutePathName(objArgs(0)));
var File1 = Stream.ReadText();
Stream.Close();
Stream.Open();
Stream.LoadFromFile(FSO.GetAbsolutePathName(objArgs(1)));
var File2 = Stream.ReadText();
Stream.Close();

if (File1 == File2) var Compare = 'Одинаковые'
else var Compare = 'Разные';
var WSHShell = WScript.CreateObject('WScript.Shell');
WSHShell.Popup(Compare, 0, Title)

Но чё-то как-то долго сравниваются даже 5Мб файлы. Может, есть способ попроще?

PS: Это только часть задуманного скрипта, поэтому не использую уже готовые программы, типа FileCompare.

2

Re: JS: Сравнение бинарных файлов

Крепыш пишет:

...
Но чё-то как-то долго сравниваются даже 5Мб файлы.
...

Вы как хотели? Два раза по 5МБ загрузить в память, а потом по-байтово сравнить две громадные 5МБ строки!!!

Крепыш пишет:

...
Может, есть способ попроще?
...

Есть - вызывайте команду fc и анализируйте код завершения. Как работает fc можно узнать так:

fc /?
( 2 * b ) || ! ( 2 * b )

3

Re: JS: Сравнение бинарных файлов

Крепыш, если Вам требуется множественное сравнение — сначала лучше сравнивать по хэшу MD5, и лишь затем, в случае совпадения, чтобы быть уверенным наверняка (теоретически возможно существование одинаковых хэшей MD5 у отличающихся файлов), делать побайтное сравнение.

4

Re: JS: Сравнение бинарных файлов

Пишу скрипт для сравнения mp3 файлов, игнорируя ID3 теги (т.е. только сам поток mp3). Длина тегов может быть различной и рассчитывается на основании данных из заголовка. Соответственно, из каждого файла выкидывается начало (ID3v2) и конец (ID3v1), а то, что остаётся (собственно сам mp3 поток) сравнивается.
Поэтому, утилиты, типа FC, не подходят, как и расчёт хэшей.

Но даже просто чтение файла таким методом (не говоря уже о сравнении) происходит довольно долго. FC делает это на порядок быстрее.

5

Re: JS: Сравнение бинарных файлов

OFF: Крепыш, хм… И много ли у Вас таких файлов?

6

Re: JS: Сравнение бинарных файлов

Ну, вот примерно так:

var Title = 'Сравнение mp3 файлов',
    stream = new ActiveXObject('ADODB.Stream'),
    FSO = WScript.CreateObject('Scripting.FileSystemObject'),
    objArgs = WScript.Arguments,
    FileName1 = FSO.GetAbsolutePathName(objArgs(0)),
    FileName2 = FSO.GetAbsolutePathName(objArgs(1));

with (stream)
{
  Type = 2;
  Charset='IBM866';
  Open();
  LoadFromFile(FileName1);
  var File1 = ReadText();
  Close();
  Open();
  LoadFromFile(FileName2);
  var File2 = ReadText();
  Close();
}
with (File1)
{
  if (substr(0, 3) == 'ID3')
    var Begin1 = parseInt(charCodeAt(6).toString(2) + charCodeAt(7).toString(2) + charCodeAt(8).toString(2) + charCodeAt(9).toString(2), 2) + 10
  else var Begin1 = 0;
  if ((End1 = lastIndexOf('TAG')) == -1) End1 = length;
}
with (File2)
{
  if (substr(0, 3) == 'ID3')
    var Begin2 = parseInt(charCodeAt(6).toString(2) + charCodeAt(7).toString(2) + charCodeAt(8).toString(2) + charCodeAt(9).toString(2), 2) + 10
  else var Begin2 = 0;
  if ((End2 = lastIndexOf('TAG')) == -1) End2 = length;
}
if (File1.substring(Begin1, End1) == File2.substring(Begin2, End2))
{
  var Compare = 'Эти файлы одинаковы:\n' + FileName1 + '\n' + FileName2,
      nType = 0
}
else
{
  var Compare = 'Эти файлы РАЗЛИЧАЮТСЯ:\n' + FileName1 + '\n' + FileName2,
      nType = 48
}
WScript.CreateObject('WScript.Shell').Popup(Compare, 0, Title, nType)

Только чё-то долго идёт чтение файлов. Может, можно их как-то кусками читать и сравнивать?

alexii пишет:

И много ли у Вас таких файлов?

Да полно. На выходных удалил более тысячи таких дублей (это Гигов на пять будет).
А можно в JScript вычислить MD5? Мне бы это очень пригодилось.

7

Re: JS: Сравнение бинарных файлов

Ну ещё можно например сравнивать по переменно 2 кб потока, и если раличаются то перейти к следующему... Т.е. это оптимизировало бы алгоритм. А если совпадают данные, то следующие 2 кб брать и т.д. А вообще MD5 лучший вариант - так как можно создать словарь с хешами, путями файлов, отсортировать хеши, и уже будет видно какие файлы необходимо удалять. Алгоритмы на форуме обсуждались

8

Re: JS: Сравнение бинарных файлов

Угу. Поиск на форуме по ключевым словам MD5. Не всё ещё до конца оформлено/доделано, но понять вполне можно. Главная проблема именно в чтении бинарных файлов кусками.

9

Re: JS: Сравнение бинарных файлов

VSVLAD пишет:

Ну ещё можно например сравнивать по переменно 2 кб потока, и если раличаются то перейти к следующему...

Я тоже как раз об этом подумал.

alexii пишет:

Главная проблема именно в чтении бинарных файлов кусками.

Т.е., готового решения таки нету?

А по поводу MD5. Нашёл вот реализацию MD5 на JSript.
Можно ли использовать в скрипте функцию из другого скрипта (другого файла)?

10

Re: JS: Сравнение бинарных файлов

Крепыш, эта реализация есть и у нас, только на VBScript (VBScript: Вычисление хэша MD5 строки, первый пост). Всё равно будет медленно, лучше попробовать использовать что-либо из следующих постов по приведённой ссылке.

P.S. Вопрос эффективного чтения бинарных файлов всё равно остаётся.

11

Re: JS: Сравнение бинарных файлов

with (new ActiveXObject('ADODB.Stream')) {
  Type = 1;
  Open();
  LoadFromFile(FileName);
  var File = Read();
  Close();
}

Как узнать размер прочитанного и код символа (допустим, первого)?

12

Re: JS: Сравнение бинарных файлов

Option Explicit

Const adTypeBinary = 1

Dim objStream
Dim arrBytes, i, strText

Set objStream = WScript.CreateObject("ADODB.Stream")

With objStream
    .Type = adTypeBinary
    .Open()
    .LoadFromFile("C:\boot.ini")
    
    WScript.Echo "Size:", .Size
    
    arrBytes = .Read()
    
    .Close
End With

WScript.Echo "First char:", Chr(AscB(MidB(arrBytes, 1, 1)))
WScript.Echo "First char code:", AscB(MidB(arrBytes, 1, 1))

strText = ""

For i = 1 To LenB(arrBytes)
    strText = strText & Chr(AscB(MidB(arrBytes, i, 1)))
Next

WScript.Echo strText

Set objStream = Nothing

WScript.Quit 0

См. также: Серый форум / VBS: чтение и запись бинарных данных.

На свойство .Size накладывается некоторое ограничение:

Note   Any number of bits can be stored in a Stream object, limited only by system resources. If the Stream contains more bits than can be represented by a Long value, Size is truncated and therefore does not accurately represent the length of the Stream.

P.S. Насколько я помню, аналога AscB/MidB/LenB в JScript нет?!

13

Re: JS: Сравнение бинарных файлов

alexii пишет:

Насколько я помню, аналога AscB/MidB/LenB в JScript нет?!

В JScript похоже ничего нет для работы с байтами. Придётся мне свой код на VBScript переводить.

14

Re: JS: Сравнение бинарных файлов

Все, считанное из файла пишется в строковую переменную. Внутренний механизм хранения данных в строке - юникод.
String Object (Windows Scripting - JScript)

Крепыш
Посмотрите, на charAt, charCodeAt, fromCharCode - работа с отдельными символами (что не синонимично понятию байт)

Информация FileSystemObject Methods
Методы - OpenAsTextStream, OpenTextFile, CreateTextFile. Интересен параметр format, который принимает три состояния, из которых интересны
-1 - открыть файл как Юникод
0 - открыть файл как ASCII

Провел один эксперимент. Поиграйте с параметром OPEN_FORMAT (0 или -1) при чтении файла. Так вот при чтении файла в формате ASCII - длина строки будет 20 байт, при чтении как юникод - 9 (длина слова "Кириллица"). По-моему, бинарный файл достаточно считать в формате ASCII. Только надо ли это делать в JScript/VBScript?

var OPEN_FORMAT = -1; // Opens the file as Unicode
//var OPEN_FORMAT =  0; // Opens the file as ASCII  


var fso = new ActiveXObject('Scripting.FileSystemObject');

var f = 'c:\\testfile.txt';
var h;
var t = "Кириллица";
var s;


h = fso.CreateTextFile(f, true, true);
h.Write(t);
h.Close();


h = fso.OpenTextFile(f, 1, 0, OPEN_FORMAT);
s = h.Read(1024);
h.Close();

WScript.Echo(s);
WScript.Echo(s.length);
WScript.Echo(s == t);
( 2 * b ) || ! ( 2 * b )

15 (изменено: Крепыш, 2010-02-03 13:20:40)

Re: JS: Сравнение бинарных файлов

Rumata,
Это хорошо работает на текстовых файлах. Попробуйте прочесть таким образом бинарный файл.
JScript: прочитать байт в бинарном виде

16

Re: JS: Сравнение бинарных файлов

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

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

17

Re: JS: Сравнение бинарных файлов

Крепыш, посмотрите на библиотеку «newObjects ActiveX Pack1». Там большая подборка средств для работы с бинарными данными. Я вот только что с подачи BeS Yara начал смотреть на IMAPI и наткнулся на интересующий меня пример для программного создания ISO: Creating ISO files with vbscript possible???, в котором как раз используется компонент из этой библиотеки для того, чтобы записать бинарный поток в файл:

Option Explicit

' Enum FsiFileSystems
'Const FsiFileSystemNone    = 0
Const FsiFileSystemISO9660 = 1
Const FsiFileSystemJoliet  = 2
'Const FsiFileSystemUDF     = 4
'Const FsiFileSystemUnknown = 1073741824

'Enum IMAPI_MEDIA_PHYSICAL_TYPE
'Const IMAPI_MEDIA_TYPE_UNKNOWN             =  0
'Const IMAPI_MEDIA_TYPE_CDROM               =  1
'Const IMAPI_MEDIA_TYPE_CDR                 =  2
'Const IMAPI_MEDIA_TYPE_CDRW                =  3
'Const IMAPI_MEDIA_TYPE_DVDROM              =  4
'Const IMAPI_MEDIA_TYPE_DVDRAM              =  5
'Const IMAPI_MEDIA_TYPE_DVDPLUSR            =  6
'Const IMAPI_MEDIA_TYPE_DVDPLUSRW           =  7
'Const IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER  =  8
'Const IMAPI_MEDIA_TYPE_DVDDASHR            =  9
'Const IMAPI_MEDIA_TYPE_DVDDASHRW           = 10
'Const IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER  = 11
Const IMAPI_MEDIA_TYPE_DISK                = 12
'Const IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER = 13
'Const IMAPI_MEDIA_TYPE_HDDVDROM            = 14
'Const IMAPI_MEDIA_TYPE_HDDVDR              = 15
'Const IMAPI_MEDIA_TYPE_HDDVDRAM            = 16
'Const IMAPI_MEDIA_TYPE_BDROM               = 17
'Const IMAPI_MEDIA_TYPE_BDR                 = 18
'Const IMAPI_MEDIA_TYPE_BDRE                = 19
'Const IMAPI_MEDIA_TYPE_MAX                 = 19


Dim objFileSystemImage
Dim objSFStream
Dim strSourceFolder
Dim strISOVolumeName
Dim strISOImageName
Dim objFileSystemImageResult
Dim lngImageSize
Dim lngWrittenBytes

strSourceFolder  = "C:\10"
strISOVolumeName = "My ISO"
strISOImageName  = "c:\11\MyImage.iso"

' Create a new file system image and retrieve root directory       
With WScript.CreateObject("IMAPI2FS.MsftFileSystemImage")
    .ChooseImageDefaultsForMediaType IMAPI_MEDIA_TYPE_DISK
    .FileSystemsToCreate = FsiFileSystemISO9660 + FsiFileSystemJoliet
    .VolumeName = strISOVolumeName
    .Root.AddTree strSourceFolder, False
    
    ' Create the Image and pass the IMAPI.IStream to an object that supports IStream
    Set objSFStream = WScript.CreateObject("newObjects.utilctls.SFStream")
    objSFStream.SetStream .CreateResultImage().ImageStream
End With

' Create the output file and write the stream
With WScript.CreateObject("newObjects.utilctls.SFMain").CreateFile(strISOImageName)
    lngImageSize    = objSFStream.Size
    lngWrittenBytes = .WriteBin(objSFStream.ReadBin(lngImageSize))
    
    .Close
End With

WScript.Echo "ISO Image Size:", lngImageSize
WScript.Echo "Written Bytes: ", lngWrittenBytes

Может быть тоже найдёте там что-нибудь подходящее?!

18 (изменено: Крепыш, 2010-02-04 00:48:18)

Re: JS: Сравнение бинарных файлов

Переписал на VBScript:

Option Explicit
Dim Title, objArgs, objWSHShell

Title = "Сравнение MP3 файлов v.0.2a"
Set objArgs = WScript.Arguments
Set objWSHShell = WScript.CreateObject("WScript.Shell")

If objArgs.length=0 Then
  objWSHShell.Popup "Сравнение MP3 файлов." &vbCr& "При сравнении учитывается только сам MPEG поток." &vbCr& "Тэги ID3v1 и ID3v2 игнорируются." &vbCr&vbCr& "Использование:" &vbCr& WScript.ScriptName & " файл1 файл2", 0, Title, 64
  WScript.Quit
End If

Dim objFSO, File(1), size(1), f(1), i
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
With WScript.CreateObject("ADODB.Stream")
  .Type = 1
  For i=0 To 1
    File(i) = objFSO.GetAbsolutePathName(objArgs(i))
    .Open
    .LoadFromFile(File(i))
    size(i) = .Size
    f(i) = .Read
    .Close
  Next
End With
Set objArgs = Nothing
Set objFSO = Nothing

Dim ID3(1), TAG(1)
For i=0 To 1
'Проверяем в начале файла наличие заголовка ID3v2 тэга - строки "ID3"
  If AscB(MidB(f(i),1,1))=73 And AscB(MidB(f(i),2,1))=68 And AscB(MidB(f(i),3,1))=51 Then
'Длина заголовка ID3v2 тэга - 10 байт
'Последнее поле в заголовке тэга – это число, закодированное в четырех байтах, в каждом
'из которых используется лишь по семь бит,содержащее размер всего тэга без учета заголовка
    ID3(i) = AscB(MidB(f(i),7,1))*2^21 + AscB(MidB(f(i),8,1))*2^14 + AscB(MidB(f(i),9,1))*2^7 + AscB(MidB(f(i),10,1)) + 10
  Else
    ID3(i) = 0
  End If
'Проверяем в конце файла наличие заголовка расширенного тэга ID3v1 (строки "TAG+")
'размером 227 байт со смещением 128 байт с конца
  If AscB(MidB(f(i),size(i)-354,1))=84 And AscB(MidB(f(i),size(i)-353,1))=65 And AscB(MidB(f(i),size(i)-352,1))=71 And AscB(MidB(f(i),size(i)-351,1))=43 Then
      TAG(i) = 355
'Проверяем в конце файла наличие заголовка ID3v1 тэга (строки "TAG") размером 128 байт
  ElseIf AscB(MidB(f(i),size(i)-127,1))=84 And AscB(MidB(f(i),size(i)-126,1))=65 And AscB(MidB(f(i),size(i)-125,1))=71 Then
      TAG(i) = 128
  Else
      TAG(i) = 0
  End If
Next

'Dim hash(1)
'With WScript.CreateObject("CAPICOM.HashedData")
'  .Algorithm = 3
'  .Hash(MidB(f(0), ID3(0)+1, size(0)-ID3(0)-TAG(0)))
'  hash(0) = .Value
'  .Hash(MidB(f(1), ID3(1)+1, size(1)-ID3(1)-TAG(1)))
'  hash(1) = .Value
'End With
'If hash(0) = hash(1) Then

If MidB(f(0),ID3(0)+1,size(0)-ID3(0)-TAG(0)) = MidB(f(1),ID3(1)+1,size(1)-ID3(1)-TAG(1)) Then
  objWSHShell.Popup "Эти файлы одинаковы:" &vbCr& File(0) &vbCr& File(1), 0, Title, 64
Else
  objWSHShell.Popup "Эти файлы РАЗЛИЧАЮТСЯ:" &vbCr& File(0) &vbCr& File(1), 0, Title, 48
End If

По сравнению с предыдущей версией работает на порядок быстрее благодаря бинарномы режиму чтения файла (Stream.Type = 1). По скорости сравнение 100-Мегабайтных файлов при этом алгоритме сопоставимо со сравнением 5-Мегабайтных файлов в предыдущем случае.
Время, затрачиваемое на побайтное сравнение и вычисление хэша MD5 (закоментировано), "на глаз" примерно одинаково.

Теперь задача: пройтись рекурсивно по папке с музыкой и посчитать MD5 хэши для всех файлов.

С VBScript не знаком, так что поправьте, если что не так. И дайте, пожалуйста, ссылку на справочник по VBScript на русском (типа такого).

Кстати, обязательно удалять объекты (Set objArgs = Nothing) после использования?

19

Re: JS: Сравнение бинарных файлов

OFF:

Крепыш пишет:

…сравнение 100-Мегабайтных файлов…

И где Вы такие mp3 находите

Крепыш пишет:

Время, затрачиваемое на побайтное сравнение и вычисление хэша MD5 (закоментировано), "на глаз" примерно одинаково.

Колоссальная разница будет наблюдаться, когда потребуется сравнивать множество файлов между собой: в случае побайтного сравнения каждого с каждым — потребуется примерно N раз читать каждый файл, тогда как при сравнении хэшей — только один раз, что в любом случае «перевесит» требуемое время на вычисление хэша (особенно, если использовать не скриптовый, а «компонентный» способ вычисления хэша).

Крепыш пишет:

Кстати, обязательно удалять объекты (Set objArgs = Nothing) после использования?

Желательно сразу привыкать к этому. В принципе, как только заканчивается область видимости переменной, неявно вызывается деструктор объекта, в котором, собственно, и производится освобождение ссылки, очистка памяти, занятой объектом и возврат её менеджеру виртуальной памяти.

При явном вызове «Set …= Nothing» сие происходит сразу (в отличие от JScript, где сборщик «мусора» сам решает, когда чистить память; проблема эта настолько серьёзна, что даже была добавлена недокументированная команда для принудительной сборки мусора «CollectGarbage()» — см., например: HTA JavaScript: Как правильно закрыть Excel?). Иногда бывает так, что забывают подчищать за собой в нужных местах (чаще всего в разнообразных циклах, где на каждом проходе создаётся новый объект), и тогда, что называется, скрипт начинает «кушать» память. Потому я предпочитаю всегда указывать «Set …= Nothing», даже когда сие будет сделано автоматически, дабы не отвыкать.

Крепыш пишет:

С VBScript не знаком, так что поправьте, если что не так. И дайте, пожалуйста, ссылку на справочник по VBScript на русском (типа такого).

Вас интересует сам язык или набор объектов WSH (хотя последнее — вряд ли, Вы ведь на JScript пользовали те же объекты)? По первому — на русском, честно говоря, даже не знаю. Но, может, коллеги с этим помогут.

20

Re: JS: Сравнение бинарных файлов

alexii пишет:

Вас интересует сам язык

Да, меня интересует сам VBScript, его синтаксис, операторы, функции...

alexii пишет:

В принципе, как только заканчивается область видимости переменной, неявно вызывается деструктор объекта, в котором, собственно, и производится освобождение ссылки, очистка памяти, занятой объектом и возврат её менеджеру виртуальной памяти.
При явном вызове «Set …= Nothing» сие происходит сразу

На всякий случай уточню: таким образом удаляют только ссылки на объекты или любые переменные?
(Я извиняюсь, если неточно выражаюсь, но я не программист и единственный язык программирования, который я знаю - это JScript. Но понятия "объект", "метод", "класс"... - для меня "тёмный лес".)

И ещё вопросик:

Dim objFSO
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(f = file1) Or objFSO.FileExists(f = file2) Then WScript.Echo(f)

Как записать, чтобы возвращался результат присваивания, как в JScript, а не сравнения?

21

Re: JS: Сравнение бинарных файлов

На всякий случай уточню: таким образом удаляют только ссылки на объекты или любые переменные?

Только на объекты.

Как записать, чтобы возвращался результат присваивания, как в JScript, а не сравнения?

Никак. Операция присваивания — это отдельный оператор в VBScript (ныне не поддерживаемый оператор «LET …»), потому присваивание в данном контексте невозможно. В JScript используется унаследованный от С поточный (от «поток») метод написания кода, с ограничителем; в VBScript, как наследнике BASIC'а и FORTRAN'а, — старый строчный метод, без ограничителей:

If a = 1 Then b = 2 Else b = 3

и, в ряде конструкций, блочный, в качестве ограничителей могут выступать некоторые конструкции самого языка:

If a = 1 Then
    b = 2
Else
    b = 3
End If

22 (изменено: VSVLAD, 2010-02-05 01:20:15)

Re: JS: Сравнение бинарных файлов

В условиях присваивать нельзя. Оператор равенства является оператором сравнения, а не присвоения в конструкции If.

Опоздал... Уже ответили

23 (изменено: Крепыш, 2010-02-06 16:25:30)

Re: JS: Сравнение бинарных файлов

Если задан 1 параметр, то считается, что это каталог и создаётся файл хэшей MD5 всех mp3 в этом каталоге и всех подкаталогах. Если задано 2 параметра, то считается, что заданы имена файлов, и они сравниваются побайтно без тэгов.

Option Explicit
Dim Title
Title = "Сравнение MP3 файлов v.0.3a"

Const Capicom_Hash_Algorithm_SHA1    = 0
Const Capicom_Hash_Algorithm_MD2     = 1
Const Capicom_Hash_Algorithm_MD4     = 2
Const Capicom_Hash_Algorithm_MD5     = 3
Const Capicom_Hash_Algorithm_SHA_256 = 4
Const Capicom_Hash_Algorithm_SHA_384 = 5
Const Capicom_Hash_Algorithm_SHA_512 = 6

Dim objArgs, objWSHShell
Set objArgs = WScript.Arguments
Set objWSHShell = WScript.CreateObject("WScript.Shell")

If objArgs.Count=0 Then
  objWSHShell.Popup "Сравнение MP3 файлов." +vbCrLf+ "При сравнении учитывается только сам MPEG поток." +vbCrLf+ "Тэги ID3v1 и ID3v2 игнорируются." +vbCrLf+vbCrLf+ "Использование:" +vbCrLf+ WScript.ScriptName & " Каталог | Файл1 Файл2", 0, Title, 64
  WScript.Quit(1)
End If

Dim objFSO
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")

If objArgs.Count=1 Then
  Dim FolderName
  FolderName = objFSO.GetAbsolutePathName(objArgs(0))
  If Not objFSO.FolderExists(FolderName) Then
    objWSHShell.Popup "Каталог """ + FolderName + """ не найден.", 0, Title, 48
    WScript.Quit(1)
  End If

  Dim objInitialFolder, LogFile
  Set objInitialFolder = objFSO.GetFolder(FolderName)
  LogFile = objFSO.BuildPath(FolderName, objFSO.GetBaseName(FolderName)) + ".md5"

  Call GetListFiles(objInitialFolder)
  Call BrowseFolders(objInitialFolder)

  Set objInitialFolder = Nothing
  objWSHShell.Popup "Создан файл контрольных сумм MD5:" +vbCrLf+ LogFile, 0, Title
  WScript.Quit(0)
End If

Dim FileName(1), i
For i=0 To 1
  FileName(i) = objFSO.GetAbsolutePathName(objArgs(i))
  If Not objFSO.FileExists(FileName(i)) Then
    objWSHShell.Popup "Файл """ + FileName(i) + """ не найден.", 0, Title, 48
    WScript.Quit(1)
  End If
Next

Set objArgs = Nothing
Set objFSO = Nothing

'If GetHash(ReadMPEG(FileName(0))) = GetHash(ReadMPEG(FileName(1))) Then
If ReadMPEG(FileName(0)) = ReadMPEG(FileName(1)) Then
  objWSHShell.Popup "Эти файлы одинаковы:" +vbCrLf+ FileName(0) +vbCrLf+ FileName(1), 0, Title, 64
Else
  objWSHShell.Popup "Эти файлы РАЗЛИЧАЮТСЯ:" +vbCrLf+ FileName(0) +vbCrLf+ FileName(1), 0, Title, 48
End If

Set objWSHShell = Nothing
WScript.Quit(0)
'------------------------------------------------------------------------------

Sub BrowseFolders(Folder)
  Dim objFolder
  For Each objFolder In Folder.SubFolders
    Call GetListFiles(objFolder)
    Call BrowseFolders(objFolder)
  Next
  Set objFolder = Nothing
End Sub

Sub GetListFiles(Folder)
  Dim objFile, FileName
  For Each objFile in Folder.Files
    FileName = objFile.Path
    If LCase(objFSO.GetExtensionName(FileName)) = "mp3" Then
      With objFSO.OpenTextFile(LogFile, 8, true)
    .WriteLine(GetHash(ReadMPEG(FileName)) + " *" + FileName)
    .Close
      End With
    End If
  Next
  Set objFile = Nothing
End Sub

Function ReadMPEG(FileName)
  Dim size, f
  With WScript.CreateObject("ADODB.Stream")
    .Type = 1
    .Open
    .LoadFromFile(FileName)
    size = .Size
    f = .Read
    .Close
  End With

  Dim ID3, TAG
  If size>10 And AscB(MidB(f,1,1))=73 And AscB(MidB(f,2,1))=68 And AscB(MidB(f,3,1))=51 Then
    ID3 = AscB(MidB(f,7,1))*2^21 + AscB(MidB(f,8,1))*2^14 + AscB(MidB(f,9,1))*2^7 + AscB(MidB(f,10,1)) + 10
  Else
    ID3 = 0
  End If
  If size>355 And AscB(MidB(f,size-354,1))=84 And AscB(MidB(f,size-353,1))=65 And AscB(MidB(f,size-352,1))=71 And AscB(MidB(f,size-351,1))=43 Then
      TAG = 355
  ElseIf size>128 And AscB(MidB(f,size-127,1))=84 And AscB(MidB(f,size-126,1))=65 And AscB(MidB(f,size-125,1))=71 Then
      TAG = 128
  Else
      TAG = 0
  End If
  ReadMPEG = MidB(f,ID3+1,size-ID3-TAG)
End Function

Function GetHash(arrBytes)
  With WScript.CreateObject("CAPICOM.HashedData")
    .Algorithm = Capicom_Hash_Algorithm_MD5
    .Hash arrBytes
    GetHash = .Value
  End With
End Function

1. Как в функции GetListFiles() помимо вывода в файл, вывести на консоль имя обрабатываемого файла?
2. Переменные, заданные в функциях, будут локальными? Т.е., при выходе из функции ReadMPEG() память очищается от значения переменной f? А то не охото "таскать" в памяти содержимое всего файла.

24

Re: JS: Сравнение бинарных файлов

Крепыш пишет:

1. Как в функции GetListFiles() помимо вывода в файл, вывести на консоль имя обрабатываемого файла?

Отвечаю сам себе:

...
With objFSO.OpenTextFile(LogFile, 8, true)
    strHash = GetHash(ReadMPEG(FileName)) + " *" + FileName
    .WriteLine(strHash)
    Wscript.Echo(strHash)
    .Close
End With
...

и запускать через консольную CScript.exe.
Теперь вопрос: можно ли оформить этот скрипт в WSH, чтобы он обрабатывался CScript'ом?
И остался 2-й вопрос: Переменные, заданные в функциях, будут локальными? Т.е., при выходе из функции ReadMPEG() память очищается от значения переменной f? А то не охото "таскать" в памяти содержимое всего файла.

25

Re: JS: Сравнение бинарных файлов

Теперь вопрос: можно ли оформить этот скрипт в WSH, чтобы он обрабатывался CScript'ом?

То есть? От того, в каком формате скрипт, хост не зависит.

Проверить, какой host можно командой:

WScript.Echo WScript.FullName

Затем, по потребности, перезапустить под другим хостом.

Полностью сменить умолчальный хост на консольный «cscript.exe» можно командой:

cscript.exe //H:CScript

или:

wscript.exe //H:CScript

К сожалению, нет возможности задать хост в файле описания *.wsh, наподобие других параметров.

26

Re: JS: Сравнение бинарных файлов

2. Переменные, заданные в функциях, будут локальными? Т.е., при выходе из функции ReadMPEG() память очищается от значения переменной f?

Да. Да.

27 (изменено: Rumata, 2010-02-07 10:55:36)

Re: JS: Сравнение бинарных файлов

Крепыш пишет:

... можно ли оформить этот скрипт в WSH, чтобы он обрабатывался CScript'ом?...

Нельзя. Но есть слабый выход из этого положения.

Иногда я делаю принудительную проверку, чтобы скрипт выполнялся ТОЛЬКО в консольном режиме.

// Если это не CSCRIPT.EXE - завершить
if ( ! WScript.FullName.match(/cscript/i) ) {
    WScript.Quit();
}
( 2 * b ) || ! ( 2 * b )

28

Re: JS: Сравнение бинарных файлов

Rumata
Можно сразу перезапустить в консольный режим:

Option Explicit
Dim objFSO, objArgs, Param, n
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objArgs = WScript.Arguments
n = objArgs.Count
Param = ""

If n>0 Then
  Dim i
  With New RegExp
    .Pattern = "(^.+ .+$)"
    For i=0 To n-1
      Param = Param + " " + .Replace(objArgs(i), """$1""")
    Next
  End With
End If

If Not LCase(objFSO.GetFileName(WScript.FullName)) = "cscript.exe" Then
  WScript.CreateObject("WScript.Shell").Run("CScript.exe """ + WScript.ScriptFullName + """" + Param)
  WScript.Quit(1)
End If

WScript.Echo(WScript.FullName)
WScript.Sleep(3000)

29

Re: JS: Сравнение бинарных файлов

Вставил контроль ошибок, если нет доступа к файлу:

Function ReadMPEG(FileName)
  Dim size, f
  With WScript.CreateObject("ADODB.Stream")
    .Type = 1
    .Open
    On Error Resume Next
      .LoadFromFile(FileName)
    If Err.Number = 0 Then
      On Error Goto 0
    Else
      Err.Clear
      On Error Goto 0
      Wscript.Echo("Не удается открыть файл " + FileName + ".")
      Exit Function
    End If
    size = .Size
    f = .Read
    .Close
  End With
...

Как проверить, код возврата функции?

30

Re: JS: Сравнение бинарных файлов

…
ReadMPEG = bla-bla-bla
Exit Function
…

Вообще же момент интересный, и каждый решает его по-своему.

31 (изменено: Крепыш, 2010-02-07 22:51:40)

Re: JS: Сравнение бинарных файлов

alexii, спасибо!

А как при открытии текстового файла методом

With WScript.CreateObject("ADODB.Stream")
  .Type = 2
  .Charset = "UTF-8"
...
  .Close
End With

добавить строку в конец файла?

32

Re: JS: Сравнение бинарных файлов

Крепыш, Вы бы уж определились на чем писать - JScript или VBScript. Я понимаю что WSH-технология позволяет готовить винегрет почти из любых языков, зарегистрированных в системе, но надо же соблюдать хоть какой-то порядок, пусть даже в собственном коде. И документацию все-таки надо открывать. Это к вопросам типа "А как...".

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

33

Re: JS: Сравнение бинарных файлов

Где здесь файл? Я вижу только поток — Stream. Если Вы подразумевали ещё и .LoadFromFile(), то, например, так:

Option Explicit

Const adTypeText            = 2
Const adModeReadWrite       = 3
Const adWriteLine           = 1
Const adSaveCreateOverWrite = 2


Dim objStream
Dim strContent

Set objStream = WScript.CreateObject("ADODB.Stream")

With objStream
    .Type = adTypeText
    .Mode = adModeReadWrite
    
    .Open
    .Charset = "cp866"
    
    .LoadFromFile "c:\boot.txt"
    
    ' После .LoadFromFile() текущая позиция в начале потока
    ' Читаем из потока, что прочли из файла и выводим на экран
    strContent = .ReadText
    WScript.Echo strContent
    
    ' Перемещаем текущую позицию в конец потока
    .Position = .Size
    
    ' Что-то пишем в поток, в виде строки с разделителем
    .WriteText "; Hello, world!", adWriteLine
    ' Ещё что-то пишем…
    .WriteText "; Hello, world!", adWriteLine
    
    ' Текущую позицию в начало потока
    .Position = 0
    
    ' Опять читаем из потока, что получилось, и выводим на экран
    strContent = .ReadText
    WScript.Echo strContent
    
    ' Пишем содержимое потока в новый файл
    .SaveToFile "c:\boot_bak.txt", adSaveCreateOverWrite
    
    ' Закрываем поток
    .Close
End With

Set objStream = Nothing

WScript.Quit 0

Крепыш, давайте всё-таки будем придерживаться Правил: один вопрос — одна тема, бо мы уже здесь сборную солянку сделали. Оставим эту тему для вопросов, соответствующих заголовку.