Что нового

Поиск-замена в RTF-файлах?

Suppir

Продвинутый
Сообщения
967
Репутация
62
Добрый день!

Есть rtf-файл в ANSI. Необходимо без использования word сделать в нем поиск и замену определенного русского текста.
Например, везде слово "лето" поменять на "осень". Но в rtf-формате каждая русская буква представлена набором символов. Кто сталкивался с подобной задачей, как лучше ее решить?
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
http://progandy.co.cc/downloads/view.download/3/5

^эту UDF уже пробовал заюзать?
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Так эту библиотеку вставили вроде в стандартную библиотеку AutoIt. Называется <GuiRichEdit.au3>.
Там можно отрисовать rtf-данные (с помощью _GUICtrlRichEdit_StreamFromFile, например), и можно записать эти данные обратно в файл, но при этом невозможно сделать поиск-замену с регулярными выражениями. А мне нужно сделать именно такой поиск-замену в rtf-данных.


Я использую следующую функцию для получения rtf-данных из буфера обмена (может быть кому понадобится):


Код:
Func _ClipBoard_GetDataAlternative($Formato = 1)
   If Not (_ClipBoard_IsFormatAvailable($Formato) Or $Formato=18 Or $Formato=19) Then Return SetError(-1, 0, 0)
   If Not _ClipBoard_Open(0) Then Return SetError(-2, 0, 0)
   Switch $Formato
      Case 18
         $iFormat=_ClipBoard_RegisterFormat ("Rich Text Format")
         If $iFormat=0 Then Return SetError(-1, 0, 0)
      Case 19
         $iFormat=_ClipBoard_RegisterFormat ("HTML Format")
         If $iFormat=0 Then Return SetError(-1, 0, 0)
      Case Else
         $iFormat=$Formato
   EndSwitch
   Local $hMemory = _ClipBoard_GetDataEx($iFormat)

   ;_ClipBoard_Close()      ; moved to end: traditionally done *after* copying over the memory

   If $hMemory=0 Then
      _ClipBoard_Close()
      Return SetError(-3, 0, 0)
   EndIf

   Local $pMemoryBlock=_MemGlobalLock($hMemory)

   If $pMemoryBlock=0 Then
      _ClipBoard_Close()
      Return SetError(-4,0,0)
   EndIf

   ; Get the actual memory size of the ClipBoard memory object (in bytes)
   Local $iDataSize=_MemGlobalSize($hMemory)

   If $iDataSize = 0 Then
      _MemGlobalUnlock($hMemory)
      _ClipBoard_Close()
      Return SetError(-5,0,"")
   EndIf

   Local $tData
   Switch $Formato
      Case $CF_TEXT, $CF_OEMTEXT, 18, 19, 20
         $tData = DllStructCreate("char[" & $iDataSize & "]", $pMemoryBlock)
      Case $CF_UNICODETEXT
         ; Round() shouldn't be necessary, as CF_UNICODETEXT should be 2-bytes wide & thus evenly-divisible
         $iDataSize=Round($iDataSize/2)
         $tData = DllStructCreate("wchar[" & $iDataSize & "]", $pMemoryBlock)
      Case Else
         ; Binary data return for all other formats
         $tData = DllStructCreate("byte[" & $iDataSize & "]", $pMemoryBlock)
   EndSwitch
   ; Grab the data from the Structure so the Memory can be unlocked
   Local $vReturn = DllStructGetData($tData, 1)

   ; Unlock the memory & Close the clipboard now that we have grabbed what we needed
   _MemGlobalUnlock($hMemory)
   _ClipBoard_Close()

   ; Return the size of the string or binary object in @extended
   Return SetExtended($iDataSize, $vReturn)
EndFunc   ;==>_ClipBoard_GetData
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
Хм, может ты просто не хотел вникать?
Вот как можно занести текст из файла в переменную(GUIRichedit контрол можно вообще спрятать):

Код:
#include <GuiRichEdit.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Main()

Func Main()
    Local $hGui, $iMsg, $btnNext, $iStep = 0, $lblMsg, $hRichEdit
    $hGui = GUICreate("Example (" & StringTrimRight(@ScriptName,4) &")", 320, 350, -1, -1)
    $hRichEdit = _GUICtrlRichEdit_Create($hGui, "This is a test.", 10, 10, 300, 220, _
            BitOR($ES_MULTILINE, $WS_VSCROLL, $ES_AUTOVSCROLL))
    $lblMsg = GUICtrlCreateLabel("", 10, 235, 300, 60)
    $btnNext = GUICtrlCreateButton("Next", 270, 310, 40, 30)
    _GUICtrlRichEdit_StreamFromFile($hRichEdit, "D:\test.rtf")
    $stream2var = _GUICtrlRichEdit_StreamToVar($hRichEdit, False, False)
   MsgBox(0,"StreamToVar",$stream2var)
   GUISetState()
    While True
        $iMsg = GUIGetMsg()
        Select
            Case $iMsg = $GUI_EVENT_CLOSE
                GUIDelete()
                Exit
        EndSelect
    WEnd
EndFunc   ;==>Main


потом обработать переменную регуляркой и таким же макаром сохранить в файл...
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Да видел я этот пример в справке много раз уже. Проблема в том, что русские буквы в RTF кодированы несколькими символами.
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
Проблема в том, что русские буквы в RTF кодированы несколькими символами.
И что? Вот щас проверил - забил в файл текст:
одинаццать
двенаццать
тринаццать
четырнаццать
открываю, обрабатываю(заменяю "цц" на "дц") и сохраняю:
Код:
#include <GuiRichEdit.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
$file="D:\test.rtf"
RichRegReplace($file)

Func RichRegReplace($rtf_file)
    Local $hGui, $iMsg, $hRichEdit
    $hGui = GUICreate("Example (" & StringTrimRight(@ScriptName,4) &")", 320, 350, -1, -1)
    $hRichEdit = _GUICtrlRichEdit_Create($hGui, "This is a test.", 10, 10, 300, 220, _
            BitOR($ES_MULTILINE, $WS_VSCROLL, $ES_AUTOVSCROLL))
    _GUICtrlRichEdit_StreamFromFile($hRichEdit, $rtf_file)
    $stream2var = _GUICtrlRichEdit_StreamToVar($hRichEdit, False, False)
    MsgBox(0,"Stream2Var",$stream2var)
    $stream2var = StringRegExpReplace($stream2var, "цц", "дц")
    MsgBox(0,"Stream2VarReplace",$stream2var)
    _GUICtrlRichEdit_StreamFromVar($hRichEdit, $stream2var)
    _GUICtrlRichEdit_StreamToFile($hRichEdit, $rtf_file, False)
    GUISetState()
    While True
        $iMsg = GUIGetMsg()
        Select
            Case $iMsg = $GUI_EVENT_CLOSE
                GUIDelete()
                Exit
        EndSelect
    WEnd
EndFunc

и все норм...

может у тебя с кодовой страницей что-то не то?
тогда в функциях _GUICtrlRichEdit_StreamToFile и _GUICtrlRichEdit_StreamToVar есть параметр для задания кодовой страницы.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Ты в файл забил русский текст.

Если этот текст забить в word или wordpad, а потом сохранить в rtf, то текст перекодируется.

Я написал таблицу обратной перекодировки в русский:

Код:
$rtf = StringReplace($rtf, "\'e0", "а")
$rtf = StringReplace($rtf, "\'e1", "б")
$rtf = StringReplace($rtf, "\'e2", "в")
$rtf = StringReplace($rtf, "\'e3", "г")
$rtf = StringReplace($rtf, "\'e4", "д")
$rtf = StringReplace($rtf, "\'e5", "е")
$rtf = StringReplace($rtf, "\'b8", "ё")
$rtf = StringReplace($rtf, "\'e6", "ж")
$rtf = StringReplace($rtf, "\'e7", "з")
$rtf = StringReplace($rtf, "\'e8", "и")
$rtf = StringReplace($rtf, "\'e9", "й")
$rtf = StringReplace($rtf, "\'ea", "к")
$rtf = StringReplace($rtf, "\'eb", "л")
$rtf = StringReplace($rtf, "\'ec", "м")
$rtf = StringReplace($rtf, "\'ed", "н")
$rtf = StringReplace($rtf, "\'ee", "о")
$rtf = StringReplace($rtf, "\'ef", "п")
$rtf = StringReplace($rtf, "\'f0", "р")
$rtf = StringReplace($rtf, "\'f1", "с")
$rtf = StringReplace($rtf, "\'f2", "т")
$rtf = StringReplace($rtf, "\'f3", "у")
$rtf = StringReplace($rtf, "\'f5", "х")
$rtf = StringReplace($rtf, "\'f4", "ф")
$rtf = StringReplace($rtf, "\'f6", "ц")
$rtf = StringReplace($rtf, "\'f7", "ч")
$rtf = StringReplace($rtf, "\'f8", "ш")
$rtf = StringReplace($rtf, "\'f9", "щ")
$rtf = StringReplace($rtf, "\'fc", "ь")
$rtf = StringReplace($rtf, "\'fa", "ъ")
$rtf = StringReplace($rtf, "\'fb", "ы")
$rtf = StringReplace($rtf, "\'fd", "э")
$rtf = StringReplace($rtf, "\'fe", "ю")
$rtf = StringReplace($rtf, "\'ff", "я")
$rtf = StringReplace($rtf, "\'c0", "А")
$rtf = StringReplace($rtf, "\'c1", "Б")
$rtf = StringReplace($rtf, "\'c2", "В")
$rtf = StringReplace($rtf, "\'c3", "Г")
$rtf = StringReplace($rtf, "\'c4", "Д")
$rtf = StringReplace($rtf, "\'c5", "Е")
$rtf = StringReplace($rtf, "\'a8", "Ё")
$rtf = StringReplace($rtf, "\'c6", "Ж")
$rtf = StringReplace($rtf, "\'c7", "З")
$rtf = StringReplace($rtf, "\'c8", "И")
$rtf = StringReplace($rtf, "\'c9", "Й")
$rtf = StringReplace($rtf, "\'ca", "К")
$rtf = StringReplace($rtf, "\'cb", "Л")
$rtf = StringReplace($rtf, "\'cc", "М")
$rtf = StringReplace($rtf, "\'cd", "Н")
$rtf = StringReplace($rtf, "\'ce", "О")
$rtf = StringReplace($rtf, "\'cf", "П")
$rtf = StringReplace($rtf, "\'d0", "Р")
$rtf = StringReplace($rtf, "\'d1", "С")
$rtf = StringReplace($rtf, "\'d2", "Т")
$rtf = StringReplace($rtf, "\'d3", "У")
$rtf = StringReplace($rtf, "\'d5", "Х")
$rtf = StringReplace($rtf, "\'d4", "Ф")
$rtf = StringReplace($rtf, "\'d6", "Ц")
$rtf = StringReplace($rtf, "\'d7", "Ч")
$rtf = StringReplace($rtf, "\'d8", "Ш")
$rtf = StringReplace($rtf, "\'d9", "Щ")
$rtf = StringReplace($rtf, "\'dc", "Ь")
$rtf = StringReplace($rtf, "\'da", "Ъ")
$rtf = StringReplace($rtf, "\'db", "Ы")
$rtf = StringReplace($rtf, "\'dd", "Э")
$rtf = StringReplace($rtf, "\'de", "Ю")
$rtf = StringReplace($rtf, "\'df", "Я")




Добавлено:
Сообщение автоматически объединено:

Еще проблема в поисках-заменах в том, что во многих случаях текст разбивается на несколько строк в rtf.

Например, если написать "Sup pir", то он в RTF будет выглядеть:

Sup
pir

А символ неразрывного пробела в rtf выглядит как "\~". Да и много других отличий, нужно разбираться...

Т.е. регулярные выражения нужно совсем по-другому составлять, нежели с обычным текстом...


Еще осталось два вопроса:

1) как включить режим отображения спец. символов в RichEdit?
2) как скопировать все содержимое RichEdit в буфер обмена? (не создавая видимого GUI)
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
хм, я текст забивал через обычный wordpad в XP...
Лучше скинь мне свою rtf-фку (одинаццать\nдвенаццать\nтринаццать\nчетырнаццать)
- хочу глянуть на нее.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Ну, вот, например:
http://zalil.ru/30004558

если открыть в wordpad, то видны русские символы, а если читать в блокноте, то вместо русских символов коды.
Я решил эту проблему перекодированием, но очень трудно делать поиск-замену по таблицам. Например, в регулярном выражении нужно совсем по-другом представлять якори "^" и "$" (начало и конец строки), потому что в rtf вместо "конца строки" может быть какая-нибудь команда вроде \cell (граница ячейки).


Добавлено:
Сообщение автоматически объединено:

Еще такой момент очень неудобный. Например, в тексте у нас написано "п р и к а з ы в а ю", а в RTF это выглядит как
п
р
и
к
а
з
ы
в
а
ю

т.е. все пробелы представлены как переводы строк. А переводы строк (@CRLF) представлены как специальные команды параграфов и абзацев. В общем, разбираться и разбираться...
 

Redline

AutoIT Гуру
Сообщения
506
Репутация
369
Suppir [?]
2) как скопировать все содержимое RichEdit в буфер обмена? (не создавая видимого GUI)
Код:
$oRP = ObjCreate('RICHTEXT.RichtextCtrl.1')
With $oRP
	.Loadfile('d:\1.rtf')
	.SelStart = 0
    .SelLength = StringLen(.Text)
	ConsoleWrite(.SelText & @CRLF)
	ConsoleWrite(.SelRTF & @CRLF)
EndWith

Замаялся на Win7 ставить :wacko:,если кому понадобится - инструкция:
качаем прикрепленный архив из двух файлов
richtx32.ocx копируем в windows\system32
регистрируем: c:\windows\system32>regsvr32 richtx32.ocx
если не помогло запускаем фикс-файл для реестра.
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
Не пойму, зачем перекодировать, кодировка норм(\ansi\ansicpg1251) - у меня даж из таблички текст выдрало и обрывов строки после каждой буквы нет...
Вот, смотри на меседжбокс - http://zalil.ru/30004721

ЗЫ: у тебя какой виндовоз?
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Redline
спасибо, я уже таким образом сделал:

Код:
$iFormat = _ClipBoard_RegisterFormat ("Rich Text Format")
If not _ClipBoard_SetData($rtf, $iFormat) Then ConsoleWrite ("Не могу записать данные в буфер" & @CRLF)


rtf - это данные формата rtf. Таким образом можно всю замену делать в памяти, без использования GUI.


Добавлено:
Сообщение автоматически объединено:

Sp01LeR
да нет же :smile: открой rtf-файл в блокноте через F4. А потом попробуй найти там русский текст, а, тем более, сделать поиск-замену с регулярными выражениями:



зы. дома XP, на работе семерка.
 

Redline

AutoIT Гуру
Сообщения
506
Репутация
369
Если просто с текстом, то у меня так сработало:
Код:
$oRP = ObjCreate('RICHTEXT.RichtextCtrl.1')
With $oRP
	.LoadFile('d:\1.rtf')
	.SelStart = 0
	.SelLength = StringLen(.Text)
	.SelText = StringRegExpReplace(.SelText, '(лето)', 'осень')
	.SaveFile('d:\2.rtf')
EndWith
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Redline

У меня вот такой код:

$oRP = ObjCreate('RICHTEXT.RichtextCtrl.1')
With $oRP
.LoadFile('temp.rtf')
.SelStart = 0
.SelLength = StringLen(.Text)
.SelText = StringRegExpReplace(.SelText, '(лето)', 'осень')
.SaveFile('d:\2.rtf')
EndWith

выдает ошибку:




windows xp, dll такая есть в system 32.
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
Suppir сказал(а):
А потом попробуй найти там русский текст, а, тем более, сделать поиск-замену с регулярными выражениями
Зачем? Ты пробовал запустить мой пример из поста №5 ???

Хотя впрочем, возможно что тебе именно нужен plain-text или замена через буфер, но решение у тебя уже есть...

ЗЫ: кстати, есть одна неплохая утилита Text Workbench(office edition) с поиском/заменой по word'ским файлам:


- может лучше попробовать ее чем с RICHTEXT обьектом мучится...
 
Верх