Pythonで文字列から印刷不可能な文字を取り除く


88

私は走っています

$s =~ s/[^[:print:]]//g;

Perlでは、印刷不可能な文字を削除します。

PythonにはPOSIX正規表現クラスはなく、[:print:]と書いて、それが私が望むものを意味するようにすることはできません。Pythonで文字が印刷可能かどうかを検出する方法は知りません。

あなたならどうしますか?

編集:Unicode文字もサポートする必要があります。string.printable方法は、それらを出力から削除します。curses.ascii.isprintは、Unicode文字に対してfalseを返します。

回答:


83

文字列の繰り返し処理は、残念ながらPythonではかなり遅くなります。この種の場合、正規表現は1桁以上速くなります。自分でキャラクタークラスを作成する必要があります。UnicodeDataモジュールは、特にこのために非常に便利ですunicodedata.category()関数。カテゴリーの説明については、Unicode文字データベースを参照してください。

import unicodedata, re, itertools, sys

all_chars = (chr(i) for i in range(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
# or equivalently and much more efficiently
control_chars = ''.join(map(chr, itertools.chain(range(0x00,0x20), range(0x7f,0xa0))))

control_char_re = re.compile('[%s]' % re.escape(control_chars))

def remove_control_chars(s):
    return control_char_re.sub('', s)

Python2の場合

import unicodedata, re, sys

all_chars = (unichr(i) for i in xrange(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
# or equivalently and much more efficiently
control_chars = ''.join(map(unichr, range(0x00,0x20) + range(0x7f,0xa0)))

control_char_re = re.compile('[%s]' % re.escape(control_chars))

def remove_control_chars(s):
    return control_char_re.sub('', s)

一部のユースケースでは、追加のカテゴリ(たとえば、コントロールグループのすべてが望ましい場合がありますが、これにより処理時間が遅くなり、メモリ使用量が大幅に増加する可能性があります。カテゴリあたりの文字数:

  • Cc (コントロール):65
  • Cf (フォーマット):161
  • Cs (代理):2048
  • Co (私用):137468
  • Cn (未割り当て):836601

編集コメントからの提案の追加。


4
ここで「Cc」で十分ですか?わかりません。ただ質問しているだけです。他の「C」カテゴリの一部もこのフィルタの候補である可能性があるようです。
Patrick Johnmeyer、2008

1
この関数は、公開されているとおり、ヘブライ語の文字の半分を削除します。私は与えられた方法の両方で同じ効果を得ます。
dotancohen

1
この場合、パフォーマンスの観点から、string.translate()はより高速に動作しませんか?stackoverflow.com/questions/265960/…を
Kashyap

3
all_chars = (unichr(i) for i in xrange(sys.maxunicode))ナロービルドエラーを回避するために使用します。
danmichaelo 2015年

4
私にとってcontrol_chars == '\x00-\x1f\x7f-\x9f'(Python 3.5.2でテスト済み)
AXO

72

私の知る限り、最もpythonic / efficientメソッドは次のようになります。

import string

filtered_string = filter(lambda x: x in string.printable, myStr)

10
あなたはおそらくfiltered_stringたい=「」.join(フィルター(ラムダX:Xあなたは、文字列取り戻すように)、string.printable myStrインチ
ネイサンShivelyの-サンダース

12
悲しいことに、string.printableにはUnicode文字が含まれていないため、出力にüまたはóは表示されません...おそらく他に何かありますか?
Vinko Vrsalovic 2008

17
フィルター+ラムダではなく、リスト内包表記またはジェネレーター式を使用する必要があります。これらのうちの1つは、99.9%の時間で高速になります。'' .join(s in myStr in s if if s in string.printable)
habnabit

3
@AaronGallagher:99.9%速い?どこからその図を摘みますか?パフォーマンスの比較はこれほど悪くはありません。
Chris Morgan

4
こんにちは、ウィリアム。このメソッドは、すべての非ASCII文字を削除するようです。Unicodeには多くの印刷可能な非ASCII文字があります!
dotancohen

17

次のunicodedata.category()関数を使用してフィルタを設定してみてください:

import unicodedata
printable = {'Lu', 'Ll'}
def filter_non_printable(str):
  return ''.join(c for c in str if unicodedata.category(c) in printable)

利用可能なカテゴリについては、Unicodeデータベースの文字プロパティの 175ページの表4-9を参照してください


あなたはあなたの最後の行で終わっていないリストの理解を始めました。開始ブラケットを完全に取り外すことをお勧めします。
tzot 2008

これを指摘していただきありがとうございます。私はそれに応じて投稿を編集しました
Ber

1
これは、最も直接的で簡単な方法のようです。ありがとう。
dotancohen 2013

1
@CsabaToth 3つすべてが有効であり、同じセットを生成します。あなたのはおそらくセットリテラルを指定する最も良い方法です。
Ber

1
@AnubhavJhalaniフィルターにUnicodeカテゴリを追加できます。文字に加えてスペースと数字を予約するには、printable = {'Lu', 'Ll', Zs', 'Nd'}
Ber

10

Python 3では、

def filter_nonprintable(text):
    import itertools
    # Use characters of control category
    nonprintable = itertools.chain(range(0x00,0x20),range(0x7f,0xa0))
    # Use translate to remove all non-printable characters
    return text.translate({character:None for character in nonprintable})

.translate()とregex&.replace()の比較については、句読点の削除に関するこのStackOverflowの投稿を参照してください

範囲は、@ Ants Aasmaで示されnonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc')ているUnicode文字データベースカテゴリを使用して生成できます。


Unicode範囲を使用することをお勧めします(@Ants Aasmaの回答を参照)。結果はになりますtext.translate({c:None for c in itertools.chain(range(0x00,0x20),range(0x7f,0xa0))})
ダークドラゴン

8

以下はUnicode入力で動作し、かなり高速です...

import sys

# build a table mapping all non-printable characters to None
NOPRINT_TRANS_TABLE = {
    i: None for i in range(0, sys.maxunicode + 1) if not chr(i).isprintable()
}

def make_printable(s):
    """Replace non-printable characters in a string."""

    # the translate method on str removes characters
    # that map to None from the string
    return s.translate(NOPRINT_TRANS_TABLE)


assert make_printable('Café') == 'Café'
assert make_printable('\x00\x11Hello') == 'Hello'
assert make_printable('') == ''

私自身のテストでは、このアプローチは文字列を反復処理してを使用して結果を返す関数よりも高速であることを示していstr.joinます。


これが、ユニコード文字で私に有効な唯一の答えです。テストケースを提供してくれて素晴らしい!
ピル

1
あなたは改行を許可したい場合は、追加LINE_BREAK_CHARACTERS = set(["\n", "\r"])してand not chr(i) in LINE_BREAK_CHARACTERSテーブルを作成するとき。
ピル

5

この関数はリスト内包表記とstr.joinを使用するため、O(n ^ 2)ではなく線形時間で実行されます。

from curses.ascii import isprint

def printable(input):
    return ''.join(char for char in input if isprint(char))

2
filter(isprint,input)
2013年

5

Python 3のさらに別のオプション:

re.sub(f'[^{re.escape(string.printable)}]', '', my_string)

これは、私とその1行に非常に役立ちました。感謝
Chop Labalagun

1
何らかの理由で、これはWindowsではうまく機能しますが、Linuxでは使用できません。fをrに変更する必要がありましたが、それが解決策かどうかはわかりません。
チョップLabalagun

Linux Pythonが古すぎてf-stringをサポートできないようです。あなたが言うことができるものの、R-文字列は、かなり異なっていますr'[^' + re.escape(string.printable) + r']'。(私はre.escape()ここでは完全に正しいとは思いませんが、うまくいくなら...)
tripleee

2

私が今思いついた最高のものは(上記のpython-izersのおかげです)

def filter_non_printable(str):
  return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])

これは、Unicode文字/文字列で機能する唯一の方法です。

より良いオプションはありますか?


1
Python 2.3を使用していない限り、内側の[]は冗長です。"return '' .join(c for c ...)"
habnabit '09 / 09/19

まったく冗長ではありません。最終的な結果は同じですが、意味(およびパフォーマンス特性)は異なります。
マイル

範囲のもう一方の端も保護されるべきではありませんか?: "ord(c)<= 126"
Gearoid Murphy

7
ただし、印刷できないUnicode文字もあります。
tripleee

2

以下のものは、上記の他のものよりも高速に実行されます。見てください

''.join([x if x in string.printable else '' for x in Str])

"".join([c if 0x21<=ord(c) and ord(c)<=0x7e else "" for c in ss])
evandrix

2

PythonにはPOSIX正規表現クラスはありません

regexライブラリを使用する場合があります:https : //pypi.org/project/regex/

よくメンテナンスされており、Unicode正規表現、Posix正規表現などをサポートしています。使用法(メソッドシグネチャ)は、Pythonのと非常によく似ていreます。

ドキュメントから:

[[:alpha:]]; [[:^alpha:]]

POSIX文字クラスがサポートされています。これらは通常、の代替形式として扱われ\p{...}ます。

(私はアフィリエイトではなく、ユーザーだけです。)



0

「空白」を削除するには、

import re
t = """
\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>
"""
pat = re.compile(r'[\t\n]')
print(pat.sub("", t))

実際には、角かっこも必要ありません。
tripleee

0

Ants Aasmashawnradの回答からの抜粋

nonprintable = set(map(chr, list(range(0,32)) + list(range(127,160))))
ord_dict = {ord(character):None for character in nonprintable}
def filter_nonprintable(text):
    return text.translate(ord_dict)

#use
str = "this is my string"
str = filter_nonprintable(str)
print(str)

Python 3.7.7でテスト済み

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.