2011-09-13 2 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バイトの2バイトです。したがって、3または4のオフの問題。

0

Per @ metatationの回答では、オフセットは文字ではなくバイト数です。データベース内のテキストはおそらくUTF8でエンコードされたUnicodeです。この場合、ASCII以外の文字であるの文字は複数のバイトので表されます。非ASCII文字の例には、アクセント記号(à、öなど)、スマート引用符、ラテン文字以外の文字セット(ギリシャ語、キリル文字、アジア系文字セットなど)などがあります。

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; 
} 

警告を::SQLiteデータベースのバイトは、Unicode文字列をUTF8でエンコードされている場合は

あなたはそうのようなオフセットを指定されたバイトのオフセット真のUnicode文字をうまくすることができますが、文字を使用している場合オフセットはNSStringにインデックスされますが、NSStringはUTF-16を使用するため、U + FFFFよりも高いUnicodeコードポイントを持つ文字はのペアの16ビット値で表されます。あなたは一般的にこれに対してテキストコンテンツのためにバンプアップしませんが、特にあいまいな文字セットや、EmojisのようなUnicodeが表すことができるテキスト以外の文字のいくつかを気にしているならば、上記のアルゴリズムはそれらを補うための改善を必要とします。

this project of mineからコードスニペットのは - それを使用して自由に感じる。)このスレッド触発

0

、特にサイモンの溶液。ここで私はそれをやっている。

NSRangeを返すよりもスウィーティーな方法があるかもしれませんが、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) 
    } 
} 
関連する問題