Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: Peter Otten <__peter__@web.de> Newsgroups: comp.lang.python Subject: Re: Processing text data with different encodings Date: Tue, 28 Jun 2016 10:30:20 +0200 Organization: None Lines: 84 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8Bit X-Trace: news.uni-berlin.de CJxXA6Y432EaS3luKhxR5Q0+AMIFhy1fJxrCKygagr/g== Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'handler': 0.04; 'string.': 0.04; 'subject:text': 0.04; 'python3': 0.05; 'line:': 0.07; 'override': 0.07; 'utf-8': 0.07; 'encode': 0.09; 'logger': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'tackle': 0.09; 'example:': 0.10; 'python': 0.10; 'def': 0.13; 'ignore': 0.14; 'encoding': 0.15; 'skip:f 30': 0.15; '(assuming': 0.16; 'arguments:': 0.16; 'ascii,': 0.16; 'codec': 0.16; 'decode': 0.16; 'emit': 0.16; "file's": 0.16; 'input:': 0.16; 'ordinal': 0.16; 'received:80.91.229.3': 0.16; 'received:dip0.t-ipconnect.de': 0.16; 'received:io': 0.16; 'received:plane.gmane.org': 0.16; 'received:psf.io': 0.16; 'received:t-ipconnect.de': 0.16; 'stack:': 0.16; 'wrote:': 0.16; 'detect': 0.18; 'input': 0.18; 'skip:" 40': 0.20; 'suggested': 0.20; 'trying': 0.22; 'skip:l 40': 0.23; 'import': 0.24; '(most': 0.24; 'module': 0.25; 'header:User- Agent:1': 0.26; 'header:X-Complaints-To:1': 0.26; 'error': 0.27; 'handling': 0.27; 'logging': 0.27; 'data,': 0.27; 'correct': 0.28; 'idea': 0.28; '---': 0.28; 'looks': 0.29; 'cat': 0.29; 'character.': 0.29; 'preceding': 0.29; 'character': 0.29; 'minimal': 0.30; "can't": 0.32; 'maybe': 0.33; 'class': 0.33; 'problem': 0.33; 'michael': 0.33; 'traceback': 0.33; 'file': 0.34; 'skip:c 30': 0.35; 'next': 0.35; 'unicode': 0.35; 'something': 0.35; 'should': 0.36; 'to:addr:python-list': 0.36; 'subject:: ': 0.37; 'expect': 0.37; 'received:org': 0.37; 'data': 0.39; 'does': 0.39; "didn't": 0.39; 'skip:e 20': 0.39; 'to:addr:python.org': 0.40; 'subject:with': 0.40; 'received:de': 0.40; 'hello,': 0.40; 'some': 0.40; 'ever': 0.60; 'skip:u 10': 0.61; 'real': 0.62; 'course': 0.62; 'more': 0.63; 'different': 0.63; 'else.': 0.66; 'here': 0.66; 'therefore': 0.67; 'strategy': 0.69; "(don't": 0.84; 'welle': 0.84 X-Injected-Via-Gmane: http://gmane.org/ X-Gmane-NNTP-Posting-Host: p57bd8677.dip0.t-ipconnect.de User-Agent: KNode/4.13.3 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Mailman-Original-Message-ID: X-Mailman-Original-References: Xref: csiph.com comp.lang.python:110670 Michael Welle wrote: > Hello, > > I want to use Python 3 to process data, that unfortunately come with > different encodings. So far I have found ascii, iso-8859, utf-8, > windows-1252 and maybe some more in the same file (don't ask...). I read > the data via sys.stdin and the idea is to read a line, detect the > current encoding, hit it until it looks like utf-8 and then go on with > the next line of input: > > > import cchardet > > for line in sys.stdin.buffer: > > encoding = cchardet.detect(line)['encoding'] > line = line.decode(encoding, 'ignore')\ > .encode('UTF-8').decode('UTF-8', 'ignore') Here the last decode('UTF-8', 'ignore') undoes the preceding encode('UTF-8'); therefore line = line.decode(encoding, 'ignore') should suffice. Does chardet ever return an encoding that fails to decode the line? Only in that case the "ignore" error handler would make sense. I expect that for line in sys.stdin.buffer: encoding = cchardet.detect(line)['encoding'] line = line.decode(encoding) will work if you don't want to use the alternative suggested by Chris. > After that line should be a string. The logging module and some others > choke on line: UnicodeEncodeError: 'charmap' codec can't encode > character. What would be a right approach to tackle that problem > (assuming that I can't change the input data)? It looks like you are trying to write the unicode you have generated above into a file using iso-8859-1 or similar: $ cat log_unicode.py import logging LOGGER = logging.getLogger() LOGGER.addHandler(logging.FileHandler("tmp.txt", encoding="ISO-8859-1")) LOGGER.critical("\N{PILE OF POO}") $ python3 log_unicode.py --- Logging error --- Traceback (most recent call last): File "/usr/lib/python3.4/logging/__init__.py", line 980, in emit stream.write(msg) UnicodeEncodeError: 'latin-1' codec can't encode character '\U0001f4a9' in position 0: ordinal not in range(256) Call stack: File "log_unicode.py", line 5, in LOGGER.critical("\N{PILE OF POO}") Message: '💩' Arguments: () If my assumption is correct you can either change the target file's encoding to UTF-8 or change the error handling strategy to ignore or something else. I didn't find an official way, so here's a minimal example: $ rm tmp.txt $ cat log_unicode.py import logging class FileHandler(logging.FileHandler): def _open(self): return open( self.baseFilename, self.mode, encoding=self.encoding, errors="xmlcharrefreplace") LOGGER = logging.getLogger() LOGGER.addHandler(FileHandler("tmp.txt", encoding="ISO-8859-1")) LOGGER.critical("\N{PILE OF POO}") $ python3 log_unicode.py $ cat tmp.txt 💩 A real program would of course override the initializer...