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... &#x2026 вместо... 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) вместо трех точек!