Форматирование столбцов, содержащих символы не-ascii

Поэтому я хочу выровнять поля, содержащие символы не-ascii. Не работает следующее:

for word1, word2 in [['hello', 'world'], ['こんにちは', '世界']]: print "{:<20} {:<20}".format(word1, word2) hello world こんにちは 世界 

Есть ли решение?

Вы форматируете многобайтную закодированную строку. Кажется, вы используете UTF-8 для кодирования вашего текста и что для кодирования используется несколько байтов на код (от 1 до 4 в зависимости от конкретного символа). Форматирование строки подсчитывает байты , а не кодовые точки, что является одной из причин, по которым ваши строки заканчиваются смещением:

 >>> len('hello') 5 >>> len('こんにちは') 15 >>> len(u'こんにちは') 5 

Вместо этого форматируйте текст как строки Unicode, чтобы вы могли подсчитывать коды, а не байты:

 for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]: print u"{:<20} {:<20}".format(word1, word2) 

Ваша следующая проблема заключается в том, что эти символы также более широкие, чем большинство; у вас есть двузначные коды:

 >>> import unicodedata >>> unicodedata.east_asian_width(u'h') 'Na' >>> unicodedata.east_asian_width(u'世') 'W' >>> for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]: ... print u"{:<20} {:<20}".format(word1, word2) ... hello worldこんにちは 世界 

str.format() не имеет возможности решать эту проблему; вам придется вручную настроить ширину столбцов до форматирования в зависимости от того, сколько символов зарегистрировано как более широкое в стандарте Unicode.

Это сложно, потому что доступно более одной ширины. См. Стандартное приложение Unicode в Восточной Азии ; есть узкие , широкие и двусмысленные ширины; узкая – это ширина, которую печатает большинство других персонажей, в два раза больше, чем на моем терминале. Неоднозначность … неоднозначна относительно того, насколько широко она будет отображаться:

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

Это зависит от контекста, как они отображаются; Например, греческие символы отображаются как узкие символы в западном тексте, но широко распространены в восточноазиатском контексте. Мой терминал отображает их как узкие, но другие терминалы (например, для восточно-азиатской локали) могут отображать их как широкие. Я не уверен, есть ли какие-нибудь надежные способы выяснить, как это будет работать.

По большей части вам нужно подсчитать символы со значением 'W' или 'F' для unicodedata.east_asian_width() как взятие 2 позиций; вычитайте 1 из вашей ширины формата для каждого из них:

 def calc_width(target, text): return target - sum(unicodedata.east_asian_width(c) in 'WF' for c in text) for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]: print u"{0:<{1}} {2:<{3}}".format(word1, calc_width(20, word1), word2, calc_width(20, word2)) 

Затем это приведет к желаемому выравниванию в моем терминале :

 >>> for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]: ... print u"{0:<{1}} {2:<{3}}".format(word1, calc_width(20, word1), word2, calc_width(20, word2)) ... hello worldこんにちは 世界 

Небольшое смещение, которое вы можете видеть выше, – это ваш браузер или шрифт, использующий разное соотношение ширины (не совсем двойное) для широких кодовых точек.

Все это связано с предостережением: не все терминалы поддерживают свойство Unicode от East-Asian Width и отображают все кодовые точки только по одной ширине.