Добавление текста до/после строк в выделенных файл-списках

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

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

Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Добавление текста до/после строк в выделенных файл-списках

Сообщение Flasher »

Давно появилась задача по редактированию строк в файл-списках.
Вот что примерно хотелось бы видеть:

Для AutoIt: одно окно с восьмью полями:
Маска файлов [__]
_________________Вставка текста________________
Слева от строки__[__]___начинающейся с_______[__]*
Справа от строки_[__]___заканчивающейся на___[__]*
Над строкой_____[__]___начинающейся с_______[__]*
Под строкой._____[__]__заканчивающейся на____[__]*
* Если какие-то поля справа пустуют, а слева нет, то обрабатываются все строки по заданным условиям в левых полях.

Для VBS: 4 окна, которые вызываются с учётом заданных через пробел параметров.
1 - вызывает окно "Вставьте текст слева от строки"
2 - вызывает окно "Вставьте текст справа от строки"
3 - вызывает окно "Вставьте текст над строкой"
4 - вызывает окно "Вставьте текст под строкой"
Необходимо комбинировать(1 2 | 2 4 | 1 3 4 и т.д.).


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

P.S. C vbs я и сам могу поковыряться, просто у меня это времени поболе займёт.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Честно говоря вообще не понял логики, как что должно работать. Например:

Слева от строки__[__]___начинающейся с_______[__]*
Справа от строки_[__]___заканчивающейся на___[__]*

Что представляет собой значение "Слева от строки"? Номер строки?
Или если это текст строки, зачем нужно "начинающейся с"?

Да и вообще, как тогда понимать "над" и "под" строкой? В этом случае, получается, строку надо брать целиком.
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

Loopback писал(а):Что представляет собой значение "Слева от строки"? Номер строки?
Вставка текста слева от любой строки в списке.
Пример:
1 строка: 2222357
2 строка: 4543638

Добавить слева:
блабла
блаблабла
блабла

Результат:
блабла
блаблабла
блабла2222357
блабла
блаблабла
блабла4543638
Loopback писал(а):Или если это текст строки, зачем нужно "начинающейся с"?
Это нужно в случае, если требуется фильтр строк. Т.е. выборочное добавление.
Loopback писал(а):Да и вообще, как тогда понимать "над" и "под" строкой?
Над строкой - на предыдущей строке(ах), под строкой - начиная со следующей строки.
результат вставки над строкой:
блабла
блаблабла
блабла
2222357
блабла
блаблабла
блабла
4543638
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher писал(а):Вставка текста слева от любой строки в списке.
Эээ. Если любой, то нафиг тут тогда поле ввода? Наверно достаточно фильтра? Т.е.

Слева от строки, начинающейся с_______[__]*

Если поле пустое - любая строка.

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

Сообщение Flasher »

Loopback писал(а):Если любой, то нафиг тут тогда поле ввода?
Что значит "нафиг"? Это самое важное. Текст туда вообще-то должен вводится, который вставлять надо. :)
Loopback писал(а):Наверно достаточно фильтра? Т.е.
Я же поясняю, что фильтр нужен для выбора тех строк, около которых будет вводится текст, вставленный в поле слева.
Loopback писал(а):Если поле пустое - любая строка.
Это условие я написал в первом посте, логично. :)
Loopback писал(а):Нужно ли плодить сущности в виде лишних полей?
Так тут комбинация важна. Вставка у одних строк с фильтром будет спереди/сзади, у других над/под. Плюс ко всему - это удобней, чем приписывать переносы или, допустим, ставить флаги с длинными названиями, которые тоже будут место занимать.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher писал(а): Текст туда вообще-то должен вводится, который вставлять надо.
Ну ты и пишешь :roll: Как это можно было понять из текста? В жизни бы не догадался. Я полагал "Вставка текста" - это текст, на который меняем, а всё остальное - условия, в этом случае первые пункты за условие ну никак не катили.
Flasher писал(а):Я же поясняю, что фильтр нужен
С фильтром как раз всё было понятно. Впрочем, вышесказанное всё расставило на места.

Ок, посмотрю вечером.
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

Loopback писал(а):Ну ты и пишешь Rolling Eyes Как это можно было понять из текста?
Смотря из какого. :wink: Там написано "Вставка текста", а далее ветви(слева.., справа.., над.., под..). Что'ж ещё в поля-то вводить? :)
Loopback писал(а):Я полагал "Вставка текста" - это текст, на который меняем
Ну про "менять" я нигде не писал, лишь "вставлять". Ты имел в виду, что текст один единственный? :) Если да, то для него бы я отобразил отдельное поле, которое и могло бы привести в замешательство, а так там его нет. :)
Loopback писал(а):Ок, посмотрю вечером.
Спасибо!
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher
Вечером не вышло.

Кстати, как обрабатывать ситуацию, когда заполнены, скажем, первое и третье поле с текстом? Допустим, скрипт обработал первое поле, добавил слева текст. Далее есть еще условие добавления перед. Добавляем перед строкой с уже добавленным слева текстом? Но если при этом задано условие выбора строки - как быть? После добавления текста, по-сути, начало строки меняется.

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

Сообщение Flasher »

Loopback писал(а):Но если при этом задано условие выбора строки - как быть? После добавления текста, по-сути, начало строки меняется.
Я даже удивился, почему ты вчера сразу об этом не спросил.) Если задано условие выбора, то обработка идёт вкупе с остальными строками. Совпадения могут быть при условии, что юзер просто не знаком с синтаксисом списков, что было бы ОЧЕНЬ странно. Если условие выбора на 1/3 или 2/4 строки не задано одновременно, то в порядке приоритета обрабатываться должны только 1,2 строки(слева, справа), а потом по окончанию 3/4(над, под).
Loopback писал(а):все условия выбора строки делать только по исходной строке.
Секундочку. По какой исходной? Там всего 4 поля с добавляющимся текстом для каждого отдельного случая. Никакого общего текста для всех 4-х вариантов быть не должно.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Flasher писал(а):Если условие выбора на 1/3 или 2/4 строки не задано одновременно
Наверно, имелось в виду все-таки "задано одновременно"?

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

Сообщение Flasher »

Loopback писал(а):Наверно, имелось в виду все-таки "задано одновременно"?
Нет-нет-нет, именно с частицей "не", т.е. если правые поля 1/3, 2/4 пустуют. Ведь в таком случае будут обрабатываться все строки без фильтра кроме пустых, конечно.
Loopback писал(а):Ну вот прочитана строка из файла - все фильтры сравниваются с ней, назависимо от того, добавило ли предыдущее условие к ней текст.
Ааа, в этом смысле. Т.е. не каждый раз прочитывать, а сразу для всех 4-х случаев? Тогда, конечно, ты прав. Только в этом случае я бы хотел добавить флаги с приоритетами. Т.е., если условия справа одинаковые, то приоритет позволит добавлять, например, сперва по "над" перед "слева" или "под" перед "справа".
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Так, что-то туго дело идет... :?

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

Сообщение Flasher »

Loopback писал(а):Так, что-то туго дело идет.
Ничего, уж ради такой важной задачи можно и потерпеть. :wink:
Loopback писал(а):Имеется в виду тот файл-список, который создает тотал по %L?
В названии темы написано "в выделенных файл-списках". В выделенных папках тоже было бы не плохо файлы по маске редактировать. Списки это или любые другие некодированные файлы(ini, htm(l) , xml, php, txt, lst, cfg, bar, lng, mnu и т.д.)- не суть важно, главное, чтобы алгоритм работал исходя из условий задачи.
Loopback
Сообщения: 1646
Зарегистрирован: Пн сен 07, 2009 09:29

Сообщение Loopback »

Ладно, вот что получилось. Сделал как понял, если что не так - будем поправлять в рабочем порядке.

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

;; Параметры: %L

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

Global $aList  = _TCHLoadFileList($CmdLine[1]) 
Global $gBackups = False, $gCaseSence = 0
Global Const $GUI_CHECKED = 1 
Global $gRemoveEmpty = False
Global $gIniFile = StringLeft(@ScriptFullPath, StringInStr(@ScriptFullPath, ".", 0, -1)) & "ini"

$hGUI = GUICreate("Вставка текста", 549, 570, -1, -1)
$edTextLeft = GUICtrlCreateEdit("", 8, 96, 529, 73)
$btnOK = GUICtrlCreateButton("OK", 384, 528, 75, 25)
$btnCancel = GUICtrlCreateButton("Отмена", 464, 528, 75, 25)
GUICtrlCreateLabel("начинающейся на", 198, 70, 94, 17, 0x2)
$cbCaseSence = GUICtrlCreateCheckbox("Учитывать регистр", 8, 520, 137, 17)
$edMask = GUICtrlCreateInput("*.*", 8, 24, 265, 21)
GUICtrlCreateLabel("Маска файлов", 8, 6, 78, 17)
$edTextRight = GUICtrlCreateEdit("", 8, 208, 529, 73)
$edTextBefore = GUICtrlCreateEdit("", 8, 320, 529, 73)
$edTextAfter = GUICtrlCreateEdit("", 8, 432, 529, 73)
GUICtrlCreateLabel("заканчивающейся на", 176, 182, 113, 17, 0x2)
GUICtrlCreateLabel("начинающейся на", 196, 294, 95, 17, 0x2)
GUICtrlCreateLabel("заканчивающейся на", 176, 406, 113, 17, 0x2)
$ibFilterLeft = GUICtrlCreateInput("", 296, 66, 241, 21)
$ibFilterRight = GUICtrlCreateInput("", 296, 178, 241, 21)
$ibFilterBefore = GUICtrlCreateInput("", 296, 290, 241, 21)
$ibFilterAfter = GUICtrlCreateInput("", 296, 402, 241, 21)
GUICtrlCreateLabel("", 8, 56, 528, 2, 0x10)
GUIStartGroup()
$Radio1 = GUICtrlCreateRadio("Добавить к строке слева", 8, 69, 153, 17)
GUICtrlSetState(-1, $GUI_CHECKED)
GUICtrlSetTip(-1, "Приоритет над п.3") 
$Radio3 = GUICtrlCreateRadio("Добавить сверху строки", 8, 293, 153, 17) 
GUICtrlSetTip(-1, "Приоритет над п.1") 
GUIStartGroup() 
$Radio2 = GUICtrlCreateRadio("Добавить к строке справа", 8, 181, 161, 17) 
GUICtrlSetState(-1, $GUI_CHECKED) 
GUICtrlSetTip(-1, "Приоритет над п.4") 
$Radio4 = GUICtrlCreateRadio("Добавить снизу строки", 8, 405, 153, 17) 
GUICtrlSetTip(-1, "Приоритет над п.2")
$cbBackups = GUICtrlCreateCheckbox("Создавать резервные копии", 160, 520, 193, 17)
$cbRemoveEmpty = GUICtrlCreateCheckbox("Удалять пустые строки", 8, 544, 150, 17)
$cbCloseDone = GUICtrlCreateCheckbox("Закрывать по завершению", 160, 544, 160, 17)
Global $AccelTable[1][2] = [["^{ENTER}", $btnOK]] 
GUISetAccelerators($AccelTable) 

GUISetState(@SW_SHOW)

GUICtrlSetState($cbCaseSence,   IniRead($gIniFile, "Settings", "CaseSenseSearch", 4))
GUICtrlSetState($cbBackups,     IniRead($gIniFile, "Settings", "MakeBackups", 4))
GUICtrlSetState($cbRemoveEmpty, IniRead($gIniFile, "Settings", "RemoveEmptyLines", 4))
GUICtrlSetState($cbCloseDone,   IniRead($gIniFile, "Settings", "CloseWhenDone", 1))


While 1 
   $nMsg = GUIGetMsg() 
   Switch $nMsg 
      Case -3, $btnCancel 
        Exit 
      Case $btnOK 
        $gMask = _MaskToRegexp(GUICtrlRead($edMask)) 
        $gBackups = BitAND(GUICtrlRead($cbBackups), $GUI_CHECKED) 
        $gRemoveEmpty = BitAND(GUICtrlRead($cbRemoveEmpty), $GUI_CHECKED) 
        If BitAND(GUICtrlRead($cbCaseSence), $GUI_CHECKED) Then $gCaseSence = 1
        WinSetState($hGUI, "", @SW_DISABLE)

        IniWrite($gIniFile, "Settings", "CaseSenseSearch",  GUICtrlRead($cbCaseSence))
        IniWrite($gIniFile, "Settings", "MakeBackups",      GUICtrlRead($cbBackups))
        IniWrite($gIniFile, "Settings", "RemoveEmptyLines", GUICtrlRead($cbRemoveEmpty))
        IniWrite($gIniFile, "Settings", "CloseWhenDone",    GUICtrlRead($cbCloseDone))

        For $i = 1 To $aList[0] 
            If StringRight($aList[$i], 1) = "\" Then 
                _DoFolder($aList[$i]) 
            Else 
                _DoFile($aList[$i]) 
            EndIf 
        Next 
        MsgBox (0, "", "Завершено.", 0, $hGUI)
        If BitAND(GUICtrlRead($cbCloseDone), $GUI_CHECKED) Then Exit
        WinSetState($hGUI, "", @SW_ENABLE)
   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 _ProcessLines(ByRef $aSrcFile, ByRef $aTrgFile, $sText, $sFilter, $nMode)
    If $sText = "" Then Return
    For $i = 1 To $aSrcFile[0]
        Switch $nMode
            Case 0, 2
                If ($sFilter <> "") Then
                    $sLeft = StringLeft($aSrcFile[$i], StringLen($sFilter))
                    If $gCaseSence Then 
                        If Not ($sLeft == $sFilter) Then ContinueLoop
                    Else
                        If $sLeft <> $sFilter Then ContinueLoop
                    EndIf
                EndIf
                If $nMode = 2 Then $aTrgFile[$i] = @CRLF & $aTrgFile[$i]
                $aTrgFile[$i] = $sText & $aTrgFile[$i]
            Case 1, 3
                If ($sFilter <> "") Then
                    $sRight = StringRight($aSrcFile[$i], StringLen($sFilter))
                    If $gCaseSence Then 
                        If Not ($sRight == $sFilter) Then ContinueLoop
                    Else
                        If $sRight <> $sFilter Then ContinueLoop
                    EndIf
                EndIf
                If $nMode = 3 Then $aTrgFile[$i] = $aTrgFile[$i] & @CRLF
                $aTrgFile[$i] = $aTrgFile[$i] & $sText
        EndSwitch
    Next
EndFunc

Func _DoFile($sFile) 
    If StringRegExp(StringLower($sFile), $gMask) = 0 Then Return 
    Local $aSrcFile = _TCHLoadFileList($sFile)
    If $gRemoveEmpty And ($aSrcFile[0] > 0) Then
        Local $aTrgFile[$aSrcFile[0]+1] = [0]
        For $i = 1 To $aSrcFile[0]
            If $aSrcFile[$i] <> "" Then 
                $aTrgFile[0] += 1
                $aTrgFile[$aTrgFile[0]] = $aSrcFile[$i]
            EndIf
        Next
        ReDim $aTrgFile[$aTrgFile[0] + 1]
        $aSrcFile = $aTrgFile
    Else
        Local $aTrgFile = $aSrcFile
    EndIf

    If BitAND(GUICtrlRead($Radio1), $GUI_CHECKED) Then
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextLeft), GUICtrlRead($ibFilterLeft), 0)
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextbefore), GUICtrlRead($ibFilterBefore), 2)
    Else
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextbefore), GUICtrlRead($ibFilterBefore), 2)
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextLeft), GUICtrlRead($ibFilterLeft), 0)
    EndIf

    If BitAND(GUICtrlRead($Radio3), $GUI_CHECKED) Then
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextRight), GUICtrlRead($ibFilterRight), 1)
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextAfter), GUICtrlRead($ibFilterAfter), 3)
    Else
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextAfter), GUICtrlRead($ibFilterAfter), 3)
        _ProcessLines($aSrcFile, $aTrgFile, GUICtrlRead($edTextRight), GUICtrlRead($ibFilterRight), 1)
    EndIf

    If $gBackups Then FileCopy($sFile, $sFile & ".bak", 1) ; бэкап 
    Local $hFile = FileOpen($sFile, 2)
    For $i = 1 To $aTrgFile[0]
        FileWriteLine($hFile, $aTrgFile[$i]) 
    Next
    FileClose($hFile) 
EndFunc

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

; fast list load function 
Func _TCHLoadFileList($sFileName, $nIncBlock = 1000) 
    Local $asList[$nIncBlock + 1] = [0] 
    Local $hfList = FileOpen($sFileName, 0) 
    Local $nCounter = 0 
    While 1 
        For $i = 1 To $nIncBlock 
            $asList[$nCounter + $i] = FileReadLine($hfList) 
            If @error Then 
                ReDim $asList[UBound($asList) + $i - $nIncBlock - 1] 
                ExitLoop(2) 
            EndIf 
        Next 
        $nCounter += $nIncBlock 
        ReDim $asList[UBound($asList)+$nIncBlock] 
    Wend 
    FileClose($hfList) 
    $asList[0] = UBound($asList)-1 
    Return $asList 
EndFunc
Последний раз редактировалось Loopback Ср окт 20, 2010 13:44, всего редактировалось 3 раза.
Аватара пользователя
Flasher
Сообщения: 14229
Зарегистрирован: Пт ноя 06, 2009 00:33
Откуда: Москва

Сообщение Flasher »

О, почти то, что надо! :)
Пока не тестил, сразу что в глаза бросилось:
названия полей лучше не писать через запятую, иначе условия будут восприниматься как необходимые. Стоит текст до запятой сдвинуть до конца влево, а после вправо( и чтобы они отстояли на одинаковом расстоянии от полей с фильтром).
Слова "слева/справа" лучше поменять местами с "к строке". Так лучше по стилистике.
"Перед"/"после" - может восприниматься как слева/справа, так и над/под. Я бы заменил на "сверху"/"снизу" строки. (У себя, естественно, уже поменял.)
Само окно можно поуже сделать на расстояние от слова "копии" до кнопки ОК + от слова "регистр" до флага.
Хорошо было бы добавить птицу "Закрывать после выполнения".
Поля фильтра желательно сделать равной длины.
Про флаги с приоритетами, надеюсь, не забыл? :wink:
Об остальном чуть позже.

Добавлено: что-то не хочет скрипт работать, что с фильтром, что без. :(
Пробовал с одним файлом под курсором, с выделенным файлом, с выделенными папками.
Ответить