简体   繁体   中英

How do I compare characters in custom sqlite collation in objective-c?

I went through lots of questions here on SO (like this one ) but I still need some assistance.

I need my sqlite select to order by slovenian alphabet (letter č comes after c, letter š after s and letter ž after z).

Here is the code I use:

static int sqlite3SloCollate(void * foo, int ll, const void *l, int rl,
                                        const void *r){
    NSString *left = [NSString stringWithCharacters:l length:ll];
    NSString *right = [NSString stringWithCharacters:r length:rl];
    //THIS IS WHERE I DON'T KNOW HOW TO COMPARE CHARACTERS
    NSComparisonResult rs = [left compare:right options:NSForcedOrderingSearch];

    return rs;
}

sqlite3_create_collation(database, "SLOCOLLATE", SQLITE_UTF8, NULL, &sqlite3SloCollate);

querySQL = [NSString stringWithFormat: @"SELECT s.id FROM lyrics l INNER JOIN song s ON (l.idSong=s.id) WHERE content LIKE '%%%@%%' GROUP BY s.id ORDER BY s.title COLLATE SLOCOLLATE;",searchString];

Which NSOrdering type should I use? Or do I have to write my own compare function (can you give me an example)?

I think that this function might help you :

- (NSComparisonResult)compare:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)range locale:(id)locale

(From Apple documentation ).

You can create a locale using :

- (id)initWithLocaleIdentifier:(NSString *)string

(From Apple NSLocale Class Documentation ).

This code should do the trick :

NSRange range = NSMakeRange(0, [left length]);
id locale = [[NSLocale alloc] initWithLocaleIdentifier:@"sl_SI"];
NSComparisonResult rs = [left compare:right options:NSForcedOrderingSearch range:range locale:locale];

I hope this will help.

The @DCMaxxx answer has most of it. Plus the comment that you need to use stringWithUTF8String . But there's some more issues.

1) stringWithUTF8String uses null-terminated c-strings, whilst sqlite is suppling strings with just a length and no null termination.

2) For the number of characters to compare, we need to take the shortest length, not just the left length.

3) When the comparison is equal for the compare, we then need to consider which string is longer.

Full code here. I use an NSMutableData object to convert length coded strings to null terminated strings. It's probably quicker and easier to do it with straight c code, if you are that way inclined.

static int sqlite3SloCollate(void * foo, int ll, const void *l, int rl,
                             const void *r){
    NSMutableData* ld = [NSMutableData dataWithBytes:l length:ll+1];
    [ld resetBytesInRange:NSMakeRange(ll, 1)];
    NSString *left = [NSString stringWithUTF8String:[ld bytes]];

    NSMutableData* rd = [NSMutableData dataWithBytes:r length:rl+1];
    [rd resetBytesInRange:NSMakeRange(rl, 1)];    
    NSString *right = [NSString stringWithUTF8String:[rd bytes]];

    NSRange range = NSMakeRange(0, MIN([left length],[right length]));
    id locale = [[NSLocale alloc] initWithLocaleIdentifier:@"sl_SI"];
    NSComparisonResult result = [left compare:right options:0 range:range locale:locale];

    if (result==NSOrderedSame) {
        if (ll>rl) {
            result = NSOrderedDescending;
        } else if (ll<rl) {
            result = NSOrderedAscending;
        }
    }    
    // NSLog(@"Comparison:%@ - %@ - %li",left,right,(long)result);
    return result;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM