NSScanner на основе последнего вхождения подстроки @

У меня есть система имен пользователей, похожая на твиттеры, использующая символ @.

Я настроил его для отправки сообщений пользователям, набрав @username. Это в основном идентично упоминаниям в Твиттере.

До сих пор мне удавалось обнаруживать текст, следующий за символом @, и прекращать обнаружение, когда он попадает в пробел. Это было прекрасно сделано с помощью NSScanner.

Моя проблема в том, что если я хочу отправить сообщение нескольким @usernames, NSScanner обнаружит только первое вхождение. Мне нужно, чтобы обнаружить последнее событие. Я нашел это как полезный инструмент:

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];

Я не могу понять, как правильно использовать это в сочетании с NSScanner. Вот 2 примера моих попыток кода.

Пример 1:

NSString *user = nil;
NSString *text = textView.text;

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];

if (lastSymbol.location != NSNotFound) {

    NSScanner *userScanner = [NSScanner scannerWithString:text];
    [userScanner scanUpToString:@"@" intoString:nil];

    NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@"@"];

    if (![userScanner isAtEnd]) {

        [userScanner scanUpToCharactersFromSet:charset intoString:nil];
        [userScanner scanCharactersFromSet:charset intoString:nil]
        [userScanner scanUpToCharactersFromSet:charset intoString:&user];


    }
}


if (user.length > 0) {

    NSRange whiteSpaceRange = [user rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
    if (whiteSpaceRange.location != NSNotFound) {

        // White space, username has ended

    } else {

        // Still getting username
        NSLog(@"%@", user);

    }

}

Как видите, я просто не совсем уверен, как заставить их работать вместе, чтобы получить результаты, которые я ищу. Любые идеи? Спасибо!

**************************ОБНОВИТЬ*********************** *****

Чтобы быть более конкретным, когда пользователь вводит символ @ и начинает вводить имя пользователя, я хочу отобразить список его друзей на основе того, что они вводят. Поэтому, естественно, я хочу вытащить строку, следующую за символом @, и по мере того, как пользователь продолжает печатать, обновлять список своих друзей. Как только пользователь вводит пробел, мы предполагаем, что он закончил вводить имя пользователя (имена пользователей не могут содержать пробелы), поэтому я перестаю отображать табличное представление.

Вышеприведенный код до сих пор просто обнаруживает и извлекает строку, следующую за символом @. Вот результат прямо сейчас:

Текст текстового поля: Привет @username

введите здесь описание изображения

Это отлично работает, как видите, я могу продолжать сверять этот текст со множеством друзей.

Допустим, пользователь хочет отправить сообщение другому пользователю в том же тексте, например:

Привет @username, это мой друг @goodfriend

На этот раз он по-прежнему отображает первое @username, но игнорирует второе @goodfriend.

Я полагаю, что лучший способ сделать это — как-то сказать NSScanner, чтобы он ссылался на ПОСЛЕДНЕЕ вхождение символа @, потому что сейчас у меня есть ссылка только на первый символ @.

Это также может быть связано с тем, как я обнаруживаю пробел, чтобы знать, что пользователь закончил вводить имя пользователя и не нуждается в дополнительных предложениях.

Надеюсь, это поможет, спасибо!


person Kyle Begeman    schedule 07.09.2013    source источник
comment
Можете ли вы опубликовать пример ввода/вывода, который вы ожидаете?   -  person Alladinian    schedule 08.09.2013
comment
@Alladinian Я обновил исходный пост, добавив более подробную информацию о том, что я пытаюсь сделать. Проще говоря, мне нужно иметь возможность определять, когда пользователь начинает вводить символ @ и имя пользователя, и отображать предложения на основе того, что они печатают. Сейчас он работает только с одним именем пользователя, но с двумя именами пользователей игнорирует второе, третье и т. д.   -  person Kyle Begeman    schedule 08.09.2013


Ответы (2)


Для работы с живым вводом, подобным этому, я бы рекомендовал использовать совершенно другой подход. Вместо того, чтобы искать последний «@», посмотрите только на последнее слово и посмотрите, начинается ли оно с «@».

NSString *user = nil;
NSString *text = textView.text;
// Find the last whitespace character
NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
NSRange lastWordRange = [text rangeOfCharacterFromSet:whitespace options:NSBackwardsSearch];
// If found, get the index after it. Otherwise, the entire string is one word.
if(lastWordRange.location != NSNotFound)
    lastWordRange.location += lastWordRange.length;
else lastWordRange.location = 0;
// If the index is not at the end, look for a '@' right after it
if(lastWordRange.location < [text length] &&
   [text characterAtIndex:lastWordRange.location] == '@') {
    // Found a '@', use the rest of the string as the username
    user = [text substringFromIndex:lastWordRange.location+1];
}
if(user) NSLog(@"%@", user); // The user is still typing a name

Если вы действительно хотите использовать сканер, вы можете использовать тот же метод substringFromIndex:, чтобы получить подстроку для сканирования.

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];
if (lastSymbol.location != NSNotFound) {
    NSString *substring = [text substringFromIndex:lastSymbol.location];
    NSScanner *userScanner = [NSScanner scannerWithString:substring];
    ...
person ughoavgfhw    schedule 07.09.2013
comment
Я вижу, что вы говорите, и это имеет смысл! Я случайно понял это самостоятельно, прежде чем увидел ваш пост, поэтому мне любопытно, есть ли какие-либо преимущества или недостатки использования одного метода по сравнению с другим? Позвольте мне опубликовать мой метод в качестве ответа здесь и сказать мне, что вы думаете? Большое спасибо! - person Kyle Begeman; 08.09.2013
comment
@ Кайл Это сработает. Он выполняет много дополнительной работы, сканируя всю строку, а не только последнее слово, но разница, вероятно, не будет существенной для строк из 140 символов. - person ughoavgfhw; 08.09.2013
comment
О, хорошо, теперь я понимаю, что вы имеете в виду. Производительность - абсолютно веская причина для использования предложенного вами метода. Для этого я отмечу его как правильный ответ, а свой оставлю для тех, у кого есть предпочтения. Большое спасибо за вашу помощь, я ценю это !! - person Kyle Begeman; 08.09.2013

Итак, вот решение, которое я придумал. У @ugjoavgfhw есть метод, который также работает с ограниченным тестированием, которое я провел.

Вот мое решение:

NSString *user = nil;
NSString *text = textView.text;

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];
NSScanner *userScanner = [NSScanner scannerWithString:text];
if (lastSymbol.location < 140) {
    [userScanner setScanLocation:lastSymbol.location];
}

[userScanner scanUpToString:@"@" intoString:nil];
NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@"@"];

if (![userScanner isAtEnd]) {        

    if (lastSymbol.location != NSNotFound) {

        [userScanner scanUpToCharactersFromSet:charset intoString:nil];
        [userScanner scanCharactersFromSet:charset intoString:nil];
        [userScanner scanUpToCharactersFromSet:charset intoString:&user];

    }

}


if (user.length > 0) {

    NSRange whiteSpaceRange = [user rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
    if (whiteSpaceRange.location != NSNotFound) {

        // White space, username has ended

    } else {

        // Still getting username
        NSLog(@"%@", user);

    }


}

Для ясности, этот код вызывается в методе делегата:

- (void)textViewDidChange:(UITextView *)textView

Каждый раз, когда текст обновляется, мы выполняем обратный поиск в поисках символа @. Это назначается NSRange.

Затем создается NSScanner. После этого мы проверяем lastSymbol.location, чтобы убедиться, что он меньше 140 символов (формат такой же, как в твиттере с ограниченным количеством символов).

Если это так, то мы назначаем место сканирования NSScanner.

Остальное работает как положено. Надеюсь, это поможет кому-то еще!

person Kyle Begeman    schedule 07.09.2013