Seite 1 von 1

Steuerzeichen aus String filtern

Verfasst: Montag 7. April 2008, 21:39
von carknue
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

Verfasst: Montag 7. April 2008, 22:01
von EyDu
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)

Verfasst: Montag 7. April 2008, 23:34
von carknue
@ 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']"

Verfasst: Montag 7. April 2008, 23:41
von EyDu
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.

Verfasst: Montag 7. April 2008, 23:42
von audax

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

Verfasst: Montag 7. April 2008, 23:59
von carknue
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

Verfasst: Dienstag 8. April 2008, 00:50
von EyDu
Um das Testen wirst du wohl kaum herumkommen.

Verfasst: Dienstag 8. April 2008, 06:23
von audax
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

Verfasst: Dienstag 8. April 2008, 13:24
von EyDu
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.

Verfasst: Dienstag 8. April 2008, 16:26
von audax
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.

Verfasst: Dienstag 8. April 2008, 20:13
von carknue
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

Re: Steuerzeichen aus String filtern

Verfasst: Mittwoch 9. April 2008, 08:54
von jens
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

Verfasst: Mittwoch 9. April 2008, 11:33
von 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.

Verfasst: Mittwoch 9. April 2008, 14:14
von helduel
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