2015-05-11 3 views
13

Previously, я был вычистить данные, используя code snippet нижеЕсть ли более быстрый способ очистки управляющих символов в файле?

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) заменить первые несколько строк (первый столбец это время, и второй столбец 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 текста. Есть ли другой способ очистки контролируемых символов?

+0

Почему вы сохраняете весь файл в памяти? –

+0

Мне нужно выполнить другую обработку позже (мне нужно позже выбрать очищенное предложение на основе некоторых критериев, а затем сделать еще больше обработки выбранного предложения). Память не является проблемой. 'Re.sub' является узким местом – alvas

+0

Вы пытались * не * использовать регулярное выражение, а просто стандартную 'replace'? REs хороши для сложных моделей, но я подозреваю, что замена более эффективна для этого. Кроме того, я попытаюсь найти способ разделить исходный текст 1 ГБ на разделы - это также должно улучшить алгоритм * * *. – jcoppens

ответ

7

нашел решение рабочего характер по 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.

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

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 
+0

вы можете дать некоторое объяснение 'chars = (list (u '%) s '% в нижнем регистре) * 115117) + control_chars', а также 'u'. join (chars [int (random() * len (chars))] для _ в диапазоне (600)) + u '\ n''? – alvas

+0

Да, первый генерирует список символов с прописными буквами и всеми управляющими символами (я просто умножаю на 115117 нижний регистр для размера контрольных символов). В итоге я получаю огромный список символов. Вторая часть состоит в случайном выборе из предыдущего списка заданного количества символов для создания файла. Все это просто для создания файла для проверки того, насколько быстрыми и точными являются функции, которые удаляют управляющие символы ... Я мог бы удалить его из ответа, но думал, что он может помочь вам проверить мое решение или помочь другим ответить быстрее – fransua

0

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

Сначала замените все регулярное выражение заменой.

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

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

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

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

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

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

5

Как и в 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) 

нормально ли это достаточно?

+0

Символ DEL - 0x7F, поэтому 127, вы можете добавить это, если считаете его контрольным символом. –

+0

Это правильно описывает набор управляющих символов. – Ross

4

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

См. Удаление control characters using sed.

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

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

3

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

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

1

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

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

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

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

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

import mmap 

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

Приятная выдержка из документации mmap;

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

Смежные вопросы