2009-11-15 40 views
10

我需要一个字符串,并将其缩短为140个字符。Python:在单词边界上拆分unicode字符串

目前我做的:

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上,如果有问题的话。

+0

+1有趣的问题 – 2009-11-15 22:42:42

回答

1

与一些本地的粤语,普通话和日语的人沟通后,似乎做了正确的事情是很难的,但我目前的算法仍然对他们有意义的互联网职位的情况下。

含义,它们用于“在空间上分割并在末尾添加...”处理。

因此,我会懒惰,坚持下去,直到我收到不明白的人的投诉。

我原来执行的唯一变化是不强迫上的最后一个字的空间,因为它是在任何语言不需要(和使用Unicode字符的... &#x2026,而不是... three dots保存2个字符)

根据Unicode字符属性数据库,
+0

它是一个HTML中的命名实体:'&hellip;',水平省略号。 – ephemient 2010-01-21 03:45:49

7

中文在单词之间通常不会有空格,而且这些符号根据上下文可以有不同的含义。你将不得不理解文本才能在文字边界处分割它。换句话说,你试图做的事情一般来说并不容易。

+0

对中文字符串进行子串处理有意义吗?就像我做了[:120]'那仍然是可读的吗? – 2009-11-15 21:01:25

+4

你最终可能会用半个字来完全改变意思。想象一下,在前三个字母分裂“协助”。 – 2009-11-15 21:05:34

+0

好的,谢谢。 “...”在其他语言中的意思是否相同,或者是否有替代的“省略号”字符 – 2009-11-15 21:08:25

5

对于中文分词以及处理自然语言的其他高级任务,如果不是完整的解决方案,考虑NLTK是一个很好的起点 - 它是一个基于Python的丰富工具包,特别适合学习NL处理技术(并且很少能够提供给您一些解决这些问题的可行解决方案)。

+3

“不是很少”==通常有时是别的东西? – 2009-11-15 21:12:46

+0

@Laurence,取决于你典型的NL任务是如何出血的,以及你需要你的代码的生产强化和性能优化。如果您要处理TB级文本或需要低延迟响应,那么您必须部署在一个大型,高度可扩展的并行群集上,NLTK最多可以让您勾画原型,而不是为您的要求提供可行的解决方案;为更小容量和更多时间容忍的任务,尤其是,众所周知的分割,“通常”适用 - 但有各种各样的中间需求和特殊问题怪癖! - ) – 2009-11-15 23:40:50

+2

我真的不想训练NLP解决方案来寻找分词符。我确定有人已经这样做了,只想要一个预先装箱的分词器。 – 2009-11-16 23:40:59

0

这使得重新模块的破门决策成为可能,但它对您来说可能已经足够好了。

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 
+0

谢谢。我添加了一个检查,如果没有字界限。对于英文字符串来说,这很好,但对于我的中文例子来说(把它加倍使它变长),我最终得到一个长度为137个字符的字符串,而不是140个。'len(shorten(s * 2,“... end “))' – 2009-11-15 21:38:18

+0

这意味着它按预期工作,因为它在最后\ b \ W中断。然而,我不知道中国人是否知道这实际上是文本中的一个词语。尝试缩短(“abcde”* 3,“”,13)'作为另一个例子,它是如何突破短于极限的。 – 2009-11-15 21:56:08

3

re.U flag将视为\s

给定的字符串,但显然并未按照python的Unicode数据库包含任何空白字符:

>>> 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'] 
+0

正确,但英语中的“空白”表示单词分隔符,因为在中文中没有单词分隔符,只有空格作为句子分隔符。 – 2009-11-16 22:51:55

-1

保存两个字符并使用省略号(0x2026),而不是三个点的!

+1

在UTF-8省略号需要3个字节,所以没有太多的保存在那里:) – 2009-11-16 23:26:39

+2

我故意使用了“字符”而不是“字节”这个词。 :) – 2009-11-16 23:28:23

+1

Adam意思是说:你保存了两个Unicode字符,但是在UTF-8中,U + 2026需要3个字节,而三个点每个需要1个字节,因此当你存储它时没有保存。我的笔记:从概念上讲,最好使用省略号字符。 – 2009-11-17 00:24:49

2

我试用了PyAPNS的推送通知解决方案,只是想分享一下我的工作。我遇到的问题是在UTF-8中以256字节截断会导致通知丢失。我必须确保通知编码为“unicode_escape”才能使其工作。我假设这是因为结果发送为JSON而不是原始的UTF-8。反正这里是为我工作的功能:

def unicode_truncate(s, length, encoding='unicode_escape'): 
    encoded = s.encode(encoding)[:length] 
    return encoded.decode(encoding, 'ignore') 
1

基本上,在CJK(除韩国有空格),就需要字典查询,UPS细分的话正确。根据你对“单词”的确切定义,日语可能比这更难,因为并不是所有的单词变体(即“行こう”与“行った”)都会出现在词典中。是否值得这个努力取决于你的应用。