Steuerzeichen aus String filtern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
carknue
User
Beiträge: 12
Registriert: Montag 7. April 2008, 21:22

Hallo,

ich muss aus einem String(wlist[3]) alle nichtdruckbaren Steuerzeichen rausfiltern. Es muss geschwindigkeitsoptimiert sein und ich kann nur Python 2.2 (auf Handy) nutzen. Ich habe mir folgendes überlegt, was auch funktioniert. Aber geht das noch schneller, vielleicht mit der map funktion?

Code: Alles auswählen

string = ""
for zeichen in wlist[3]:
	if ord(zeichen) < 32 or (126 < ord(zeichen) < 160):
		zeichen = ""
	string = string + zeichen
wlist[3] = string
Gruss
Carsten
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Probier mal:

Code: Alles auswählen

def is_not_control(c):
  c = chr(c)
  return not (c < 32 or 126 < c < 160)

filter(is_not_control, my_string)
carknue
User
Beiträge: 12
Registriert: Montag 7. April 2008, 21:22

@ EyDu

ok, statt chr(c) muss es ord(c) heissen aber es funktioniert leider nicht an allen stellen im Programm. Manchmal bekomme ich dann als Ergebnis von der filter funktion nicht den String, sondern die Liste, also statt "Hallo" "[u'H',u'a',u'l',u'l',u'o']"
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Man könnte natürlich daraus ein

Code: Alles auswählen

u"".join(filter(is_not_control, my_string))
machen. Ich bekomme jedoch immer Strings raus. Hast du vielleicht ein Beispiel, wo es bei dir nicht funktioniert.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Code: Alles auswählen

def is_not_control(c):
  c = ord(c)
  return not (c < 32 or 126 < c < 160)

u''.join(filter(is_not_control, my_string))
Dan joint man diese Liste eben wieder zusammen...

€dit:
Ich werd alt und langsam /o\

€dit²:

Code: Alles auswählen

In [14]: def is_not_control(c):       
  c = ord(c)
  return not (c < 32 or 126 < c < 160)
   ....: 

In [17]: 

In [18]: f = functools.partial(filter, is_not_control)

In [19]: f("""dsadsadsadsdsad+#+#%&/()
\t
\t
sadsad
""")
Out[23]: 'dsadsadsadsdsad+#+#%&/()sadsad'
Läuft bei mir prächtig :]
carknue
User
Beiträge: 12
Registriert: Montag 7. April 2008, 21:22

Hmm, ok. Die Frage ist dann, ob das wirklich einen Geschwindigkeitsvorteil bringt? Müsste ich dann mal testen oder kann mir das jemand plausibel erklären? Ich weiß, for Schleifen sind eher langsam... Ich habe meinen Code jetzt mal so gemacht:

Code: Alles auswählen

def is_not_control(input):
	string = ""
	for zeichen in input:
		if ord(zeichen) < 32 or (126 < ord(zeichen) < 160):
			zeichen = ""
		string += zeichen
	return string
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Um das Testen wirst du wohl kaum herumkommen.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

dax@grimbold ~ :) % python bench.py
17.4521019459
18.9652159214
http://paste.pocoo.org/show/38594

Ist also...marginal. Trotzdem ist die filter() Lösung hübscher :D
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Es geht ja nicht darum, wie schnell die Lösung auf irgend einem PC ist, sondern auf einem Handy/Samartphone. Dort verhalten sich einige Operationen ja recht unterschiedlich.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Da auf dem Handy 2.2 läuft, ist vermutlich die filter-Lösung fixer, aber mehr als spekulieren kann ich da auch nicht, das muss carknue schon selbst testen.
carknue
User
Beiträge: 12
Registriert: Montag 7. April 2008, 21:22

Erstmal Danke fürs benchen, auf dem Handy gibt es leider weder functools, noch timeit. Auf meinem PC ist die for Schleife aber einen Tick flotter.
.

IDLE 1.2.1
>>> ================================ RESTART ================================
>>>
8.05010858246
6.94058707765
Und die Schleife noch weiter abgespeckt wird der Unterschied zu gunsten der Schleife noch größer. Oder Interpretiere ich die Ergebnisse falsch?

Code: Alles auswählen

# -*- coding: utf-8 -*-
import functools

def sane_char(c):       
  c = ord(c)
  return not (c < 32 or 126 < c < 160)

f = functools.partial(filter, sane_char) 

def is_not_control(input):
    string = ""
    for zeichen in input:
        input = ord(zeichen)
        if not input < 32 or (126 < input < 160):
          string += zeichen
    return string

if __name__=='__main__':
    from timeit import Timer
    STRING = "FOOO§$fds\t\t\\n\r\nSFD"
    t = Timer("f(STRING)", "from __main__ import STRING, f")
    print t.timeit()
    t = Timer("is_not_control(STRING)", "from __main__ import STRING, is_not_control")
    print t.timeit()
>>>
8.17552013851
5.49103833806
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

carknue hat geschrieben:ich kann nur Python 2.2 (auf Handy) nutzen.
Damit fällt die [mod]functools[/mod] Variante flach ;)

Ich hab da noch einen:

Code: Alles auswählen

# -*- coding: utf-8 -*-

import timeit, string

loop = 100000

WHITELIST_STRING = string.ascii_letters + string.digits


def is_not_control1(input):
    string = ""
    for zeichen in input:
        if ord(zeichen) < 32 or (126 < ord(zeichen) < 160):
            zeichen = ""
        string += zeichen
    return string


def is_not_control2(input):
    string = ""
    for zeichen in input:
        if ord(zeichen) < 32 or (126 < ord(zeichen) < 160):
            continue
        string += zeichen
    return string


def is_not_control3(input):
    result = []
    for zeichen in input:
        if zeichen in WHITELIST_STRING:
            result.append(zeichen)
    return "".join(result)


def is_not_control4(input):
    string = ""
    for zeichen in input:
        if zeichen in WHITELIST_STRING:
            string += zeichen
    return string



if __name__=='__main__':
    TEST_STRING = "FOOO§$fds\\t\\t\\n\\r\\nSFD"

    tests = (
        ("is_not_control1('%s')" % TEST_STRING, "from __main__ import is_not_control1"),
        ("is_not_control2('%s')" % TEST_STRING, "from __main__ import is_not_control2"),
        ("is_not_control3('%s')" % TEST_STRING, "from __main__ import is_not_control3"),
        ("is_not_control4('%s')" % TEST_STRING, "from __main__ import is_not_control4"),
    )

    for no, test in enumerate(tests):
        print "%s - %s" % (no+1, test[0])

        test = timeit.Timer(test[0], test[1])
        print "%.2f" % test.timeit(number=loop)

        print
Ergebnis:
1 - is_not_control1('FOOO§$fds\t\t\n\r\nSFD')
0.85

2 - is_not_control2('FOOO§$fds\t\t\n\r\nSFD')
0.77

3 - is_not_control3('FOOO§$fds\t\t\n\r\nSFD')
0.68

4 - is_not_control4('FOOO§$fds\t\t\n\r\nSFD')
0.45

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

`functools.partial()` kann man ja durch eine ``lambda``-Funktion ersetzen.

Ansonsten würden mir noch Variationen für die ersten beiden Funktionen einfallen: Das `ord()` weglassen und dafür die Zahlen als Zeichen(ketten) schreiben. Also statt ``ord(zeichen) < 32`` zum Beispiel ``zeichen < ' '``. So spart man die `ord()`-Aufrufe.
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Moin,

ich erweitere mal jens' Beispiele:

Code: Alles auswählen

def is_not_control5(input):
    return filter(string.printable.__contains__, input)
Ergebnis auf meiner Maschine:
1 - is_not_control1('FOOO§$fds\t\t\n\r\nSFD')
1.03

2 - is_not_control2('FOOO§$fds\t\t\n\r\nSFD')
0.94

3 - is_not_control3('FOOO§$fds\t\t\n\r\nSFD')
0.93

4 - is_not_control4('FOOO§$fds\t\t\n\r\nSFD')
0.69

5 - is_not_control5('FOOO§$fds\t\t\n\r\nSFD')
0.44
Gruß,
Manuel
Antworten