Уведомления

Группа в Telegram: @pythonsu

#1 Авг. 8, 2014 14:40:06

_alexs_
Зарегистрирован: 2012-04-02
Сообщения: 42
Репутация: +  0  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

Для запуска стороннего консольного приложения из своей программы использую модуль subprocess. Код, отвечающий за запуск сейчас выглядит так (commands — список, содержащий команду и её аргументы):

cmd = ''.join(['%s ' % c for c in commands])
proc = subprocess.Popen(
    cmd,
    shell=True,
    stdout=subprocess.PIPE,
    stdin=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    universal_newlines=False,
    ).stdout
Но если аргументы содержат non-ASCII символы (кириллицу, умляуты или вообще иероглифы), то Popen аварийно завершается с ошибкой «UnicodeEncodeError: ‘ascii’ codec can't encode character u'\xf3' in position 237: ordinal not in range(128)». Причем только в Windows, в Linux проблем нет.

Гугл подсказал, что модуль subprocess в Python 2.7.х не умеет работать с юникодными строками. В качестве workaround (например тут и тут) предлагают использовать перекодирование в кодировку файловой системы:
fse = sys.getfilesystemencoding()
cmd = ''.join(['%s ' % c.encode(fse) if isinstance(c, unicode) else c for c in commands])
proc = subprocess.Popen(
    cmd,
    shell=True,
    stdout=subprocess.PIPE,
    stdin=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    universal_newlines=False,
    ).stdout

К сожалению, у меня этот трюк не сработал, точнее сработал как-то не так. Ошибка UnicodeEncodeError ушла, но пути стали «испорченными»: например было «D:\проба\relief.dat» стало «D:\i?iaa\relief.dat». При этом getfilesystemencoding() возвращает «mbcs». Чувствую, что упускаю какую-то мелочь из виду, но не могу сообразить какую именно.

Да, нужно кроссплатформенное и независимое от кодировок решение, которое будет работать и в Linux, и в Windows с любыми символами (кириллица, умляуты и т.д.). Python 2.7.х, перейти на тройку пока нет возможности.

Офлайн

#2 Авг. 9, 2014 00:29:52

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10015
Репутация: +  857  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

#!/usr/bin/env python
# coding: utf-8
  
import subprocess as subp
  
p = subp.Popen([u'echo', u'x', u'б'], stdout=subp.PIPE)
p.wait()
print (p.stdout.read(), p.returncode)

Такой попробуй запустить.



Отредактировано py.user.next (Авг. 9, 2014 10:24:55)

Офлайн

#3 Авг. 10, 2014 09:47:12

_alexs_
Зарегистрирован: 2012-04-02
Сообщения: 42
Репутация: +  0  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

В Linux работает, вывод ниже

('x \xd0\xb1\n', 0)
В Windows падает с UnicodeEncodeError
Traceback (most recent call last):
File "d:\test.py", line 6, in <module>
p = subp.Popen([u'echo', u'x', u'¦-'], stdout=subp.PIPE)
File "C:\OSGEO4~1\apps\Python27\lib\subprocess.py", line 711, in __init__ errread, errwrite)
File "C:\OSGEO4~1\apps\Python27\lib\subprocess.py", line 948, in _execute_child startupinfo)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0431' in position 7: ordinal not in range(128)

Офлайн

#4 Авг. 10, 2014 12:45:35

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10015
Репутация: +  857  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

Поковырялся - дохлый номер. Винда.

import os
 
with os.popen('echo x б') as p:
    print p.read()



Отредактировано py.user.next (Авг. 10, 2014 12:46:41)

Офлайн

#5 Авг. 10, 2014 13:49:14

Kasta_neda
Зарегистрирован: 2014-06-08
Сообщения: 210
Репутация: +  6  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

import os
 
with os.popen('echo x б') as p:
    print p.read().decode('cp866')
Если кроссплатформенное, то возможно придется делать проверку на ось, и запускать участок кода под эту ось, потому как с кодировкой в винде под 2 питон вечные затыки

Офлайн

#6 Авг. 10, 2014 14:01:19

Kasta_neda
Зарегистрирован: 2014-06-08
Сообщения: 210
Репутация: +  6  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

#!/usr/bin/env python
# coding: utf-8
  
import subprocess
 
 
proc = subprocess.Popen(['echo', 'д'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = ' '
while out:
    out = proc.stdout.readline()
    print(out.rstrip().decode('cp866'))

Офлайн

#7 Авг. 10, 2014 23:46:01

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10015
Репутация: +  857  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

Код Kasta_neda выводит не то, если у файла кодировка utf-8. Если файл в кодировке cp1251 (даже если декларирует utf-8), нормально выводит. Если же файл в utf-8 и декларируется utf-8, то символы правильно распознаются (в обычных строках), а subprocess выдаёт proc.stdout с вопросами.



Отредактировано py.user.next (Авг. 10, 2014 23:46:49)

Офлайн

#8 Авг. 11, 2014 06:22:20

Kasta_neda
Зарегистрирован: 2014-06-08
Сообщения: 210
Репутация: +  6  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

Да, файл в кодировке cp1251 должен быть сохранен, или сохранен в utf-8 а декларирован в cp1251.

Отредактировано Kasta_neda (Авг. 11, 2014 06:25:57)

Офлайн

#9 Авг. 11, 2014 09:44:20

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10015
Репутация: +  857  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

Kasta_neda
или сохранен в utf-8 а декларирован в cp1251

Не, в чём он записан, в том он и должен быть декларирован. Она (декларация) для того и придумана, чтобы питон мог раскодировать правильно.



Отредактировано py.user.next (Авг. 11, 2014 09:44:45)

Офлайн

#10 Авг. 11, 2014 12:21:32

_alexs_
Зарегистрирован: 2012-04-02
Сообщения: 42
Репутация: +  0  -
Профиль   Отправить e-mail  

subprocess и non-ASCII символы

py.user.next
Если файл в кодировке cp1251 (даже если декларирует utf-8), нормально выводит. Если же файл в utf-8 и декларируется utf-8, т
У меня все файлы имеют кодировку utf-8, менять её на cp1251 не вариант.

Похоже решения таки нет, жаль. Спасибо за помощь.

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version