Python: разделение строки юникода на границах слов
Мне нужно взять строку и укоротить ее до 140 символов.
В настоящее время я делаю:
if len(tweet) > 140:
tweet = re.sub(r"\s+", " ", tweet) #normalize space
footer = "… " + utils.shorten_urls(post['url'])
avail = 140 - len(footer)
words = tweet.split()
result = ""
for word in words:
word += " "
if len(word) > avail:
break
result += word
avail -= len(word)
tweet = (result + footer).strip()
assert len(tweet) <= 140
Так что это отлично подходит для английского и английского, как строки, но не подходит для китайской строки, потому что tweet.split()
просто возвращает один массив:
>>> s = u"简讯:新華社報道,美國總統奧巴馬乘坐的「空軍一號」專機晚上10時42分進入上海空域,預計約30分鐘後抵達浦東國際機場,開展他上任後首次訪華之旅。"
>>> s
u'\u7b80\u8baf\uff1a\u65b0\u83ef\u793e\u5831\u9053\uff0c\u7f8e\u570b\u7e3d\u7d71\u5967\u5df4\u99ac\u4e58\u5750\u7684\u300c\u7a7a\u8ecd\u4e00\u865f\u300d\u5c08\u6a5f\u665a\u4e0a10\u664242\u5206\u9032\u5165\u4e0a\u6d77\u7a7a\u57df\uff0c\u9810\u8a08\u7d0430\u5206\u9418\u5f8c\u62b5\u9054\u6d66\u6771\u570b\u969b\u6a5f\u5834\uff0c\u958b\u5c55\u4ed6\u4e0a\u4efb\u5f8c\u9996\u6b21\u8a2a\u83ef\u4e4b\u65c5\u3002'
>>> s.split()
[u'\u7b80\u8baf\uff1a\u65b0\u83ef\u793e\u5831\u9053\uff0c\u7f8e\u570b\u7e3d\u7d71\u5967\u5df4\u99ac\u4e58\u5750\u7684\u300c\u7a7a\u8ecd\u4e00\u865f\u300d\u5c08\u6a5f\u665a\u4e0a10\u664242\u5206\u9032\u5165\u4e0a\u6d77\u7a7a\u57df\uff0c\u9810\u8a08\u7d0430\u5206\u9418\u5f8c\u62b5\u9054\u6d66\u6771\u570b\u969b\u6a5f\u5834\uff0c\u958b\u5c55\u4ed6\u4e0a\u4efb\u5f8c\u9996\u6b21\u8a2a\u83ef\u4e4b\u65c5\u3002']
Как мне это сделать, чтобы он обрабатывал I18N? Это имеет смысл на всех языках?
Я на python 2.5.4, если это имеет значение.
Ответы
Ответ 1
После разговоров с некоторыми родными кантонскими, мандаринскими и японскими ораторами кажется, что правильная вещь - это сложно, но мой текущий алгоритм все еще имеет смысл для них в контексте интернет-сообщений.
Смысл, они используются для "разделения по пространству и добавления... в конце" лечения.
Итак, я собираюсь быть ленивым и придерживаться его, пока не получаю жалобы от людей, которые этого не понимают.
Единственное изменение в моей первоначальной реализации было бы не форсировать пробел на последнем слове, так как он не нужен на любом языке (и используйте символ unicode... …
вместо... three dots
, чтобы сохранить 2 символа )
Ответ 2
У китайцев обычно нет пробелов между словами, и символы могут иметь разные значения в зависимости от контекста. Вам нужно будет понять текст, чтобы разбить его на границе слова. Другими словами, то, что вы пытаетесь сделать, нелегко в целом.
Ответ 3
Для сегментации слов на китайском языке и других сложных задач обработки естественного языка рассмотрите NLTK как хорошую отправную точку, если не полный решение - это богатый набор инструментов на базе Python, особенно полезный для изучения технологий обработки NL (и не редко достаточно хороших, чтобы предложить вам жизнеспособное решение некоторых из этих проблем).
Ответ 4
re.U
flag будет обрабатывать \s
в соответствии с базой данных свойств символов Unicode.
Однако данная строка, по-видимому, не содержит символов пробела в соответствии с базой данных юникода python:
>>> x = u'\u7b80\u8baf\uff1a\u65b0\u83ef\u793e\u5831\u9053\uff0c\u7f8e\u570b\u7e3d\u7d71\u5967\u5df4\u99ac\u4e58\u5750\u7684\u300c\u7a7a\u8ecd\u4e00\u865f\u300d\u5c08\u6a5f\u665a\u4e0a10\u664242\u5206\u9032\u5165\u4e0a\u6d77\u7a7a\u57df\uff0c\u9810\u8a08\u7d0430\u5206\u9418\u5f8c\u62b5\u9054\u6d66\u6771\u570b\u969b\u6a5f\u5834\uff0c\u958b\u5c55\u4ed6\u4e0a\u4efb\u5f8c\u9996\u6b21\u8a2a\u83ef\u4e4b\u65c5\u3002'
>>> re.compile(r'\s+', re.U).split(x)
[u'\u7b80\u8baf\uff1a\u65b0\u83ef\u793e\u5831\u9053\uff0c\u7f8e\u570b\u7e3d\u7d71\u5967\u5df4\u99ac\u4e58\u5750\u7684\u300c\u7a7a\u8ecd\u4e00\u865f\u300d\u5c08\u6a5f\u665a\u4e0a10\u664242\u5206\u9032\u5165\u4e0a\u6d77\u7a7a\u57df\uff0c\u9810\u8a08\u7d0430\u5206\u9418\u5f8c\u62b5\u9054\u6d66\u6771\u570b\u969b\u6a5f\u5834\uff0c\u958b\u5c55\u4ed6\u4e0a\u4efb\u5f8c\u9996\u6b21\u8a2a\u83ef\u4e4b\u65c5\u3002']
Ответ 5
Я попробовал решение с PyAPNS для push-уведомлений и просто хотел поделиться тем, что сработало для меня. Проблема, которую я имел, заключается в том, что усечение в 256 байт в UTF-8 приведет к отбрасыванию уведомления. Я должен был убедиться, что уведомление было закодировано как "unicode_escape", чтобы заставить его работать. Я предполагаю, что это потому, что результат отправляется как JSON, а не raw UTF-8. В любом случае это функция, которая работала для меня:
def unicode_truncate(s, length, encoding='unicode_escape'):
encoded = s.encode(encoding)[:length]
return encoded.decode(encoding, 'ignore')
Ответ 6
В принципе, в CJK (кроме корейского с пробелами) вам нужны словарные словари для правильного сегментации слов. В зависимости от вашего точного определения "слова", японцы могут быть более сложными, так как не все искаженные варианты слова (т.е. "行 こ う" и "行 っ た" ) появятся в словаре. Стоит ли это усилий, зависит от вашего приложения.
Ответ 7
Это отбрасывает слово-решение для модуля re, но оно может работать достаточно хорошо для вас.
import re
def shorten(tweet, footer="", limit=140):
"""Break tweet into two pieces at roughly the last word break
before limit.
"""
lower_break_limit = limit / 2
# limit under which to assume breaking didn't work as expected
limit -= len(footer)
tweet = re.sub(r"\s+", " ", tweet.strip())
m = re.match(r"^(.{,%d})\b(?:\W|$)" % limit, tweet, re.UNICODE)
if not m or m.end(1) < lower_break_limit:
# no suitable word break found
# cutting at an arbitrary location,
# or if len(tweet) < lower_break_limit, this will be true and
# returning this still gives the desired result
return tweet[:limit] + footer
return m.group(1) + footer
Ответ 8
Сохраните два символа и используйте elipsis (…
, 0x2026) вместо трех точек!