Avatar ·

Как получить длину строки в Swift? Не работает свойство .length как в Objective-C

Всем привет! Я только начал изучать Swift, до этого немного писал на Objective-C. Там, чтобы получить длину строки, я просто писал: ```objc NSString *str = @"Привет"; NSInteger len = [str length]; ``` Теперь я пытаюсь сделать то же самое в Swift, но компилятор выдаёт ошибку. Вот мой код: ```swift let str = "Привет" let len = str.length // Error: Value of type 'String' has no member 'length' ``` Я попробовал поискать в интернете и нашёл, что в Swift нужно использовать `count`, но у меня не получается: ```swift let str = "Привет" let len = str.count // Это работает, но даёт странный результат для эмодзи ``` Проблема в том, что когда я считаю длину строки с эмодзи, например "👨‍👩‍👧‍👦 Семья", `count` возвращает 1 для этого составного эмодзи, хотя визуально там 4 человечка. А мне нужно получить именно количество видимых символов, как это было в Objective-C. Подскажите, пожалуйста, как правильно получить длину строки в Swift? Желательно с учётом Unicode и эмодзи. Заранее спасибо! <<>>

Правильный способ получить длину строки в Swift

В Swift, в отличие от Objective-C, строки основаны на Unicode и работают с графем-кластерами (grapheme clusters), а не с отдельными байтами или UTF-16 кодовыми единицами. Поэтому свойство .length отсутствует — его заменяет свойство .count, которое возвращает количество видимых пользовательских символов (extended grapheme clusters).

Основное решение: count

Для большинства случаев используйте именно .count:

let str = "Привет, мир!"
print(str.count) // 13

let emoji = "😀👍"
print(emoji.count) // 2

Это работает корректно для обычных букв, цифр и простых эмодзи. Однако для составных эмодзи (например, "👨‍👩‍👧‍👦" — семья из 4 человек) .count вернёт 1, потому что это один графем-кластер. Это правильное поведение с точки зрения Unicode — пользователь воспринимает этот эмодзи как один символ.

Если вам нужно количество "составных частей" (как в Objective-C)

Иногда нужно получить количество именно UTF-16 кодовых единиц (как работал NSString.length). Для этого используйте:

let str = "👨‍👩‍👧‍👦"
print(str.utf16.count) // 11 — количество UTF-16 единиц

Но будьте осторожны: это число не соответствует визуальному восприятию пользователя. Для эмодзи "семья" вы получите 11, хотя визуально это один символ.

Как получить количество видимых "человечков" внутри составного эмодзи?

Если вам действительно нужно разобрать составной эмодзи на части (например, "👨‍👩‍👧‍👦" разбить на "👨", "👩", "👧", "👦"), это сложная задача, требующая работы с Unicode-последовательностями. Простой способ — использовать unicodeScalars:

let family = "👨‍👩‍👧‍👦"
let scalars = family.unicodeScalars
print(scalars.count) // 7 — количество Unicode-скаляров

Но это всё равно не даст вам 4 отдельных человечка, так как эмодзи кодируются специальными символами-модификаторами (ZWJ — Zero Width Joiner).

Практические рекомендации

  • Обычные строки (текст, адреса, имена): используйте .count — это правильно и интуитивно.
  • Работа с эмодзи: тоже используйте .count, если вам важно, как пользователь видит строку.
  • Совместимость с Objective-C API: если передаёте строку в старый код, используйте (str as NSString).length или str.utf16.count.
  • Проверка на пустоту: всегда используйте str.isEmpty вместо str.count == 0 — это быстрее и читаемее.

Полный пример с пояснениями

let text = "Hello, Swift! 👋"
print("count: \(text.count)")          // 15 (буквы + пробел + эмодзи)
print("utf16: \(text.utf16.count)")    // 16 (эмодзи занимает 2 UTF-16 единицы)
print("unicodeScalars: \(text.unicodeScalars.count)") // 15 (один скаляр на символ)

// Для пустой строки
let empty = ""
print(empty.isEmpty) // true — так правильно
print(empty.count == 0) // true, но медленнее

Запомните главное правило: в Swift строка — это коллекция графем-кластеров, а не байтов. Поэтому .count — это количество символов, которые видит пользователь, а не техническое представление.

<<>>

Альтернативный подход: работа через NSString

Если вы мигрируете с Objective-C или вам нужно точное поведение, как у NSString.length, можно явно привести String к NSString:

let str = "Привет 👨‍👩‍👧‍👦"
let nsLength = (str as NSString).length
print(nsLength) // 12 (8 букв + пробел + 3 UTF-16 единицы для эмодзи)

Это даст вам количество UTF-16 кодовых единиц — именно так работал старый метод length. Но помните: такой подход считается устаревшим в чистом Swift-коде.

Когда это может пригодиться?

  • При работе с фреймворками, которые ожидают NSString (например, NSAttributedString).
  • При парсинге текста по индексам (хотя лучше использовать String.Index).
  • Для обратной совместимости с Objective-C кодом.

Важное замечание про производительность

Свойство .count у String имеет сложность O(n), так как строки в Swift не хранят длину заранее (из-за Unicode). Если вам нужно часто вызывать .count в цикле, лучше сохранить длину в переменную:

let str = "Длинная строка..."
let length = str.count
for _ in 0..<length {
    // Ваш код
}

Или, если возможно, используйте str.isEmpty для проверки на пустоту — это O(1).