Замена кусков текста в выделенных файлах

Обсуждение вопросов автоматизации Total Commander с помощью сторонних программ и скриптовых технологий.

Модераторы: Avada, CaptainFlint, Nik, d-view, Моторокер

Аватара пользователя
Batya
Сообщения: 2236
Зарегистрирован: Ср дек 15, 2004 09:51
Откуда: Москва, Россия

Сообщение Batya »

Flasher писал(а):Речь о формировании временных файлов с текстом поиска/замены (в папке %TEMP%, по всей видимости). Или если какой-то более изощрённый способ?
Текст также, как и у Loopback, будет грузиться в память. Вопрос по объёму файла актуален и для того варианта, который собираюсь реализовать я.

Добавлено.
Вообщем-то, тут особо и писать нечего.
Всё в том же скрипте от Volny после строки

Код: Выделить всё

Find_String = InputBox("Введите искомую строку", "Поиск и замена")
добавь

Код: Выделить всё

Find_String = Replace(Find_String, "\n", vbNewLine)
И после строки

Код: Выделить всё

Replace_String = InputBox("Введите строку для замены", "Поиск и замена")
добавь

Код: Выделить всё

Replace_String = Replace(Replace_String, "\n", vbNewLine)
Нет, я не сплю. Я просто медленно моргаю.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher
Тут есть два пути: или отказаться от библиотек, заменив константы из них значениями (что не очень удобно), или скомпилировать скрипт не в экзашник, а в байт-код. Получится один файл, с расширением a3x, который можно запускать как обычный au3, но который уже будет включать все необходимые библиотеки. Для этого в утилите Aut2exe нужно выбрать тип для Destination как "Encoded script files".

И кстати, тут я немного поспешил с библиотеками - можно смело оставить только GUIConstantsEx.au3.
Последний раз редактировалось Loopback Вт окт 05, 2010 13:13, всего редактировалось 1 раз.
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

CaptainFlint писал(а):Я как раз и подразумевал, что программа может обрабатывать лишь по одному файлу за раз, тогда lst2multi и приведёт к многократному вызову утилиты для каждого файла в отдельности.
Скорее всего Вахмурка подразумевал утилиту, где задаётся список либо путь к файлам, и тут вовсе не имелся в виду редактор MDI/SDI, с которым lst2mlt и lst2str умеют работать. Ну а многократный вызов SDI-приложения вообще не вариант..
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

Batya
Понял, сделаю. Спасибо!
Насчёт рекурсии какие там дела?
Loopback писал(а):скомпилировать скрипт не в экзашник, а в байт-код. Получится один файл, с расширением a3x, который можно запускать как обычный au3, но который уже будет включать все необходимые библиотеки
А ведь точно. Изображение
Loopback писал(а):И кстати, тут я немного поспешил с библиотеками - можно смело оставить только GUIConstantsEx.au3
Замечательно! Всё свелось к 2 Кб.Изображение
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

Loopback
Я вот ещё о чём подумал. Бывает, что надо заменить несколько разных кусков, разбросанных в разных местах кода/текста на один единственный. Вот хотелось бы (если это возможно) разбивать левое окно на заданное в параметрах кол-во частей ( можно ограничить до 6-8 ), в каждое поле которых будет заносится свой кусок текста. Есть такая возможность?
Аватара пользователя
Batya
Сообщения: 2236
Зарегистрирован: Ср дек 15, 2004 09:51
Откуда: Москва, Россия

Сообщение Batya »

Flasher писал(а):Насчёт рекурсии какие там дела?
Замени процедуру DoItForThisFile на:

Код: Выделить всё

Sub DoItForThisFile(FilePath)
   If FSO.FileExists(FilePath) Then
     Set F = FSO.OpenTextFile(FilePath, ForReading)
     Buffer = F.ReadAll
     F.Close
   
     Buffer = Replace(CStr(Buffer), Find_String, Replace_String, 1, -1, 1)
     FSO.CopyFile FilePath, FilePath & ".bak"
   
     Set F = FSO.OpenTextFile(FilePath, ForWriting)
     F.Write Buffer
     F.Close
   End If
   If FSO.FolderExists(FilePath) Then DoItForThisFolder(FilePath)
End Sub

Sub DoItForThisFolder(FolderPath)
   For Each F In FSO.GetFolder(FolderPath).SubFolders
     DoItForThisFolder(F)
   Next
   For Each F In FSO.GetFolder(FolderPath).Files
     DoItForThisFile(F)
   Next
End Sub
Нет, я не сплю. Я просто медленно моргаю.
Аватара пользователя
Rodny
Сообщения: 949
Зарегистрирован: Ср янв 24, 2007 00:59
Откуда: Могилёв, Беларусь

Сообщение Rodny »

А можно я со своим реквестом (но в рамках темы) влезу? :oops:

У меня частный случай: два INI в кодировке UTF-8. Можно ли заданную секцию (целиком) из первого файла скопировать во второй, заменив имеющуюся? Можно упростить задачу: в первом файле только эта одна секция.
Пути файлов и названия секций либо в самом скрипте, либо в параметрах кнопки, непринципиально.
Т.е. никакого интерфейса не требуется, в один клик.

P.S. Оно может уже есть, но вроде не припоминаю, ткните тогда носом, если у кого ссылка под рукой.
P.P.S. Поскольку с AutoIt'ом сталкиваться ещё не доводилось, ставить/компилировать несколько напряжно, если можно будет обойтись vbs, это предпочтительнее.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher
Ну вот примерно так.

Код: Выделить всё

#include <GUIConstantsEx.au3>

If $CmdLine[0] < 1 Then 
    MsgBox (0, "", "Недостаточно параметров, должен быть 1.") 
    Exit 
EndIf

Global $aList  = _TCHLoadFileList($CmdLine[1])
Global $gReplaces = 0, $gMask

$hGUI = GUICreate("Поиск и замена", 563, 260, -1, -1)
$edSrc = GUICtrlCreateEdit("", 8, 72, 265, 145)
$edTrg = GUICtrlCreateEdit("", 288, 72, 265, 145)
$btnOK = GUICtrlCreateButton("OK", 400, 224, 75, 25)
$btnCancel = GUICtrlCreateButton("Отмена", 480, 224, 75, 25)
GUICtrlCreateLabel("Текст для поиска", 8, 54, 94, 17)
GUICtrlCreateLabel("Текст для замены", 288, 54, 98, 17)
$cbCaseSence = GUICtrlCreateCheckbox("Учитывать регистр", 8, 232, 137, 17)
$edMask = GUICtrlCreateInput("*.*", 8, 24, 265, 21)
GUICtrlCreateLabel("Маска файлов", 8, 6, 78, 17)
GUISetState(@SW_SHOW)

If $aList[0] = 0 Then GUICtrlSetState($btnOK, $GUI_DISABLE)

While 1
	$nMsg = GUIGetMsg()
	Switch $nMsg
		Case $GUI_EVENT_CLOSE, $btnCancel
			Exit
        Case $btnOK
            $gMask = _MaskToRegexp(GUICtrlRead($edMask))
            For $i = 1 To $aList[0]
                If StringRight($aList[$i], 1) = "" Then 
                    _DoFolder($aList[$i])
                Else
                    _DoFile($aList[$i])
                EndIf
            Next
            MsgBox (0, "", "Выполнено замен: " & $gReplaces, 0, $hGUI)
            Exit
	EndSwitch
WEnd

Func _DoFolder($sFolder)
    If $sFolder = "" Then Return
    Local $sDirs = ""
    Local $hSearch = FileFindFirstFile($sFolder & "*.*")
    If @error Then Return
    While 1
        $sFile = FileFindNextFile($hSearch)
        If @error Then ExitLoop
        If @extended Then 
            $sDirs &= $sFolder & $sFile & "" & @LF
            ContinueLoop
        EndIf
        _DoFile($sFolder & $sFile)
    Wend
    FileClose($hSearch)
    ; Обработку каталогов приходится делать отдельно,
    ; чтобы не оставлять открытый поисковый хэндл,
    ; их максимально может быть 64
    $aDirs = StringSplit($sDirs, @LF)
    For $i = 1 To $aDirs[0]
        _DoFolder($aDirs[$i])
    Next
EndFunc

Func _DoFile($sFile)
    If StringRegExp($sFile, $gMask) = 0 Then Return
    Local $sSrcText = FileRead($sFile)
    Local $sTrgText = StringReplace($sSrcText, GUICtrlRead($edSrc), GUICtrlRead($edTrg), 0, _
                                    BitAND(GUICtrlRead($cbCaseSence), $GUI_CHECKED))
    If @extended Then
        $gReplaces += @extended
        Local $hFile = FileOpen($sFile, 2)
        FileWrite($hFile, $sTrgText)
        FileClose($hFile)
    EndIf
EndFunc

Func _MaskToRegexp($sMask)
    $sMask = StringRegExpReplace($sMask, "(\\|\.|\+|\^|\(|\)|\[|\]|\{|\}|\$)", "\\\1")
    $sMask = StringReplace($sMask, "?", ".")
    $sMask = StringReplace($sMask, "*", ".+?")
    Return("(?i)^" & $sMask & "$")
EndFunc

Func _TCHLoadFileList($sFileName)
    Local $asList[1] = [0]
    Local $hfList = FileOpen($sFileName, 0)
    While 1
        $sLine = FileReadLine($hfList)
        If @error Then Exitloop
        ReDim $asList[UBound($asList)+1]
        $asList[UBound($asList)-1] = $sLine
    Wend
    FileClose($hfList)
    $asList[0] = UBound($asList)-1
    Return $asList
EndFunc
Flasher писал(а):Я вот ещё о чём подумал. Бывает, что надо заменить несколько разных кусков, разбросанных в разных местах кода/текста на один единственный. Вот хотелось бы (если это возможно) разбивать левое окно на заданное в параметрах кол-во частей ( можно ограничить до 6-8 ), в каждое поле которых будет заносится свой кусок текста. Есть такая возможность?
Да в принципе всё возможно. Только "окно на части" - это как? Тогда уж проще динамически сделать несколько табов с полями ввода. Ну или просто не закрывать окно после выполнения и менять код в первом поле.
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

Batya
Я чего-то ничего не пойму. Ты предлагал сделать самому замену переносов строки на /n? Ведь в скрипте всё осталось по старому по части окон. Если я заношу в буфер кусок текста, то скрипт с учётом неактивности не сможет преобразовать этот кусок в строку, а значит я смогу вставить только первую строчку и то при условии, если она не будет начинаться с переноса, пробела или табуляции, а это очень нехорошо..
Rodny писал(а):Поскольку с AutoIt'ом сталкиваться ещё не доводилось, ставить/компилировать несколько напряжно
Exe-шник первого скрипта устроит?
Loopback писал(а):Да в принципе всё возможно. Только "окно на части" - это как? Тогда уж проще динамически сделать несколько табов с полями ввода. Ну или просто не закрывать окно после выполнения и менять код в первом поле.
"Табов" в каком смысле? Типа вкладок или всё-таки таблицы? Последний вариант самый простой, но хотелось бы сразу вставить все куски и выполнить по одному клику, пусть и за несколько проходов. :)
Аватара пользователя
Batya
Сообщения: 2236
Зарегистрирован: Ср дек 15, 2004 09:51
Откуда: Москва, Россия

Сообщение Batya »

Flasher писал(а):Я чего-то ничего не пойму. Ты предлагал сделать самому замену переносов строки на /n?
Да :)

Ну, если ты так хочешь, можешь вставить в начало скрипта автоматическое преобразование буфера:

Код: Выделить всё

With CreateObject("TCScript.Helper")
  .SetTextToClip(Replace(.GetTextFromClip, vbNewLine, "\n"))
End With
Нет, я не сплю. Я просто медленно моргаю.
Аватара пользователя
Lev
Сообщения: 392
Зарегистрирован: Ср мар 02, 2005 16:34

Сообщение Lev »

Flasher писал(а):Поправьте, если я ошибаюсь, в VBS это не достижимо.
Самые обычные регексп замены с ключами Multiline и Global.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher писал(а):"Табов" в каком смысле? Типа вкладок или всё-таки таблицы? Последний вариант самый простой, но хотелось бы сразу вставить все куски и выполнить по одному клику, пусть и за несколько проходов.
Ну вкладки, да.
Можно, конечно, создать колонку из полей ввода, но интерфейс сильно вниз растянется, коряво как-то.
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

Batya писал(а):Да :)
Долго, нужно не один такой кусок к замене представлять.
Batya писал(а):Ну, если ты так хочешь, можешь вставить в начало скрипта автоматическое преобразование буфера:
Так, окей, уже лучше, а что с табуляцией делать?

Lev
Дык, по-моему, тоже со строками только работает. :?:

Loopback
А табличный вариант не получится? С ним быстрей просто.
Аватара пользователя
Rodny
Сообщения: 949
Зарегистрирован: Ср янв 24, 2007 00:59
Откуда: Могилёв, Беларусь

Сообщение Rodny »

Flasher писал(а):Exe-шник первого скрипта устроит?
У меня проблема в том, что я точно знаю только название секции, т. е. я не могу здесь задать строки для поиска.
По идее, должны же быть какие-нибудь ini-специфические функции.

Гы, подумал, как может называться такая функция, вбил в поиск "WriteIniSection". Вы не поверите... :lol:
Попробую что-нибудь сам сочинить на досуге.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher писал(а):А табличный вариант не получится? С ним быстрей просто.
Если имеется в виду таблица типа экселевской - то в WinAPI такой нет. SysListView, если мне не изменяет память, тоже не поддерживает многострочный текст.
Так что либо так, либо этак, либо еще как-нибудь :).
Ответить