2011-09-13 37 views
4

短消息: 我有一個UTF NSString和一個字節偏移量。我想知道該字節偏移處的字符。我能怎麼做?從字節偏移量中檢測UTF NSString中的字符位置(是SQLite偏移量()和編碼問題)

下面是一個很長的故事,如果你敢:

根據this文檔偏移()函數返回字節項的列中的偏移。我編制了一些文本索引,並在顯示結果時使用該偏移指向文本的特定部分。

關鍵問題是使用這個字節偏移量我無法指出該術語的正確位置。有時它指向正確,有時距離正確點3/4字符。

我的表是非常簡單的:

CREATE VIRTUAL TABLE t1 USING fts4(file, body, page); 

如果我做一個查詢,如:

SELECT page, body, offsets(t1) from t1 where body match 'and'; 

我收到:

........... 
502|1 0 427 3 
505|1 0 370 3 1 0 1307 3 1 0 1768 3 
506|1 0 10 3 1 0 1861 3 1 0 2521 3 

........... 

舉個例子,如果我點爲char身體的427我沒有得到'和'的正確位置,但是我跳了2/3個字符。如果我去370,同樣如果我去10,我會得到正確的位置。

我在哪裏錯了?

回答

0

查看Sqlite FTS3 docs,你會注意到偏移和長度在字節不是字符。

在將字節解碼爲字符串之前,您必須應用偏移和長度才能顯示正確的偏移量。來自Sqlite的偏移量會計算多字節字符的每個字節,而您使用該偏移量來計算個字符

您的索引文本可能有3或4個字符,它們是兩個字節。因此,這是3或4的問題。

0

Per @ metatation的答案,偏移量以字節爲單位,而不是字符。數據庫中的文本可能是UTF8編碼的Unicode,在這種情況下,任何單個非ASCII 字符都由多個字節表示。非ASCII字符的示例包括帶有重音符號(à,ö等)的字符,智能引號,來自非拉丁字符集(希臘語,西里爾語,大多數亞洲字符集等)的字符等等。

如果SQLite數據庫的字節UTF8編碼的Unicode字符串就可以制定出真正的Unicode字符爲給定的字節偏移抵消像這樣:

NSUInteger characterOffsetForByteOffsetInUTF8String(NSUInteger byteOffset, const char *string) { 
    /* 
    * UTF-8 represents ASCII characters in a single byte. Characters with a code 
    * point from U+0080 upwards are represented as multiple bytes. The first byte 
    * always has the two most significant bits set (i.e. 11xxxxxx). All subsequent 
    * bytes have the most significant bit set, the next most significant bit unset 
    * (i.e. 10xxxxxx). 
    * 
    * We use that here to determine character offsets. We step through the first 
    * `byteOffset` bytes of `string`, incrementing the character offset result 
    * every time we come across a byte that doesn't match 10xxxxxx, i.e. where 
    * (byte & 11000000) != 10000000 
    * 
    * See also: http://en.wikipedia.org/wiki/UTF-8#Description 
    */ 
    NSUInteger characterOffset = 0; 
    for (NSUInteger i = 0; i < byteOffset; i++) { 
     char c = string[i]; 
     if ((c & 0xc0) != 0x80) { 
      characterOffset++; 
     } 
    } 
    return characterOffset; 
} 

警告:如果您使用的字符偏移索引到NSString,請記住NSString在引擎蓋下使用UTF-16,所以Unicode代碼點高於U + FFFF的字符由對的(16位值)表示。你通常不會碰到這個文本內容,但如果你關心特別模糊的字符集,或者一些非文本字符Unicode可以表示如Emojis,那麼上面的算法將需要改進以迎合這些。

(代碼段從this project of mine的 - 隨時自由地使用它。)

0

通過這個線程啓發,和西蒙在特定的解決方案;這是我如何做到的。

可能比返回NSRange更「Swifty」的方式,但我需要它來突出顯示NSAttributedString

extension String { 

    func charRangeForByteRange(range : NSRange) -> NSRange { 

     let bytes = [UInt8](utf8) 

     var charOffset = 0 

     for i in 0..<range.location { 
      if ((bytes[i] & 0xc0) != 0x80) { charOffset++ } 
     } 

     let location = charOffset 

     for i in range.location..<(range.location + range.length) { 
      if ((bytes[i] & 0xc0) != 0x80) { charOffset++ } 
     } 

     let length = charOffset - location 

     return NSMakeRange(location, length) 
    } 
}