Как удалить JavaScript и другие теги с помощью Python… без импорта модулей
В первой части школьного проекта я пытаюсь понять, как удалить JavaScript <script {...} >и </script {...} >теги, а также все, что находится между <и >.
Однако мы не можем импортировать какие-либо модули (даже встроенные в Python), потому что, очевидно, маркер не сможет получить к ним доступ и т. Д.
Я пробовал это:
text = "<script beep beep> hello </script boop doop woop> hello <hi> hey <bye>"
while text.find("<script") >= 0:
script_start = text.find("<script")
script_end = text.find(">", text.find("</script")) + 1
text = text[:script_start] + text[script_end:]
while text.find("<") >= 0:
script2_start = text.find("<")
script2_end = text.find(">") + 1
text = text[:script2_start] + text[script2_end:]
И это работает для файлов меньшего размера, но проект имеет отношение к большим текстовым файлам (упрощенный тестовый файл, который нам дали, составляет 10,4 МБ), поэтому он не завершается и просто застревает.
У кого-нибудь есть идеи, чтобы сделать его более эффективным?
Ответы
Удалять ничего не нужно. Фактически: вы никогда не захотите изменять строки.
Строки неизменны: каждый раз, когда вы «изменяете» одну, вы вместо этого создаете новую и удаляете старую. Это пустая трата процессора и памяти.
Вы работаете с файлами - поэтому обрабатывайте их по символам:
- помни, внутри ты
<...>или нет - если так, единственный важный персонаж -
>снова выйти на улицу - если снаружи и персонаж,
<вы попадаете внутрь и игнорируете этого персонажа - если снаружи а не
<вы пишете символ в вывод (-файл)
# create file
with open("somefile.txt","w") as f:
# up the multiplicator to 10000000 to create something in the megabyte range
f.write("<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata\n"*10)
# open file to read from and file to write to
with open("somefile.txt") as f, open("otherfile.txt","w") as out:
# starting outside
inside = False
# we iterate the file line by line
for line in f:
# and each line characterwise
for c in line:
if not inside and c == "<":
inside = True
elif inside and c != ">":
continue
elif inside and c == ">":
inside = False
elif not inside:
# only case to write to out
out.write(c)
print(open("somefile.txt").read() + "\n")
print(open("otherfile.txt").read())
Вывод:
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
hello hello hey tata
Если вам не разрешено напрямую работать с файлами, прочтите файл в список, который занимает 11+ мегабайт памяти:
data = list("<script beep beep> hello </script boop doop woop> hello <hi> hey <bye> tata\n" * 10)
result = []
inside = False
for c in data:
if inside:
if c == ">":
inside = False
# else ignore c - because we are inside
elif c == "<":
inside = True
else:
result.append(c)
print(''.join(result))
это все еще лучше, чем итеративный поиск первого появления «<» в списке, но может потребоваться до двух раз больше памяти вашего источника (если он не содержит никаких <..>, вы удваиваете список).
Работа с файлами намного эффективнее с точки зрения памяти, чем любая модификация списка на месте (это был бы третий способ сделать это).
Есть некоторые вопиющие вещи, которые вам также нужно решить, например,
<script type="text/javascript">
var i = 10;
if (i < 5) {
// some code
}
</script>
оставим «код» внутри.
Это может помочь в более легких угловых случаях:
# open file to read from and file to write to
with open("somefile.txt") as f, open("otherfile.txt","w") as out:
# starting outside
inside = False
insideJS = False
jsStart = 0
# we iterate the file line by line
for line in f:
# string manipulation :/ - will remove <script ...> .. </script ..>
# even over multiple lines - probably missed some cornercases.
while True:
if insideJS and not "</script" in line:
line = ""
break
if "<script" in line:
insideJS = True
jsStart = line.index("<script")
jsEnd = len(line)
elif insideJS:
jsStart = 0
if not insideJS:
break
if "</script" in line:
jsEnd = line.index(">", line.index("</script", jsStart))+1
line = line[:jsStart] + line[jsEnd:]
insideJS = False
else:
line = line[:jsStart]
# and each line characterwise
for c in line:
# ... same as above ...
Несмотря на то, что есть 2 цикла while, это все еще линейная сложность
string = "<script beep beep> hello </script boop doop woop> hello <hi> hey <bye>"
new_string = ''
i = 0
while i < len(string):
if string[i] == "<":
while i < len(string):
i += 1
if string[i] == '>':
break
else:
new_string += string[i]
i += 1
print(new_string)
Выходы:
hello hello hey
Вот один из подходов с FSA :
output = ''
NORMAL, INSIDE_TAG = range(2) # availale states
state = NORMAL # start with normal state
s = '<script beep beep> hello </script boop doop woop> hello <hi id="someid" class="some class"><a> hey </a><bye>'
for char in s:
if char == '<': # if we encounter '<' we enter the INSIDE_TAG state
state = INSIDE_TAG
continue
elif char == '>': # we can safely exit the INSIDE_TAG state
state = NORMAL
continue
if state == NORMAL:
output += char # add the char to the output only if we are in normal state
print(output)
Если требуется синтаксический анализ семантики тегов, обязательно используйте стек (можно реализовать с помощью list).
Это увеличило бы сложность, но вы добьетесь надежной проверки с помощью FSM.
см. следующий пример:
output = ''
(
NORMAL,
TAG_ATTRIBUTE,
INSIDE_JAVASCRIPT,
EXITING_TAG,
BEFORE_TAG_OPENING_OR_ENDING,
TAG_NAME,
ABOUT_TO_EXIT_JS
) = range(7) # availale states
state = NORMAL # start with normal state
tag_name = ''
s = """
<script type="text/javascript">
var i = 10;
if (i < 5) {
// some code
}
</script>
<sometag>
test string
<a href="http://google.com"> another string</a>
</sometag>
"""
for char in s:
# print(char, '-', state, ':', tag_name)
if state == NORMAL:
if char == '<':
state = BEFORE_TAG_OPENING_OR_ENDING
else:
output += char
elif state == BEFORE_TAG_OPENING_OR_ENDING:
if char == '/':
state = EXITING_TAG
else:
tag_name += char
state = TAG_NAME
elif state == TAG_ATTRIBUTE:
if char == '>':
if tag_name == 'script':
state = INSIDE_JAVASCRIPT
else:
state = NORMAL
elif state == TAG_NAME:
if char == ' ':
state = TAG_ATTRIBUTE
elif char == '>':
if tag_name == 'script':
state = INSIDE_JAVASCRIPT
else:
state = NORMAL
else:
tag_name += char
elif state == INSIDE_JAVASCRIPT:
if char == '<':
state = ABOUT_TO_EXIT_JS
else:
pass
# output += char
elif state == ABOUT_TO_EXIT_JS:
if char == '/':
state = EXITING_TAG
tag_name = ''
else:
# output += '<'
state = INSIDE_JAVASCRIPT
elif state == EXITING_TAG:
if char == '>':
state = NORMAL
print(output)
ВЫВОД:
test string
another string