Подтвердить что ты не робот

Есть ли более быстрый способ очистки управляющих символов в файле?

Ранее, я очищал данные, используя фрагмент кода ниже

import unicodedata, re, io

all_chars = (unichr(i) for i in xrange(0x110000))
control_chars = ''.join(c for c in all_chars if unicodedata.category(c)[0] == 'C')
cc_re = re.compile('[%s]' % re.escape(control_chars))
def rm_control_chars(s): # see http://www.unicode.org/reports/tr44/#General_Category_Values
    return cc_re.sub('', s)

cleanfile = []
with io.open('filename.txt', 'r', encoding='utf8') as fin:
    for line in fin:
        line =rm_control_chars(line)
        cleanfile.append(line)

В файле есть символы новой строки, которые я хочу сохранить.

Ниже указано время, затраченное на cc_re.sub('', s), чтобы заменить первые несколько строк (1-й столбец - это время, а 2-й столбец - len(s)):

0.275146961212 251
0.672796010971 614
0.178567171097 163
0.200030088425 180
0.236430883408 215
0.343492984772 313
0.317672967911 290
0.160616159439 142
0.0732028484344 65
0.533437013626 468
0.260229110718 236
0.231380939484 204
0.197766065598 181
0.283867120743 258
0.229172945023 208

Как предложил @ashwinichaudhary, используя s.translate(dict.fromkeys(control_chars)) и те же самые длинные выходы журнала:

0.464188098907 252
0.366552114487 615
0.407374858856 164
0.322507858276 181
0.35142993927 216
0.319973945618 314
0.324357032776 291
0.371646165848 143
0.354818105698 66
0.351796150208 469
0.388131856918 237
0.374715805054 205
0.363368988037 182
0.425950050354 259
0.382766962051 209

Но для моего 1GB текста код очень медленный. Есть ли другой способ очистки управляемых символов?

4b9b3361

Ответ 1

нашел рабочий характер решения charater, я скачал его с помощью файла 100K:

import unicodedata, re, io
from time import time

# This is to generate randomly a file to test the script

from string import lowercase
from random import random

all_chars = (unichr(i) for i in xrange(0x110000))
control_chars = [c for c in all_chars if unicodedata.category(c)[0] == 'C']
chars = (list(u'%s' % lowercase) * 115117) + control_chars

fnam = 'filename.txt'

out=io.open(fnam, 'w')

for line in range(1000000):
    out.write(u''.join(chars[int(random()*len(chars))] for _ in range(600)) + u'\n')
out.close()


# version proposed by alvas
all_chars = (unichr(i) for i in xrange(0x110000))
control_chars = ''.join(c for c in all_chars if unicodedata.category(c)[0] == 'C')
cc_re = re.compile('[%s]' % re.escape(control_chars))
def rm_control_chars(s):
    return cc_re.sub('', s)

t0 = time()
cleanfile = []
with io.open(fnam, 'r', encoding='utf8') as fin:
    for line in fin:
        line =rm_control_chars(line)
        cleanfile.append(line)
out=io.open(fnam + '_out1.txt', 'w')
out.write(''.join(cleanfile))
out.close()
print time() - t0

# using a set and checking character by character
all_chars = (unichr(i) for i in xrange(0x110000))
control_chars = set(c for c in all_chars if unicodedata.category(c)[0] == 'C')
def rm_control_chars_1(s):
    return ''.join(c for c in s if not c in control_chars)

t0 = time()
cleanfile = []
with io.open(fnam, 'r', encoding='utf8') as fin:
    for line in fin:
        line = rm_control_chars_1(line)
        cleanfile.append(line)
out=io.open(fnam + '_out2.txt', 'w')
out.write(''.join(cleanfile))
out.close()
print time() - t0

вывод:

114.625444174
0.0149750709534

Я пробовал файл 1Gb (только для второго), и он длился 186 секунд.

Я также написал эту другую версию того же script, немного быстрее (176s) и более эффективную память (для очень больших файлов, не соответствующих RAM):

t0 = time()
out=io.open(fnam + '_out5.txt', 'w')
with io.open(fnam, 'r', encoding='utf8') as fin:
    for line in fin:
        out.write(rm_control_chars_1(line))
out.close()
print time() - t0

Ответ 2

Как и в UTF-8, все управляющие символы закодированы в 1 байт (совместимы с ASCII) и ниже 32, я предлагаю этот быстрый фрагмент кода:

#!/usr/bin/python
import sys

ctrl_chars = [x for x in range(0, 32) if x not in (ord("\r"), ord("\n"), ord("\t"))]
filename = sys.argv[1]

with open(filename, 'rb') as f1:
  with open(filename + '.txt', 'wb') as f2:
    b = f1.read(1)
    while b != '':
      if ord(b) not in ctrl_chars:
        f2.write(b)
      b = f1.read(1)

Достаточно ли этого?

Ответ 3

Это должно быть в python? Как насчет очистки файла перед его чтением в python для начала. Используйте sed, который будет обрабатывать его по очереди в любом случае.

См. удаление управляющих символов с помощью sed.

и если вы подключите его к другому файлу, вы можете открыть его. Я не знаю, как быстро это будет. Вы можете сделать это в оболочке script и протестировать ее. согласно эта страница - sed - 82M символов в секунду.

Надеюсь, что это поможет.

Ответ 4

Если вы хотите, чтобы он двигался очень быстро? Разбейте свой ввод на несколько фрагментов, заверните этот код перехвата данных как метод и используйте пакет Python multiprocessing для его параллелизации, записывая в обычный текстовый файл. Идти по-характеру - это самый простой способ хрустить такие вещи, но это всегда занимает некоторое время.

https://docs.python.org/3/library/multiprocessing.html

Ответ 5

Я удивлен, что никто не упомянул mmap, который может быть здесь просто подходящим.

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

Вы загружаете файл в память (вид), а затем вы можете фактически запустить re.sub() над объектом. Это помогает устранить узкое место ввода-вывода и позволяет вам изменить байты на месте, прежде чем записывать его обратно.

После этого вы можете поэкспериментировать с str.translate() vs re.sub() и также включать в себя любые дальнейшие оптимизации, такие как двойной буферный CPU и IO или использование нескольких ядер или потоков CPU.

Но это будет выглядеть примерно так:

import mmap

f = open('test.out', 'r')
m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

Хорошая выдержка из документации mmap:

.. Вы можете использовать объекты mmap в большинстве мест, где ожидаются строки; например, вы можете использовать модуль re для поиска по файлу с отображением памяти. Поскольку theyre mutable, вы можете изменить один символ, выполнив obj [index] = 'a',..

Ответ 6

Несколько вещей, которые я хотел бы попробовать.

Во-первых, замените замену всем регулярным выражением.

Во-вторых, настройте класс regex char с известным диапазоном управления char вместо этого класса индивидуального управления char.
(Это означает, что двигатель не оптимизирует его для диапазонов.
Диапазон требует двух условий на уровне сборки,
в отличие от отдельных условных для каждого char в классе)

В-третьих, поскольку вы удаляете символы, добавьте жадный квантор
после класса. Это отрицает необходимость вступить в замещение
подпрограмм после каждого одиночного совпадения char, вместо этого захватывая все соседние символы по мере необходимости.

Я не знаю синтаксиса питонов для конструкций регулярных выражений с верхней части головы,
ни все управляющие коды в Юникоде, но результат будет выглядеть как-то например:

[\u0000-\u0009\u000B\u000C\u000E-\u001F\u007F]+

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

При прочих равных условиях регулярное выражение (как описано выше) является самым быстрым способом.