2009-11-15 39 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細分的話正確。根據你對「單詞」的確切定義,日語可能比這更難,因爲並不是所有的單詞變體(即「行こう」與「行った」)都會出現在詞典中。是否值得這個努力取決於你的應用。