LINUX.ORG.RU

Питон, юникод и конвеер

 ,


0

2

Есть программа test_u.py

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

s = u"Вася"
print u"Hello {0}".format(s)

При этом

$ python test_u.py
Hello Вася
$ python test_u.py | cat
Traceback (most recent call last):
  File "test_u.py", line 7, in <module>
    print u"Hello {0}".format(s)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 6-9: ordinal not in range(128)

Проверял на верссиях 2.6 и 2.7

★★

Ответ на: комментарий от AnDoR

Все нормальные люди перешли уже на 3. Только фрики на 2.

anonymous
()
Ответ на: комментарий от hippi90

Некрофилы должны страдать, такие дела, да.

Внезапно на моем Debian testing

$ python --version
2.7.8
$ cat /etc/debian_version
8.0
Никто не ставит в продакшен третий питон, такие дела.

Yur4eg ★★
() автор топика
Последнее исправление: Yur4eg (всего исправлений: 1)
Ответ на: комментарий от daniilArch

Вся суть в версии питона, скорее всего.

вся суть в том что команда print так работает . :-)

print делает encode в *заведомо-известную* кодировку stdout (sys.stdout.encoding)

но так как в случае pipe — Пайсон не знает какая кодировка , то в этом случае print делает encode с использование *кодировки-поумолчанию* :-D для ситуаций с неизвестной кодировкой..

вот в этом весь и прикол.. тут (далее) раскрывается нюанс :-) :

* устаревший Python-2.X — считает что в случае неизвестной кодировки мы должны всегда использованить ASCII (независимо от того какая у нас локаль в системе..)

* а обычный Python-3.X — считает что в случае неизвестной кодировки мы должны использовать UTF-8 (независимо от того какая у нас локаль в системе.. даже если Eddy_Em разбанят :-) )

# вывод: поведение Python-2.X как обычно черезжопное .. и весь расчёт ведётся на то что пользователь будет вручную делать decode\encode не полагаясь на автоматику (и сверяться с локалью, если нужно). а поведение Python-3.X работает вполне удовлетворительно (так как если мы не знаем какая кодировка то UTF-8 в 99.99%-случаев окажется правдой :))

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 4)
Ответ на: комментарий от loz

Скорее те, кто считает питон юзабельным языком.

Вот только не надо опять начинать питоно-срачь, уже всё обсудили. Питон более чем юзабельный язык, в определенных областях удобнее многих.

hippi90 ★★★★★
()
Ответ на: комментарий от user_id_68054

но так как в случае pipe — Пайсон не знает какая кодировка , то в этом случае print делает encode с использование *кодировки-поумолчанию* :-D для ситуаций с неизвестной кодировкой..

и здесь я немного уточню..

например вы можете возрозить "но как же не знает?! почему Пайсон не может просто взять кодировку из информации Локале?"

ответ:

потому что Пайсон же понятия не имеет что этот "... | cat ..." выдаст информацию на экран! (ды и вообще — Пайсон и даже не знает что он вообще подключён к "cat" !)

Пайсон просто передаёт байты по PIPE без понятия о том кто это случает на другой стороне :-) ..

следовательно говорить об *автоматическом* использовании кодировки из локали — тут совершенно неуместно!

user_id_68054 ★★★★★
()

Объясните нубасу, зачем u"строка"?

$ python2 -c 's="ЛОР"
> print "Ололо привет {0}".format(s)' | cat
Ололо привет ЛОР
$ cat testlor 
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
s="ЛОР"
print "Ололо привет {0}".format(s)

$ ./testlor | cat
Ололо привет ЛОР
alozovskoy ★★★★★
()
Ответ на: комментарий от user_id_68054

apr-get работает как часы, зависимости иногда подглючивают, но редко. последний libreoffice не очень работает с xmonad, но это проблема маргинальных оконных менеджеров: их никто не хочет поддерживать.

Возвращаясь к вопросу и для тех кто прийдет с поисковиков

import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
фиксит программы. На самом деле отдать по конвееру результат это очень редкая вещь, мне за три года как пишу на питоне первый раз понадобилась

Yur4eg ★★
() автор топика
Ответ на: комментарий от alozovskoy

Если ты получаешь свое s из файла, то по питон-теории ты должен его преобразовать в кодировку на которой написан твой скрипт. Про методы encode и decode у строк слышал же? Чтобы не заграмождать код, можно сказать принудительно, что вот строка s=u" в юникоде. У тебя нет юникода вот по этому и работает.

Yur4eg ★★
() автор топика
Ответ на: комментарий от Yur4eg

вообще хорошего *универсального* решения тут не думаю что можно придумать :)).

так как — вдруг stdout должен быть в другой кодеровке (не UTF-8) ? и должны ли мы хардкодно указывать UTF-8 или же должны ли мы взять значение из локали? это зависит от задачи..

но в целом — как костыль — это норм.

и можно этот костыль даже слегка улучшить:

import sys
import codecs
if not getattr(sys.stdout, 'encoding', None):
    sys.stdout = codecs.getwriter('utf8')(sys.stdout)

ну или сделать примерно-где-то-вот-так

import sys

def safe_print(*args, **kwargs):
    sep=kwargs.get('sep', u' ')
    end=kwargs.get('end', u'\n')
    file=kwargs.get('file', sys.stdout)
    
    def safe_conv(value):
        encoding = getattr(file, 'encoding', None) or 'utf-8'
        
        if isinstance(value, bytes):
            safe_value = value
        elif isinstance(value, unicode):
            safe_value = value.encode(encoding, 'replace')
        else:
            safe_value = unicode(value).encode(encoding, 'replace')
        
        return safe_value
    
    print_str = safe_conv(sep).join(safe_conv(v) for v in args) + safe_conv(end)
    
    file.write(print_str)
    file.flush()

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 2)
Ответ на: комментарий от alozovskoy

Зависит от версии, но в общем ответ нет. Да и с какой радости? О чем и тред

Yur4eg ★★
() автор топика

и вся головная боль от этого Python-2.X — что нужно делать НЕ только "safe_print" [ см.. Питон, юникод и конвеер (комментарий) ]...

...но и вообще чуть ли не для каждой стандартной функции делать подобные врапперы, которые бы исправляли бы встроенное поведение на более адекватное :-)

так что «батарейки в комплекте» — слегка своеобразные :-)

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 2)
Ответ на: комментарий от user_id_68054

Мне вообще интересно, какой сумрачный гений додумался до того, что кодировки при выводе на stdout и в файл/pipe должны различаться? В этм плане убогость второй и третьей веток абсолютно одинакова.

Для кого, черт возьми, LC_CTYPE задается?!

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

Мне вообще интересно, какой сумрачный гений додумался до того, что кодировки при выводе на stdout и в файл/pipe должны различаться? В этм плане убогость второй и третьей веток абсолютно одинакова.

Для кого, черт возьми, LC_CTYPE задается?!

вот поэтому я дополнительно и объяснил(*) :-)

с какого перепуга мы (то есть — Пайсон) должны пихать в PIPE именно то что красиво читается на дисплее пользователя (LC_CTYPE) ?

а вдруг PIPE предназначен НЕ для спросмотра его на дисплее? :-)

думаю вполне логично представлять PIPE как некий кросплатформеный канал (в отличии от того что печатается на мониторе) , ведь данные передаваемые по PIPE теоретически могут попасть и на другие компьютеры и на другие операционные системы..

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 1)
Ответ на: комментарий от user_id_68054

с какого перепуга мы (то есть — Пайсон) должны пихать в PIPE именно то что красиво читается на дисплее пользователя (LC_CTYPE) ?

Как тебе такая аналогия: я пишу

printf("%d\n", 256);
И оно выводит число «256». А если перенаправить вывод в файл, там будет «0xff». Питон ведет себя именно так.

Потому как CTYPE, в частности, влияет на то, как будут воспринимать ввод некоторые приложения. Он един для системы. Попытка в третьем питоне пропихнуть свой юникод во все щели тоже ни к чему хорошему не приводит.

Да и вообще, в 95% задач лучше, если язык считает строки «бинарными массивами переменной длины». Но из-за оставшихся 5%, когда нужнен юникодный collate и casefolding, страдают все.

ведь данные передаваемые по PIPE теоретически могут попасть и на другие компьютеры и на другие операционные системы

Это, кстати, аргумент в пользу «считать все строки потоком бинарных данных» и никоим образом не лезть в их начинку.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

Считать строки бинарными данными можно только если ты их тупо, например, пересылаешь из одного места в другое, что происходит весьма редко.

PolarFox ★★★★★
()
Ответ на: комментарий от PolarFox

Считать строки бинарными данными можно только если ты их тупо, например, пересылаешь из одного места в другое, что происходит весьма редко.

А также в подавляющих случаях фильтрации. Я, например, не припомню, чтобы мне когда-то был нужен регистронезависимый grep не по латинице.

Вообще «поддержка юникода на уровне языка программирования» очень сильно переоценена. Гораздо проще иметь библиотеку для collation, casefolding и разбиения на символы, чем на каждом шагу сталкиваться с тем, что произвольный набор байт в строку так легко не положить/не распечатать.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

Это, кстати, аргумент в пользу «считать все строки потоком бинарных данных» и никоим образом не лезть в их начинку.

вот наверно поэтому в Python-3.X придумали два разны объекта:

«sys.stdout» — для вывода на дисплей..

и

«sys.stdout.buffer» — для всяких там PIPE и прочиго бинарного..

ну а уж когда мы (программисты) вместо «sys.stdout.buffer» используем «sys.stdout» [ а функция «print» как раз подразумевает «sys.stdout» ] — вот в этом случае мы (программисты) сами виноваты.

по сути Python3 только слегка разряжает обстановку в случае такой программистской ошибки (например программист писал программу для вывода на дисплей, а пользователь программы рашил заиспользовать запись потока в бинарный файл и передать результат через интернет)

Попытка в третьем питоне пропихнуть свой юникод во все щели тоже ни к чему хорошему не приводит.

по хорошему самым правильный способом — было бы просто СРАЗУ вывод ОШИБКИ если программист пытается в PIPE пропихнуть текстовый текст :-)

но вот решили в Python3 пощадить программистов и пропихнуть UTF-8 :-) .. не совсем идеологически правильно (быть может?!) но зато подходит под большенство use case и одновременно не создаюёт путанницу кодировок :-)

user_id_68054 ★★★★★
()
Ответ на: комментарий от kawaii_neko
$ echo привет | tr п ы
ырѸѲѵт

Как только проводишь любое действие с отдельными символами (поиск подстроки, замена итд), в случае с utf-8, коллекцию байтов нужно декодить в строку (коллекцию символов).

PolarFox ★★★★★
()
Ответ на: комментарий от user_id_68054

Питон 3 utf-8 никоим образом не пропихивает (разве что теперь исходник по-дефолту считается текстом в utf-8). Он просто перестал неявно енкодить строки в байты и декодить байты в строки.

PolarFox ★★★★★
()
Последнее исправление: PolarFox (всего исправлений: 1)
Ответ на: комментарий от PolarFox

Питон 3 utf-8 никоим образом не пропихивает (разве что теперь исходник по-дефолту считается текстом в utf-8). Он просто перестал неявно енкодить строки в байты и декодить байты в строки.

вот тебе пример:

read_fd = open('in.txt', mode='rt') # открыть файл -- на чтение в режиме текста (используется кодировка в зависимости от Локали)
write_fd = open('out.txt', mode='wb')  # открыть файл -- на запись в режиме бинарника

text = read_fd.read()
binary = text.encode() # ВОТ ТУТ -- что происходит? какая кодировка?

write_fd.write(binary)

суть этого примера в том что Пайсон третьей версии — использует UTF-8 во всех тех моментах когда у Пайсона нет инфы о том какую кодировку использовать.

и это касается как видишь не только вопроса о кодировке программного кода :-)

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 6)
Ответ на: комментарий от user_id_68054

UTF-8 сделали дефолтной кодировкой, да. Но неявное преобразование между байтами и строками убрали.

Питон2: пишешь тупой код который работает, но потом внезапно взрывается на неASCII-вводе, и приходится выискивать все те места, где были неявные преобразования между string и unicode.

Питон3: теперь программисту нужно держать в голове отдельные концепты строк и байтов (даже если ему нужно лишь ASCII, где эти концепты совпадают), но UTF-8 сделали кодировкой по умолчанию.

PolarFox ★★★★★
()
Ответ на: комментарий от kawaii_neko

Мне вообще интересно, какой сумрачный гений додумался до того, что кодировки при выводе на stdout и в файл/pipe должны различаться?

разработчики windows
для логгера например:

if sys.stdout.isatty():
	console_handler = logging.StreamHandler()
	console_handler.setLevel(logging.DEBUG)
	if sys.platform.startswith('win'):
		reload(sys)
		sys.setdefaultencoding('cp1251')
		sys.stdout = codecs.getwriter('cp866')(sys.stdout,'replace')
		windows_format = logging.Formatter(u'%(levelname)-8s [%(asctime)s]  %(message)s'.encode('cp1251'))

system-root ★★★★★
()
% cat lor.rb 
#! /usr/bin/env ruby

puts 'Привет, ЛОР!'   
% ./lor.rb 
Привет, ЛОР!  
 ./lor.rb | cat
Привет, ЛОР!  
% /usr/bin/env ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
blackst0ne ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.