Как удалить последний символ юникода из NSString

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

NSRange range = NSMakeRange(currentTextFieldString.length-1, 1);
[currentTextFieldString replaceCharactersInRange:range withString:@""];

Все идет нормально.

Теперь проблема в том, что у пользователя есть возможность ввести некоторые специальные символы Unicode, это не 1 байт, они могут быть и 2 байтами, теперь при нажатии кнопки удаления я должен удалить весь символ, но если я следуя описанному выше подходу, пользователь должен дважды нажать кнопку удаления.

Вот, если я это сделаю:

NSRange range = NSMakeRange(currentTextFieldString.length-2, 2);
[currentTextFieldString replaceCharactersInRange:range withString:@""];

он работает нормально, но тогда обычные символы, состоящие всего из 1 байта, удаляются дважды за раз.

Как справиться с такими сценариями?

Заранее спасибо.

РЕДАКТИРОВАТЬ:

Странно, что если я переключаюсь на клавиатуру iPhone, она обрабатывает оба случая соответствующим образом. Должен быть какой-то способ сделать это, я чего-то упускаю, но не могу понять, что именно.


person Bani Uppal    schedule 01.08.2012    source источник
comment
Как ваша настраиваемая клавиатура добавляет символы?   -  person Joe    schedule 01.08.2012
comment
Мой ответ на этот вопрос может помочь вам stackoverflow.com/q/11567049/1487063   -  person Dustin    schedule 01.08.2012
comment
@Joe: всякий раз, когда пользователь нажимает кнопку, я ловлю соответствующий символ, добавляю его к текущему тексту текстового поля и назначаю его как текст для текстового поля.   -  person Bani Uppal    schedule 01.08.2012
comment
@Dustin: Вместо использования метода replaceCharactersInRange: withRange: я использовал substringToIndex: currentTextFieldString.length-1. Это имеет тот же эффект. Фактически, для специальных юникодов он считается как 2 индекса. Таким образом, пользователь должен дважды нажать клавишу удаления, чтобы удалить такие символы.   -  person Bani Uppal    schedule 01.08.2012


Ответы (3)


Вот в чем проблема. NSStrings кодируются с использованием UTF-16. Многие распространенные глифы Unicode занимают только один unichar (16-битное значение без знака). Однако некоторые глифы занимают два символа unichars. Хуже того, некоторые глифы могут быть составлены или разложены, например, é может быть одной кодовой точкой Unicode или может быть двумя - острым ударением, за которым следует e. Это затрудняет выполнение того, что вы хотите, а именно удаление одного «символа», потому что очень сложно определить, сколько unichars он занимает.

К счастью, в NSString есть метод, который помогает в этом: -rangeOfComposedCharacterSequenceAtIndex: . Что вам нужно сделать, так это получить индекс последнего unichar, запустить на нем этот метод, и возвращенный NSRange сообщит вам, откуда удалить. Это выглядит примерно так (не проверено):

NSUInteger lastCharIndex = [myString length] - 1; // I assume string is not empty
NSRange rangeOfLastChar = [myString rangeOfComposedCharacterSequenceAtIndex: lastCharIndex];
myNewString = [myString substringToIndex: rangeOfLastChar.location];
person JeremyP    schedule 01.08.2012
comment
+1 Это именно то, что я искал. Это прекрасно работает. Некоторые методы спрятаны глубоко внутри документации, чтобы выяснить это. - person Bani Uppal; 01.08.2012

Если вы не можете заставить это работать по умолчанию, используйте блок if / else и проверьте, является ли последний символ частью специального символа. Если это так, используйте подстроку для length-2, в противном случае используйте подстроку для length-1.

person Dustin    schedule 01.08.2012
comment
Спасибо за ответ! Но не будет ли это проблемой, если, скажем, пользователь вводит символ с клавиатуры, скажем @ / # / $ и т. Д. Если я проверю, является ли последний символ специальным символом, в этом случае это будет верно, и мы бы удалить 2 символа, где мы должны были удалить один? - person Bani Uppal; 01.08.2012
comment
Я имею в виду, что в вашем операторе if проверьте конкретные символы, которые вызывают проблемы. Не те, которых нет. - person Dustin; 01.08.2012
comment
Ах да, но некоторые из них, которые вызывают у меня проблемы, существуют в обоих :) В этом суть моей проблемы. - person Bani Uppal; 01.08.2012
comment
Затем проверьте последние 2 символа - person Dustin; 01.08.2012
comment
О, это логически правильно, спасибо за это. Но если у меня введено 2 специальных символа и юникод, который на 95% представляет собой комбинацию 2 специальных символов, я не могу решить, снова проблема. - person Bani Uppal; 01.08.2012

Я точно не знаю, в чем проблема с байтовой длиной специальных символов.

Я предлагаю:

  • Сохраните длину строки в параметре перед добавлением любых новых символов
  • Если пользователь выбирает backspace (удаляет последние символы), то удаляет строку с последней длины до новой длины. Означает, например, что длина последней сохраненной строки равна 5, а длина новой строки - 7, затем удалите, чтобы получить новую строку с индексом от 0 до 4, поэтому она обрежет оставшиеся символы.

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

Но я думаю, что логически это решение должно работать.

Наслаждайтесь кодированием :)

person Mrunal    schedule 01.08.2012
comment
О да, я думаю, это должно сработать. Спасибо за ответ, но я все еще не понимаю, как решить эту проблему с помощью более методологического подхода. Я пробовал всевозможные вещи, например, преобразовал его в NSData, а затем удалил байты, чтобы увидеть, что на самом деле происходит, но безрезультатно :( - person Bani Uppal; 01.08.2012