1

Тема: JScript: Альтернативные чтение и запись файлов

Приветствую. Думаю, что для вас не секрет, что чтение файлов посредством FileSystemObject и ADODB.Stream довольно тормозное. Хорошей и скоростной альтернативой может послужить SAPI.spFileStream.

В общем, я написал обертку, которая позволяет работать с файлами как с бинарными данными, так и с текстовыми. Она позволяет работать с файлами огромного размера на высокой скорости. Данная технология не грузит файлы целиком в память,  а работает только с нужными фрагментами.  В настоящий момент осуществляется поддержка текстовой кодировки UTF-8.

Функции:
FileOpen (FilePath, Mode) - Открытие файла.  FilePath - это путь к файлу. Mode - режим открытия, принимает следующие значения: 0 (по умолчанию) - режим чтения, 1 - режим записи (каретка находится в конце файла), 2 - режим перезаписи, 8 - создание структуры папок, если ее не существует. Возвращает так называемый FileHandle.

FileRead (FileHandle, count, result) - Читает файл. FileHandle - это либо путь к файлу, либо то, что возвращает FileOpen. сount (по умолчанию - 0) - сколько байт читать. Если параметр равен нулю или опущен, то считывается весь файл. result - объект, в котором будут записаны свойства buffer (результат чтения) и count (сколько байт прочитано). Функция возвращает то, что было прочитано (то же самое, что result.buffer).

FileReadLine(FileHandle) - Читает строку в файле.

FileWrite(FileHandle, data) - Пишет в файл. FileHandle - это либо путь к файлу, либо то, что возвращает FileOpen. data - данные.
FileWriteLine(FileHandle, data) - без комментариев.

FileGetPos (FileHandle) - текущая позиция в файле.

FileSetPos(FileHandle, offset, origin). Задает позицию в файле. offset - смещение в байтах. origin - относительно чего осуществится смещение. Принимает следующие значения: 0 (по умолчанию) - начало файла, 1 - текущая позиция, 2 - конец файла.

Пример:


var Stream = FileOpen(GetScriptFullPath()+".123.txt", 2);
    
    MsgBox(FileGetPos(Stream));
    FileWriteLine(Stream, "Hello!");
    FileWrite(Stream, "Привет!");
    FileSetPos(Stream, 0, 0);
    MsgBox(FileRead(Stream));
    FileSetPos(Stream, 0, 0);
    MsgBox(FileReadLine(Stream));
    MsgBox(FileReadLine(Stream));
    FileSetPos(Stream, 0, 0);
    FileWrite(Stream, ArrayToBinary([65,65,65,65,65,65,65,65,65,65,65,65,65,65])); // пишем побайтово
    FileSetPos(Stream, 0, 0);
    MsgBox(FileReadLine(Stream, 10));
    Exit();

А теперь весь код 8-)


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

if (GlobalObject.WScript && FileExists("%WinDir%\\SysWow64\\wscript.exe") && !/wow64/i.test(WScript.FullName + "")) {
    WshShell.Run("%WinDir%\\SysWow64\\wscript.exe \"" + WScript.FullName + "\"", 1, 0);
    WScript.Quit();
}

var vbs = "\r\n\
Function [_MidB](v, s, l):[_MidB]=MidB(v, s+1, l):End Function\r\n\
Function [_Asc](v):[_Asc]=Asc(v):End Function\r\n\
Function [_Chr](v):[_Chr]=Chr(v):End Function\r\n\
Function [_LenB](v):[_LenB]=LenB(v):End Function\r\n\
Function [_VarType](v):[_VarType]=VarType(v):End Function\r\n\
Function [_TypeName](v):[_TypeName]=TypeName(v):End Function\r\n\
Function [_ChrB](v):[_ChrB]=ChrB(v):End Function\r\n\
Function [_AscB](v):[_AscB]=AscB(v):End Function\r\n\
Function [_LeftB](v, l):[_LeftB]=LeftB(v, l):End Function\r\n\
Function [_RightB](v, l):[_RightB]=RightB(v, l):End Function\r\n\
Function [_InstrB](start, stringtosearch, stringtofind, comparemode):[_InstrB]=InstrB(start, stringtosearch, stringtofind, comparemode):End Function\r\n\
\r\n\
Sub ConvertBinaryToJSArray(arrByteArray, js_arr)\r\n\
Dim i\r\n\
For i = 1 To LenB(arrByteArray)\r\n\
    js_arr.push(AscB(MidB(arrByteArray, i, 1)))\r\n\
Next\r\n\
End Sub\r\n\
\r\n\
Function MultiByteToBinary(MultiByte)\r\n\
  \' 2000 Antonin Foller, http://www.motobit.com\r\n\
  \' MultiByteToBinary converts multibyte string To real binary data (VT_UI1 | VT_ARRAY)\r\n\
  \' Using recordset\r\n\
  Dim RS, LMultiByte, Binary\r\n\
  Const adLongVarBinary = 205\r\n\
  Set RS = CreateObject(\"ADODB.Recordset\")\r\n\
  LMultiByte = LenB(MultiByte)\r\n\
  If LMultiByte>0 Then\r\n\
    RS.Fields.Append \"mBinary\", adLongVarBinary, LMultiByte\r\n\
    RS.Open\r\n\
    RS.AddNew\r\n\
      RS(\"mBinary\").AppendChunk MultiByte & ChrB(0)\r\n\
    RS.Update\r\n\
    Binary = RS(\"mBinary\").GetChunk(LMultiByte)\r\n\
  End If\r\n\
  MultiByteToBinary = Binary\r\n\
End Function\r\n\
    \r\n\
Function RSBinaryToString(xBinary)\r\n\
\'1999 Antonin Foller, Motobit Software\r\n\
\'This function converts binary data (VT_UI1 | VT_ARRAY or MultiByte string)\r\n\
\'to string (BSTR) using ADO recordset\r\n\
\'The fastest way - requires ADODB.Recordset\r\n\
\'Use this function instead of MBBinaryToString if you have ADODB.Recordset installed\r\n\
\'to eliminate problem with PureASP performance\r\n\
\r\n\
Dim Binary\r\n\
\'MultiByte data must be converted to VT_UI1 | VT_ARRAY first.\r\n\
if vartype(xBinary) = 8 then Binary = MultiByteToBinary(xBinary) else Binary = xBinary\r\n\
\r\n\
Dim RS, LBinary\r\n\
Const adLongVarChar = 201\r\n\
Set RS = CreateObject(\"ADODB.Recordset\")\r\n\
LBinary = LenB(Binary)\r\n\
\r\n\
if LBinary>0 then\r\n\
RS.Fields.Append \"mBinary\", adLongVarChar, LBinary\r\n\
RS.Open\r\n\
RS.AddNew\r\n\
RS(\"mBinary\").AppendChunk Binary\r\n\
RS.Update\r\n\
RSBinaryToString = RS(\"mBinary\")\r\n\
Else\r\n\
RSBinaryToString = \"\"\r\n\
End If\r\n\
End Function\r\n\
\r\n\
Sub SAPIFileRead(o, count, js_obj)\r\n\
Dim s, buffer\r\n\
s = o.Read(buffer, count)\r\n\
js_obj.count = s\r\n\
js_obj.buffer = buffer\r\n\
End Sub\r\n\
";

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

function VarType(s)
{
    return VBSEngine.CodeObject._VarType(s);
}

function TypeName(s)
{
    return VBSEngine.CodeObject._TypeName(s);
}

WshShell.CurrentDirectory = GetScriptDir();

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

//@ScriptFullPath Equivalent to @ScriptDir & "\" & @ScriptName 
function GetScriptFullPath() {
    if (GlobalObject.location && GlobalObject.location.href) return location.href.replace(/^.+\/\/\//, "").replace(/\//g, "\\");
    if (GlobalObject.WScript) return WScript.ScriptFullName;
    return "";
}
//MsgBox(GetScriptFullPath());
//Exit();

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

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

//BinaryMid Extracts a number of bytes from a binary variant. Start - 0
function BinaryMid(s, l, c) {
    return VBSEngine.CodeObject._MidB(s, l, c);
}

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

// -1 = not found
function BinaryInBinary(stringtosearch, stringtofind, start, comparemode) {
    if (!start) start = 1;
    else start++;
    return VBSEngine.CodeObject._InstrB(start, stringtosearch, stringtofind, comparemode) - 1;
}
//MsgBox(BinaryInBinary(Binary("123123123"), Binary("123")));

//array of integers (bytes) to ByteArray
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 BinaryToArray(binary) {
    var ar = [];
    VBSEngine.CodeObject.ConvertBinaryToJSArray(binary, ar);
    return ar;
}

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


//FileExists Checks if a file or directory exists.
function FileExists(Path) {
    Path = WshShell.ExpandEnvironmentStrings(Path);
    return FSO.FolderExists(Path) || FSO.FileExists(Path);
}

//FileDelete Delete one or more files. (принудительное удаление. принимает как массивы файлов, так и просто путь) поддерживает удаление по маске.
function FileDelete(Path) {
    if (/Array/i.test(Path.constructor + "")) {
        for (var i = 0, l = Path.length; i < l; i++)
            FileDelete(Path[i]);

        return true;
    }

    Path = WshShell.ExpandEnvironmentStrings(Path);
    try {
        FSO.DeleteFile(Path, true);
        return 1;
    } catch (e) {
        return 0
    }
}

function FileOpen(FilePath, Mode)
{
    var StreamMode = 0;
    
    if (!FilePath) return false;

    if (!Mode) Mode = 0;

    if (Mode & 0) {StreamMode = 0;}
    if (Mode & 1) {StreamMode = 1;}//Write mode (append to end of file)
    if (Mode & 2) {FileDelete(FilePath); FSO.CreateTextFile(FilePath).Close(); StreamMode = 1;}//$FO_OVERWRITE (2) = Write mode (erase previous contents)
    if (Mode & 8) DirCreate(FileGetDirectory(FilePath)); //$FO_CREATEPATH (8) = Create directory structure if it doesn't exist (See Remarks).
    

    var Stream = new ActiveXObject("SAPI.spFileStream")
    Stream.Open(FilePath, StreamMode);
    
    if (Mode & 1) FileSetPos(Stream, 0, 2); //$FO_APPEND (1) = Write mode (append to end of file)
    
    return Stream;
}

//FileRead Read in a number of characters from a previously opened text file.
function FileRead(FileHandle, count, result)
{
    if (!result) result = {};
    if (typeof FileHandle == "string")
    {
        if (!count) count = FileGetSize(FileHandle);
        FileHandle = FileOpen(FileHandle, 0);
    }
    
    result.count = 0;
    result.buffer = "";
    
    if (!count) count = 1024;
    VBSEngine.CodeObject.SAPIFileRead(FileHandle, count, result);
//    MsgBox(result.buffer);
//    MsgBox(result.count);
    return result.buffer;
}

//FileSetPos Sets the current file position.
/*

Origin
Must be one of the following:
    $FILE_BEGIN (0) = Beginning of the file.
    $FILE_CURRENT (1) = Current position.
    $FILE_END (2) = End of the file.
Constants are defined in FileConstants.au3
*/
function FileSetPos(FileHandle, offset, origin)
{
    if (!origin) origin = 0;
    return FileHandle.Seek(offset, origin);
}

//FileGetPos Retrieves the current file position.
function FileGetPos(FileHandle)
{
    return FileSetPos(FileHandle, 0, 1);
}

//FileWrite Append a text/data to the end of a previously opened file.
function FileWrite(FileHandle, data)
{
    if (typeof FileHandle == "string")
    {
        FileHandle = FileOpen(FileHandle, 1);
    }
    
    if (typeof data == "string")
    {
        data = MultiByteToBinary(data);
    }
    
    return FileHandle.Write(data);
}

//FileWriteLine Append a line of text to the end of a previously opened text file.
function FileWriteLine(FileHandle, data)
{
    return FileWrite(FileHandle, data + "\r\n");
}

//FileReadLine Read in a line of text from a previously opened text file.
function FileReadLine(FileHandle)
{
    if (typeof FileHandle == "string")
    {
        FileHandle = FileOpen(FileHandle, 0);
    }

    var result = {count:0, buffer:""}, 
    startPos = FileGetPos(FileHandle), 
    pos = -1, i=0, step = 1024, len=2,
    buffer, CrLf = ArrayToBinary([13,0,10,0]),
    Cr = ArrayToBinary([13,0]);

    while (true){
        FileRead(FileHandle, step, result); 
        pos = BinaryInBinary(result.buffer, CrLf);
        
        if (pos!=-1) {len=4; break};
        pos = BinaryInBinary(result.buffer, Cr);
        if (pos!=-1) {len=2; break};
        if (result.count>4) {FileSetPos(FileHandle, -4, 1); i+=step-4;} else
        i+=step;
        
        if (result.count!=step) break;
    }
    
    var result = "";
    if (pos==0) {FileSetPos(FileHandle, startPos+len, 0); return ""};
    
    FileSetPos(FileHandle, startPos, 0);
    result = FileRead(FileHandle, i+pos);
    FileSetPos(FileHandle, len, 1);
    return result;
}
var Stream = FileOpen(GetScriptFullPath()+".123.txt", 2);
    
    MsgBox(FileGetPos(Stream));
    FileWriteLine(Stream, "Hello!");
    FileWrite(Stream, "Привет!");
    FileSetPos(Stream, 0, 0);
    MsgBox(FileRead(Stream));
    FileSetPos(Stream, 0, 0);
    MsgBox(FileReadLine(Stream));
    MsgBox(FileReadLine(Stream));
    FileSetPos(Stream, 0, 0);
    FileWrite(Stream, ArrayToBinary([65,65,65,65,65,65,65,65,65,65,65,65,65,65]));
    FileSetPos(Stream, 0, 0);
    MsgBox(FileReadLine(Stream, 10));
    Exit();