So entfernen Sie JavaScript und andere Tags mit Python… ohne Module zu importieren

Sep 30 2020

Für den ersten Teil des Schulprojekts versuche ich herauszufinden, wie JavaScript <script {...} >und </script {...} >Tags sowie alles zwischen <und entfernt werden können >.

Wir können jedoch keine Module importieren (auch keine in Python eingebauten), da der Marker anscheinend nicht auf sie zugreifen kann usw. usw.

Ich habe es versucht:

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:]

Und das funktioniert für kleinere Dateien, aber das Projekt hat mit großen Textdateien zu tun (die vereinfachte Testdatei, die wir erhalten haben, ist 10,4 MB), sodass es nicht fertig ist und einfach hängen bleibt.

Hat jemand Ideen, um es effizienter zu machen?

Antworten

2 PatrickArtner Sep 30 2020 at 17:33

Sie müssen nichts löschen. In der Tat: Sie möchten niemals Zeichenfolgen ändern.

Zeichenfolgen sind unveränderlich: Jedes Mal, wenn Sie eine "ändern", erstellen Sie stattdessen eine neue und werfen die alte in den Papierkorb. Das ist eine Verschwendung von Prozessor und Speicher.

Sie bearbeiten Dateien - verarbeiten Sie sie also zeichenweise:

  • Denken Sie daran, ob Sie drinnen sind <...>oder nicht
  • Wenn ja, ist es nur wichtig >, wieder nach draußen zu gehen
  • Wenn außen und Charakter ist, <kommst du hinein und ignorierst diesen Charakter
  • Wenn außerhalb und nicht <, schreiben Sie das Zeichen in die Ausgabe (-Datei)

# 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())

Ausgabe:

<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

Wenn Sie nicht direkt mit den Dateien arbeiten dürfen, lesen Sie die Datei in eine Liste, die mehr als 11 MB Speicher belegt:

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))

Dies ist immer noch besser, als iterativ nach dem ersten Auftreten von "<" in der Liste zu suchen, benötigt jedoch möglicherweise bis zum doppelten Speicher Ihrer Quelle (wenn es keine <..> enthält, verdoppeln Sie die Liste).

Das Bedienen der Dateien ist weitaus speichereffizienter als das Ändern von Inplace-Listen (was eine dritte Möglichkeit wäre, dies zu tun).


Es gibt einige krasse Dinge, die Sie auch umgehen müssten, z

<script type="text/javascript">
var i = 10;
if (i < 5) {
  // some code
}
</script>

lässt den "Code" drinnen.


Dies könnte die einfacheren Eckfälle lösen:

# 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 ...
1 woblob Sep 30 2020 at 18:18

Obwohl es 2 while-Schleifen gibt, ist die Komplexität immer noch linear

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)

Ausgänge:

 hello  hello  hey 
1 Tibebes.M Sep 30 2020 at 17:49

Hier ist ein Ansatz mit 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)

Wenn das Parsen der Tags-Semantik erforderlich ist, stellen Sie sicher, dass Sie den Stapel verwenden (kann mit implementiert werden list).


Dies würde die Komplexität erhöhen, aber Sie erreichen eine zuverlässige Überprüfung mit FSM.

siehe folgendes Beispiel:

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)

AUSGABE:

  test string
  another string