1

Тема: VBS: пакетное переименование HTML-файлов

С пылу с жару.

Скрипт для переименования HTML/HTM-файлов, также работает в пакетном режиме.
Новое имя файла задаётся на основе тега <title>.

На жутко загруженной машинке (Celeron 2,6Ghz, 1GB ram)
5372 файла обработал  за ~1,5 минуты.


'************************************************************************
'* Name:            Packet_HTML-renamer.vbs                             *
'* Language:        VBScript                                            *
'* commandline:     Packet_HTML-renamer.vbs [file.htm|HTML-dir]         *
'* Description:     Скрипт предназначен для пакетного переименования    *
'*                  HTM/HTML-файлов. Новое имя задаётся на основе       *
'*                  TITLE-заголовка    из содержимого файла.            *
'*                                                                      *
'* Author:          Аскет                                               *
'************************************************************************


On Error Resume Next
set FSO = CreateObject ("scripting.FileSystemObject")

'**************    проверка аргументов ком. строки **************
if Wscript.Arguments.Length > 0 then
 obj = wscript.Arguments.Item(0)
 if FSO.FolderExists (obj) then parsefolder (obj)
 if FSO.FileExists (obj) then parse (obj)
 wsh.quit
end if

'**************    Диалог выбора папки **************
set shellapp = CreateObject ("Shell.Application")
Set objFolder = shellapp.BrowseForFolder(0, "Папка с HTML",16, "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}")
 if not FSO.FolderExists (objFolder.Self.Path) Then wsh.quit :else parsefolder (objFolder.Self.Path)

'**************    Обработка папки **************
SUB parsefolder(folder)
    set HTMLFolder = fso.GetFolder(folder)
    old =  Time()
    For Each Fil In HTMLFolder.Files
        if (LCase(FSO.GetExtensionName (Fil))="html") or (LCase(FSO.GetExtensionName (Fil))="htm") then
            parse (Fil)
        end if
    Next
    msgbox "Начало обработки: [" & old &"]"& vbcr & "Обработка папки закончена. [" & Time() & "]",,"Packet HTML renamer"
END SUB

'**************    парсинг **************
SUB parse(fileName)
    DIM titleflag: titleflag = false
    UTF_charset = false
    set file_ = fso.OpenTextFile (fileName)
    Do While not (file_.AtEndOfLine) or (titleflag)
        str = file_.ReadLine()
        if (instr (str,"charset")>0) And (instr (str,"UTF-8")>0) then UTF_charset= true
        if instr(LCase(str),LCase("<title>"))>0 then            
            titleflag = true
            opentagindex =  instr (LCase(str),"<title>")
            closetagindex =  instr (LCase(str),"</")
            str = replace (str,LCase("</title>"),"")
            str = replace (str,"/"," - ")
            str = replace (str,"\"," - ")
            STR =  mid (str,opentagindex+7)
            STR = Replace (STR,":"," ")
            if (UTF_charset) then STR = UTF8toWin1251(STR)
            file_.close()
            RENAME FileName,STR
            EXIT DO                
        end if
    Loop
END SUB
            
'********* переименование файла **************
SUB RENAME(fileName,STR)    
        indexName=0
        ParentFolder = FSO.GETPARENTFOLDERNAME(fileName) & "\"
        oldName = FSO.GetBaseName (fileName) 
        fileext = "." & FSO.GetExtensionName (fileName)
        NewName = ParentFolder & str
        if fileName = (NewName &  fileext) then exit sub
        if fso.FileExists (NewName &  fileext) then 
            while FSO.FileExists (NewName & "_(" &  indexName & ")" &  fileext)
                indexName = indexName+1
            wend
            NewName = NewName & "_(" & indexName & ")"
        end if        
        FSO.Movefile fileName, NewName &  fileext
end sub

'********* UTF8 -> Win-1251 **************
Function UTF8toWin1251(sIn)
    Set Recode = CreateObject("ADODB.Stream")
    Recode.Open
    Recode.CharSet = "windows-1251"
    Recode.WriteText sIn
    Recode.Position = 0
    Recode.CharSet = "UTF-8"
    UTF8toWin1251 = Recode.ReadText
    Recode.Close
    SET Recode = NOTHING
End Function

2

Re: VBS: пакетное переименование HTML-файлов

Function UTF8toWin1251(sIn)
    Set Recode = CreateObject("ADODB.Stream")
    Recode.Open
    Recode.CharSet = "windows-1251"
    Recode.WriteText sIn
    Recode.Position = 0
    Recode.CharSet = "UTF-8"
    UTF8toWin1251 = Recode.ReadText

Вы открываете поток, пишете в него в кодировке windows-1251, потом читаете уже в кодировке UTF-8, но функция называется UTF8toWin1251?

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

3

Re: VBS: пакетное переименование HTML-файлов

Rumata пишет:

но функция называется UTF8toWin1251?

Ой, вы не поверите...

p.s. Я догадываюсь к чему вы клоните, но постарайтесь задать вопрос более яснее.

4 (изменено: Rumata, 2011-02-25 14:40:11)

Re: VBS: пакетное переименование HTML-файлов

ну ладно. Тогда будет много слов.
1. Функция называется UTF8toWin1251, то есть она выполняет перевод UTF8 -> Win1251
2.1. Для этого создается поток, устанавливается кодировка Win1251
2.2. пишется в поток строка в UTF8
3.1. перключается кодировка на UTF8
3.2. считывается строка в кодировке Win1251

Мне непонятно именно как 2.1. и 2.2 соотносятся друг с другом. Почему строка UTF8 пишется в поток с выставленной кодировкой Win1251. Я уже не первый раз запинаюсь об этот ADODB.Stream и кажется на одном и том же месте. Кажется, логика нарушена.

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

5

Re: VBS: пакетное переименование HTML-файлов

Rumata пишет:

Кажется, логика нарушена.

Больше похоже что вы дотошно пытаетесь найти изъян.

Проследите построчное выполнение скрипта, поэкспериментируйте с файлами в разных кодировках, быть может поймёте что к чему.
Или всё недоумение из-за названия функции?


Rumata пишет:

Почему строка UTF8 пишется в поток с выставленной кодировкой Win1251

Да потому что, это не UTF8, а Windows-1251 с крокозябрами.

6

Re: VBS: пакетное переименование HTML-файлов

Аскет пишет:

Больше похоже что вы дотошно пытаетесь найти изъян.

Аскет, закомментировав:

On Error Resume Next

Вы и сами найдёте.

7

Re: VBS: пакетное переименование HTML-файлов

alexii пишет:

Аскет, закомментировав:

On Error Resume Next

Гениально! Чтож вы раньше-то молчали?

Если какой-то оператор присутствует в скрипте, значит он для чего-то нужен, не так-ли?

А вообще оно для подавления ошибки BrowseForFolder (при нажатии "отмена").

Или предложите свой вариант решения для подавления оной?

8 (изменено: Dmitrii, 2011-02-25 17:48:05)

Re: VBS: пакетное переименование HTML-файлов

Аскет пишет:

... оно для подавления ошибки BrowseForFolder (при нажатии "отмена").
Или предложите свой вариант решения для подавления оной?

Элементарно:

Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.BrowseForFolder(0, "Выбор каталога", &h10 + &h200, &h11)
If Not objFolder Is Nothing Then
    WScript.Echo objFolder.Self.Path
Else
    WScript.Echo "Каталог не выбран."
End If

OFF:
----
Аскет, хорошо, что Вы деятельно участвуете в работе форума, но жаль, что столь неконструктивно реагируете на критику Ваших решений.

9

Re: VBS: пакетное переименование HTML-файлов

Dmitrii пишет:
If Not objFolder Is Nothing

А оно вот как просто оказалось.

Рядом ведь был. Я пытался реализовать подобное, через IsObject но objFolder  в любом случае оказывался объектом:

if Not IsObject (objFolder) then wscript.quit

10

Re: VBS: пакетное переименование HTML-файлов

Аскет пишет:

Гениально! Чтож вы раньше-то молчали?

Я полагал, что Вы начали-таки читать «Правила форума» — §3.10:

Если Вы публикуете ссылку на свою разработку или публикуете код своего скрипта, объясните внятно, зачем нужна эта разработка или скрипт, как это запустить и как это использовать. В противном случае Ваша публикация будет абсолютно бессмысленной. Помните, что скачивать Ваш файл или запускать Ваш скрипт из чисто спортивного любопытства и разгадывать ребус о том, что же с этим можно сделать, будут 0,01% посетителей. Если посетителей оказалось всего 1000 (тысяча) человек, то 0,01% из них - это 0 (ноль) посетителей.

Видно, что я ошибся.

Аскет пишет:

Если какой-то оператор присутствует в скрипте, значит он для чего-то нужен, не так-ли? smile

Ага. В теории. На практике же подобное использование «On Error Resume Next» в начале скрипта без какой-либо последующей обработки ошибок означает, что мы можем с большой вероятностью огрести в итоге кучу проблем с таким подходом.

В MSDN пример с «BrowseForFolder()» использует такой же, приведённый коллегой Dmitrii, способ — «Not … Is Nothing». И на нашем форуме ранее встречалась сия конструкция. Поскольку Вы не привели комментариев (см. выше), каким образом я должен был догадаться о столь странном предназначении «On Error Resume Next»?!

Шут с ним, использовали Вы его для метода «.BrowseForFolder()» — и ладно. Вопрос в другом. Почему включаете задолго до вызова метода? Почему не проверяете наличие ошибки и не обрабатываете её? Почему не возвращаете стандартную обработку ошибок на место после вызова метода и обработки? Закомментируйте «On Error Resume Next» и подивитесь на творение рук своих.

Аскет пишет:

Я пытался реализовать подобное, через IsObject но objFolder  в любом случае оказывался объектом:

Всё верно. Наличие «Set» в операторе присваивания означает, что будет создан объект.

WScript.Echo TypeName(Nothing)
WScript.Echo VarType(Nothing), vbObject
Nothing
9 9

11

Re: VBS: пакетное переименование HTML-файлов

alexii пишет:

Если Вы публикуете ссылку на свою разработку или публикуете код своего скрипта, объясните внятно, зачем нужна эта разработка или скрипт, как это запустить и как это использовать.

Это вы типа как бы намекаете комментировать каждую строчку?

Думаю скрипт достаточно откомментирован и не нуждается в дополнительных объяснениях каждой строки.

alexii пишет:

Закомментируйте «On Error Resume Next» и подивитесь на творение рук своих.

Если вы опять же на что-то намекаете, то увы, отрабатывает корректно.  (Если не запускать его без дела и жать отмену)
Скрипт изначально вообще был без строки "On Error Resume Next". Добавлена как раз для таких случаев отмены выбора папки.

А вы его вообще тестировали на работоспособность?

Возьмите распакованный hxs/chm-документ, например из msdn (для чего в принципе скрипт и писался),  файлы там имеют имена типа "09c6bc12-25fd-4359-a5fc-8dab8dddbfd2.htm"
да натравите папку "html" на скрипт (параметром запуска или же выбором в BrowseForFolder) а потом уже по факту кашляйте на "творение рук моих".

12

Re: VBS: пакетное переименование HTML-файлов

Аскет пишет:

Это вы типа как бы намекаете комментировать каждую строчку?

Я говорю о том, что мне непонятно, чего Вы хотели, выкладывая Вашу разработку. Об этом Вы не написали в сопроводиловке.

Аскет пишет:

Думаю скрипт достаточно откомментирован…

Раз возникли вопросы (и по поводу «On Error Resume Next» в частности) — очевидно, недостаточно.

Аскет пишет:

Если вы опять же на что-то намекаете, то увы, отрабатывает корректно… А вы его вообще тестировали на работоспособность? Возьмите распакованный hxs/chm-документ … да натравите папку "html" на скрипт … а потом уже по факту кашляйте на "творение рук моих".

Пожалуйста.

Комментируем «On Error Resume Next». Запускаем скрипт. Выбираем папку с файлом *.htm. Получаем:

E:\Песочница\0036\0001.vbs(80, 9) Ошибка выполнения Microsoft VBScript: Недопустимое имя или номер
файла

Добавляю перед:

        FSO.Movefile fileName, NewName &  fileext

строку:

        WScript.Echo fileName, NewName &  fileext

Вижу:

author-patterns.htm \XPath Tutorial Application< - TITLE>.htm

Согласно стандарту HTML регистр символов в тэге «title» может быть произвольным. Браузер это понимает. Ваш скрипт:

            str = replace (str,LCase("</title>"),"")

— нет.

Кашляю дальше. Ваш скрипт исправляет некоторые недопустимые символы «/», «\», «:» из формируемого заголовка. MSDN говорит о несколько большем числе: «/», «\», «:», «*», «#», «?», «"», «<», «>» и «|». Далеко ходить не надо — в том же упоминаемом MSDN большое число страниц с такими символами в тэге «title»:

<title>KB243298 - BUG: A "C2668: 'InlineIsEqualGUID'" error occurs when you try to build a default ATL project that contains a COM object in Visual C++</title>
<title>KB244232 - How To Add Context Help Button (? Button) to Title Bar of CPropertySheet</title>
<title>KB242527 - PRB: #import Wrapper Methods May Cause Access Violation</title>

Продолжать? Потрясающе в скрипте выглядит перебор строк файла — вплоть до конца строки:

Do While not (file_.AtEndOfLine) …

Обычно ограничение — конец файла.

Двигаемся дальше. Стандарт HTML предусматривает, что веб-страницы могут быть не только в ANSI и UTF-8, но и в других кодировках. «По факту» — Ваш скрипт об этом знать не знает и переименовывает такие страницы как есть — то бишь, в наличествующей кодировке. Например:

<meta http-equiv="Content-Type" content="text/html; charset=koi8-r">
<title>Серый форум / AHK: Помогите создать скрипт</title>
E:\Песочница\0036\уЕТЩК ЖПТХН  -  AHK  рПНПЗЙФЕ УПЪДБФШ УЛТЙРФ.htm

А как насчёт того, что тэг «tile» не обязан располагаться на одной физической строке? Об этом скрипт тоже не в курсе. Итог — такое имя файла после работы скрипта:

.htm

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

Про использование недокументированных возможностей, типа «wsh.quit» вместо «WScript.Quit», полуописанные переменные (часть описана, часть нет), разный регистр в одних и тех же ключевых словах и общий внешний вид скрипта говорить после такого уже не хочется, хотя и это является необходимым требованием для помещения скрипта в Коллекцию.

Я бы вместо TextStream работал непосредственно c DOM или DHTML — сразу уйдут многие ошибки с некорректным определением «title». Наподобие:

Option Explicit

Dim objHTMLDocument
Dim objRegExp

Dim strTitle


Set objHTMLDocument = GetDocumentFromURL("file://E:\Песочница\0036\qww.htm")
Set objRegExp       = WScript.CreateObject("VBScript.RegExp")

strTitle = objHTMLDocument.title

With objRegExp
    .IgnoreCase = True
    .Global     = True
    .Pattern    = "[/|\\|:|\*|#|\?|""|<|>|\|]"
    
    WScript.Echo .Replace(strTitle, "_")
End With

Set objRegExp       = Nothing
Set objHTMLDocument = Nothing

WScript.Quit 0
'=============================================================================

'=============================================================================
'Set objHTMLDocument = DocumentFromURL(url)
' http://forum.script-coding.com/viewtopic.php?pid=34580#p34580
' http://forum.script-coding.com/viewtopic.php?pid=7920#p7920
Function GetDocumentFromURL(strURL)
    Dim objHTMLDocument
    Dim arrHtmlText
    
    Set objHTMLDocument = WScript.CreateObject("HTMLFile")
    
    With WScript.CreateObject("MSXML2.XMLHTTP")
        .open "GET", strURL, False
        .send
        arrHtmlText = .responseBody
    End With
    
    With WScript.CreateObject("ADODB.Stream")
        .Type = 1
        .Open
        .Write arrHtmlText
        
        .Position = 0
        .Type = 2
        .Charset = "windows-1251"
        
        objHTMLDocument.write .ReadText
    End With
    
    Set GetDocumentFromURL = objHTMLDocument
    Set objHTMLDocument = Nothing
End Function
'=============================================================================
…
<title>
Серый форум / AHK: Помогите 
создать скрипт =/=\=:=\=*=#=?="=<=>=|=
</title>
…
Серый форум _ AHK_ Помогите создать скрипт =_=_=_=_=_=_=_=_=_=_=_=

13 (изменено: Аскет, 2011-02-26 02:58:29)

Re: VBS: пакетное переименование HTML-файлов

alexii пишет:

author-patterns.htm \XPath Tutorial Application< - TITLE>.htm

Это вы где-то явно намудрили, слэш в любом случае заменяется, перепроверил.

А вот угловые скобки и остальные спец. символы, да, стоит добавить в список замены.

alexii пишет:

Согласно стандарту HTML регистр символов в тэге «title» может быть произвольным. Браузер это понимает. Ваш скрипт:
           

 str = replace (str,LCase("</title>"),"")

— нет.

Упоминание LCase на это и расчитано, но с позицией напутал. Вот так будет правильно:

str = replace (LCase(str),"</title>","")

...но так всё преобразует в нижний регистр. (

Так что Regexp самое подходящее.

.Pattern    = "[/|\\|:|\*|#|\?|""|<|>|\|]"
alexii пишет:

MSDN говорит о несколько большем числе: «/», «\», «:», «*», «#», «?», «"», «<», «>» и «|»

Не верю.
У вас наверно msdn пиратский, с ошибками.

Решётка тут не при делах. Да и квадратные скобки ни к чему.
Запрещённых символов всего 9 (X:/путь\, "?маска файлов.*", операторы перенаправления).

Do While not (file_.AtEndOfLine)

Вообще там должен был быть и имелось ввиду AtEndOfStream, но видимо на скорую руку напутал. Но работоспособность от этого не меняется.

koi8-r
Кодировка малоиспользуемая, да и msdn на ней не писан. Поэтому небыло необходимости добавлять. А добавить функцию дело не хитрое.

Идём далее.

Я бы вместо TextStream работал непосредственно c DOM или DHTML — сразу уйдут многие ошибки с некорректным определением «title».
MSXML2.XMLHTTP, HTMLfile

Ну не знаю-не знаю, тут уж встаёт вопрос оптимизации. Надо будет сравнить "что быстрее".

14

Re: VBS: пакетное переименование HTML-файлов

Ах да, и ещё.

alexii пишет:

Про использование недокументированных возможностей, типа «wsh.quit» вместо «WScript.Quit», полуописанные переменные (часть описана, часть нет)

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

Во вторых: По поводу "часть описана, часть нет". Спецификация языка VBScript НЕ предполагает обязательного объявления переменных, до их использования, если это явно не указано интерпретатору.

Я вообще считаю что подобное растягивание кода dim'ами и формальными инструкциями (там где это явно излишнее) только затрудняет его понимание, и отводит взгляд от основных инструкций.

p.s. на заметку: «wsh» это синоним объекта «Wscript».

15

Re: VBS: пакетное переименование HTML-файлов

Аскет пишет:

... Не верю. У вас наверно msdn пиратский, с ошибками...

Загляните сюда:
http://msdn.microsoft.com/ru-ru/library/ms163853.aspx

Аскет пишет:

... квадратные скобки ни к чему...

Это служебные символы, использующиеся при определении шаблона регулярного выражения.

OFF:
-----

Аскет пишет:

... считаю что подобное растягивание кода dim'ами и формальными инструкциями (там где это явно излишнее) только затрудняет его понимание, и отводит взгляд от основных инструкций...

Размещение блока описания переменных и констант в заголовках (сценария, процедур, функций, функциональных блоков) ничуть не затрудняет, а повышает "читабельность" кода. Затруднения создаёт как раз нерегулярность в соблюдении данного правила. То же самое можно сказать и по поводу единообразия в именовании объектов сценария.

16

Re: VBS: пакетное переименование HTML-файлов

Dmitrii пишет:

Загляните сюда:
http://msdn.microsoft.com/ru-ru/library/ms163853.aspx

Неудачная ссылка - это справедливо для SQL Server 2008 R2, что в принципе и вытекает из категории документации (слева) и подзаголовка статьи.

----

Для добивки темы о запрещённых и разрешённых символах. Далеко ходить и рыться по документациям не надо.

1) в проводнике создаём файл с именем #%«'»&^.ext

2) там же пытаемся переименовать файл в ".ext и получаем подсказку с ясным списком запрещённых символов.

17

Re: VBS: пакетное переименование HTML-файлов

Аскет пишет:

Неудачная ссылка...

Согласен. Приношу извинения.