Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Скриптовые языки программирования > Python
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 18.10.2023, 14:13   #21
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,291
По умолчанию

Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
тут не понятно
У пробела индекс 4, а не 5.
Код:
012345678
1234 234
Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
там же у него несколько выложено-какой смотреть?
Вообще ссылка ведет на конкретный комментарий, но если браузер не перематывает на него, то я имел ввиду код с функцией paragraph_replace_text. Вот еще хороший пример - isolate_run() (scanny - главный контрибьютор python-docx, ему лучше всех известно внутреннее устройство библиотеки). С ним работает:
Код:
import itertools
from docx import Document
from docx.text.run import Run
from docx.shared import Pt, RGBColor
import re
import copy


def isolate_run(paragraph, start, end):
    """Return docx.text.Run object containing only `paragraph.text[start:end]`.

    Runs are split as required to produce a new run at the `start` that ends at `end`.
    Runs are unchanged if the indicated range of text already occupies its own run. The
    resulting run object is returned.

    `start` and `end` are as in Python slice notation. For example, the first three
    characters of the paragraph have (start, end) of (0, 3). `end` is *not* the index of
    the last character. These correspond to `match.start()` and `match.end()` of a regex
    match object and `s[start:end]` in Python slice notation.
    """
    rs = tuple(paragraph._p.r_lst)

    def advance_to_run_containing_start(start, end):
        """Return (r_idx, start, end) triple indicating start run and adjusted offsets.

        The start run is the run the `start` offset occurs in. The returned `start` and
        `end` values are adjusted to be relative to the start of `r_idx`.
        """
        # --- add 0 at end so `r_ends[-1] == 0` ---
        r_ends = tuple(itertools.accumulate(len(r.text) for r in rs)) + (0,)
        r_idx = 0
        while start >= r_ends[r_idx]:
            r_idx += 1
        skipped_rs_offset = r_ends[r_idx - 1]
        return rs[r_idx], r_idx, start - skipped_rs_offset, end - skipped_rs_offset

    def split_off_prefix(r, start, end):
        """Return adjusted `end` after splitting prefix off into separate run.

        Does nothing if `r` is already the start of the isolated run.
        """
        if start > 0:
            prefix_r = copy.deepcopy(r)
            r.addprevious(prefix_r)
            r.text = r.text[start:]
            prefix_r.text = prefix_r.text[:start]
        return end - start

    def split_off_suffix(r, end):
        """Split `r` at `end` such that suffix is in separate following run."""
        suffix_r = copy.deepcopy(r)
        r.addnext(suffix_r)
        r.text = r.text[:end]
        suffix_r.text = suffix_r.text[end:]

    def lengthen_run(r, r_idx, end):
        """Add prefixes of following runs to `r` until `end` is reached."""
        while len(r.text) < end:
            suffix_len_reqd = end - len(r.text)
            r_idx += 1
            next_r = rs[r_idx]
            if len(next_r.text) <= suffix_len_reqd:
                # --- subsume next run ---
                r.text = r.text + next_r.text
                next_r.getparent().remove(next_r)
                continue
            if len(next_r.text) > suffix_len_reqd:
                # --- take prefix from next run ---
                r.text = r.text + next_r.text[:suffix_len_reqd]
                next_r.text = next_r.text[suffix_len_reqd:]

    # --- 1. skip over any runs before the one containing the start of our range ---
    r, r_idx, start, end = advance_to_run_containing_start(start, end)

    # --- 2. split first run where our range starts, placing "prefix" to our range
    # ---    in a new run inserted just before this run. After this, our run will begin
    # ---    at the right point and the left-hand side of our work is done.
    end = split_off_prefix(r, start, end)

    # --- 3. if run is longer than isolation-range we need to split-off a suffix run ---
    if len(r.text) > end:
        split_off_suffix(r, end)

    # --- 4. But if our run is shorter than the desired isolation-range we need to
    # ---    lengthen it by taking text from subsequent runs
    elif len(r.text) < end:
        lengthen_run(r, r_idx, end)

    # --- if neither 3 nor 4 apply it's because the run already ends in the right place
    # --- and there's no further work to be done.

    return Run(r, paragraph)


d = Document('test.docx')
vFindText = ["123", "234"]

for paragraph in d.paragraphs:
    runs = paragraph.runs
    runs[0].text = paragraph.text
    runs[0].font.size = Pt(16)
    runs[0].font.color.rgb = RGBColor(0, 0, 0)

    for run in runs[1:]:
        r = run._element
        r.getparent().remove(r)

    for match in re.finditer("|".join(map(re.escape, vFindText)), paragraph.text):
        isolate_run(paragraph, match.start(), match.end()).font.color.rgb = RGBColor(255, 0, 0)

d.save('test-highlightened.docx')
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA вне форума Ответить с цитированием
Старый 18.10.2023, 14:42   #22
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

BDA спасибо
что сказать, работает как нужно
60 строк кода на питоне - код который не я писал и поэтому конечно плаваю....
30 строк кода на VBA - код который я написал и мне понятен
руки дойдут сравню быстродействие.......
Ципихович Эндрю вне форума Ответить с цитированием
Старый 18.10.2023, 14:59   #23
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,291
По умолчанию

При сравнении количества строк считал бы для питона только последние 17 строк (и несколько строк с импортами). Все равно и библиотека docx, и функция isolate_run уже написаны и используются как черный ящик.
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA вне форума Ответить с цитированием
Старый 18.10.2023, 20:25   #24
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

BDA, стал пробовать, загрузил вордовский файл с реальными данными
получил ошибку на строке: runs[0].text = paragraph.text
Traceback (most recent call last):
File "C:\pythonProject\sudrf.ru1.py" , line 57, in <module>
runs[0].text = paragraph.text
~~~~^^^
IndexError: list index out of range
как подправить, чтобы работало?
Ципихович Эндрю вне форума Ответить с цитированием
Старый 18.10.2023, 20:52   #25
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,291
По умолчанию

Код:
    runs = paragraph.runs
    if not runs:
        continue
    runs[0].text = paragraph.text
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA вне форума Ответить с цитированием
Старый 19.10.2023, 08:23   #26
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

BDA, поправил, спасибо, ошибок нет
запустил тест с 716000 замен, они считываются с файла - пока делает, минут уж десять, а ещё код показали джи пи ти, выдала оптимизированный код, как он Вам:
Код:
from docx import Document
from docx.shared import Pt, RGBColor
import re
from datetime import datetime

def configure_run(run):
    # run.font.size = Pt(16)
    run.font.color.rgb = RGBColor(0, 0, 0)  # Черный цвет


def highlight_matches(paragraph, vFindText):
    for match in re.finditer("|".join(map(re.escape, vFindText)), paragraph.text):
        start, end = match.start(), match.end()
        run = paragraph.runs[0]
        configure_run(run)
        run.text = paragraph.text[:start]
        for i in range(1, len(paragraph.runs)):
            paragraph.runs[i].clear()
        if end < len(paragraph.text):
            new_run = paragraph.runs[0].insert_run(end)
            new_run.text = paragraph.text[end:]
            configure_run(new_run)
            new_run.font.color.rgb = RGBColor(255, 0, 0)  # Красный цвет


def main():
    date_start = datetime.now()
    doc = Document('test.docx')
    vFindText = ["123", "234"]

    for paragraph in doc.paragraphs:
        highlight_matches(paragraph, vFindText)

    doc.save('test-highlightened.docx')


if __name__ == "__main__":
    main()

date_end = datetime.now()
print('Начато:', date_start, '\n' \
    'Закончено:', date_end)
относительно date это я уже сам добавил))
Ципихович Эндрю вне форума Ответить с цитированием
Старый 19.10.2023, 11:33   #27
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
джи пи ти, выдала оптимизированный код, как он Вам
опробовал, увы искомый текст с массива НЕ красился в красный цвет, а удалялся и удалялся и до конца абзаца
а код с поста № 21 делал работу при количестве 716000 замен, он делал работу часа три, я не дождался окончания, так как предполагается, что этих замен будет более 7 млн - понятно, что НЕ справится питон с этой задачей, на каком ЯП это возможно?
Ципихович Эндрю вне форума Ответить с цитированием
Старый 20.10.2023, 00:21   #28
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,291
По умолчанию

Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
искомый текст с массива НЕ красился в красный цвет
Оптимизированный код натолкнул на такой вариант:
Код:
from docx import Document
from docx.shared import RGBColor
import re
from datetime import datetime

def main():
    date_start = datetime.now()
    highlights = 0

    d = Document('война и мир.docx')
    vFindText = ["а"]

    for paragraph in d.paragraphs:
        text = paragraph.text
        if not text:
            continue
        paragraph.clear()
        last_idx = 0
        for match in re.finditer("|".join(map(re.escape, vFindText)), text):
            highlights += 1
            start, end = match.start(), match.end()
            if start > last_idx:
                paragraph.add_run(text[last_idx:start]).font.color.rgb = RGBColor(0, 0, 0)
            last_idx = end
            paragraph.add_run(text[start:end]).font.color.rgb = RGBColor(255, 0, 0)
        if last_idx != len(text):
            paragraph.add_run(text[last_idx:]).font.color.rgb = RGBColor(0, 0, 0)

    d.save('война и мир-highlightened.docx')

    date_end = datetime.now()
    print('Начато: ', date_start, '\nЗакончено: ', date_end, '\nСекунд: ', (date_end - date_start).total_seconds(),'\nВыделения: ', highlights)

if __name__ == "__main__":
    main()
В 590-страничном документе выделение буквы "а" (119 тысяч раз встречается в тексте) с помощью кода с isolate_run занимало примерно 260 секунд, этот вариант управляется за 37 секунд. Ваш вариант на VBA справляется с этим текстом за 3.6 секунд.
Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
при количестве 716000 замен
Это длина списка vFindText или количество цветных выделений в тексте?
Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
на каком ЯП это возможно?
Не знаю. А VBA не справляется?
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA вне форума Ответить с цитированием
Старый 20.10.2023, 16:51   #29
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

Цитата:
Сообщение от BDA Посмотреть сообщение
А VBA не справляется?
давно на нём не писал и ещё нужно вспомнить как мои замены подать ему с текстового файла в нужном формате, для вба это та ещё задача...........
пробовал на вордовском файле в 2108 страниц
если правильно распаковал все замены из текстового файла благодаря вашему ответу от сегодня в другой теме то получается что:

Длина текстового файла с заменами: 42407597
Длина текстового файла без комментариев JS: 36797955
Количество замен в текстовом файле: 887933

но подумал для раскрашивание скрипту подать только те замены, которые хотя бы есть в ворд файле ну и на вашем последнем примере, так, что пока тема не закрыта))
Ципихович Эндрю вне форума Ответить с цитированием
Старый 20.10.2023, 22:10   #30
Ципихович Эндрю
Старожил
 
Регистрация: 24.01.2011
Сообщений: 3,043
По умолчанию

BDA
Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
но подумал для раскрашивание скрипту подать только те замены, которые хотя бы есть в ворд файле
и согласно своим знаниям делаю это так:
Код:
import docx # pip install python-docx
doc = docx.Document('test.docx')
text_doc = []
text_doc_n = ''
for paragraph in doc.paragraphs:
    text_doc.append(paragraph.text)
    text_doc_n += paragraph.text + '\n'
# print('\n'.join(text_doc))
text_doc_n = text_doc_n.lower() # все символы преобразовать в нижний регистр
# 'ё' заменить на 'е', так как в файле замен употребляется буква 'ё', а в файле, где будет идти поиск может вместо буквы 'ё' употребляться буква 'е'
text_doc_n = text_doc_n.replace('ё', 'е')
# '«' заменить на '"', так как в файле замен нет '«', нет '»', имеются только '"'
text_doc_n = text_doc_n.replace('«', '"')
# '»' заменить на '"', так как в файле замен нет '«', нет '»', имеются только '"'
text_doc_n = text_doc_n.replace('»', '"') 
print(text_doc_n)
vFindTextDoc = []
for l in vFindText: # в этом списке 887933 значений
    if l in text_doc_n: vFindTextDoc.append(l)
и всё скрипт делается более часа - код оптимальный?, или можно его поправить? как?
можно для упрощения задачи вордовский файл из 2018 страниц скопировать и сохранить в текстовом виде, тогда будет 27073 строки, может скрипту будет легче?

BDA кстати может в помощь будет нечто, забыл как оно называется, оно появилось в питоне 11, который у меня есть - ищет пересечения данных одного списка в данных другого списка одним махом, но я это нечто так и не освоил так как не мог понять, где лично я смогу такое применить и тут выдался случай, как это нечто называется? поняли о чём речь?

Цитата:
Сообщение от Ципихович Эндрю Посмотреть сообщение
поняли о чём речь?
вспомнил - Pattern matching

Последний раз редактировалось BDA; 22.10.2023 в 06:52.
Ципихович Эндрю вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Шрифт текста в файле пдф Alifhan Фриланс 2 24.08.2019 11:21
изменить шрифт detalik Помощь студентам 2 15.04.2011 16:22
Как изменить шрифт FIREVISTA Общие вопросы C/C++ 12 24.04.2010 19:08
Искаженный шрифт в скомпилированном файле справки Fantom Общие вопросы Delphi 0 19.09.2008 12:51
ШРИФТ в файле справки prog38 Помощь студентам 7 13.09.2008 08:36