Преобразование NSData в кодировке UTF-8 в NSString

У меня есть кодировка UTF-8 NSData с сервера Windows, и я хочу преобразовать ее в NSString для iPhone. Поскольку данные содержат символы (например, градусы), которые имеют разные значения на обеих платформах, как преобразовать данные в строку?


person Ashwini Shahapurkar    schedule 18.03.2010    source источник
comment
UTF-8 везде UTF-8. Если это UTF-8, нет разных значений для разных платформ. В этом весь смысл.   -  person gnasher729    schedule 12.04.2014


Ответы (7)


Если данные не заканчиваются нулем, вы должны использовать -initWithData:encoding:

NSString* newStr = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];

Если данные заканчиваются нулем, вместо этого следует использовать -stringWithUTF8String:, чтобы избежать лишнего \0 в конце.

NSString* newStr = [NSString stringWithUTF8String:[theData bytes]];

(Обратите внимание, что если входные данные некорректно закодированы в UTF-8, вы получите nil.)


Быстрый вариант:

let newStr = String(data: data, encoding: .utf8)
// note that `newStr` is a `String?`, not a `String`.

Если данные заканчиваются нулем, вы можете пойти безопасным способом, который удаляет этот нулевой символ, или небезопасным способом, аналогичным версии Objective-C выше.

// safe way, provided data is \0-terminated
let newStr1 = String(data: data.subdata(in: 0 ..< data.count - 1), encoding: .utf8)
// unsafe way, provided data is \0-terminated
let newStr2 = data.withUnsafeBytes(String.init(utf8String:))
person kennytm    schedule 18.03.2010
comment
осторожно!! если вы используете stringWithUTF8String, не передавайте ему аргумент NULL, иначе будет сгенерировано исключение - person JasonZ; 05.07.2012
comment
ОБРАТИТЕ ВНИМАНИЕ: при использовании stringWithUTF8String: для строки, которая не заканчивается нулем, результат непредсказуем! - person Berik; 01.08.2012
comment
Оба решения возвращают для меня ноль. - person Husyn; 22.08.2015
comment
Вы также можете удалить NS, если используете swift 2, сделав это: let newStr = String (data: data, encoding: NSUTF8StringEncoding) - person Jeremiah; 20.01.2016
comment
Я обнаружил сбой: NSString jsonStr = [NSString stringWithUTF8String: [jsonData bytes]]; И NSString newStr = [[NSString alloc] initWithData: theData encoding: NSUTF8StringEncoding]; - person ylgwhyh; 14.11.2016
comment
Вероятно, глупый вопрос (просто изучаю Swift, совсем не знаю Objective-C): вы показываете два варианта для Objective-C в зависимости от того, завершается ли ввод нулем или нет, но только один вариант для Swift? - person RenniePet; 06.12.2016
comment
Как узнать, завершаются ли ваши NSData нулевым символом в конце или нет? См. Ответ Тома Харрингтона по адресу: stackoverflow.com/questions/27935054/. По моему опыту, никогда не следует предполагать, что NSData либо завершается нулем, либо нет: он может отличаться от одной передачи к другой, даже от известного сервера. - person Elise van Looij; 08.07.2018
comment
@ElisevanLooij Спасибо за ссылку. Я бы сказал, что если передаваемые данные могут быть случайным образом завершены нулем или нет, протокол не определен. - person kennytm; 09.07.2018
comment
@kennytm Без сомнения, ты прав, но иногда приходится грести веслами, которые есть у тебя. (Прошу прощения за перевод голландской поговорки) - person Elise van Looij; 09.07.2018

Вы можете назвать этот метод

+(id)stringWithUTF8String:(const char *)bytes.
person Gouldsc    schedule 18.03.2010
comment
Только если данные заканчиваются нулем. Которого может и не быть (и, на самом деле, вероятно, нет). - person Ivan Vučica; 08.03.2013
comment
я не знаю, с какой стати это сломалось бы для строк, не завершающихся нулем, видя, как NSData знает, сколько байтов у него есть ... - person Claudiu; 01.10.2013
comment
@Claudiu, вы не передаете объект NSData, вы передаете ему (const char *), полученный с помощью [байтов данных], который является просто указателем, без информации о размере. Следовательно, блок данных, на который он указывает, должен иметь нулевое завершение. Ознакомьтесь с документацией, в ней прямо сказано об этом. - person jbat100; 21.10.2013
comment
@ jbat100: Конечно. Я не понял. Я имел в виду, что, учитывая, что можно перейти от NSData без завершающего нуля к NSString (см. Ответ KennyTM), я удивлен, что нет +(id)stringWithUTF8Data:(NSData *)data, который просто работает. - person Claudiu; 21.10.2013
comment
stringWithUTF8Data, поэтому большинство из нас создают категорию NSString + Foo и создают метод. - person Cerniuk; 14.10.2017

Я скромно добавляю категорию, чтобы это не раздражало:

@interface NSData (EasyUTF8)

// Safely decode the bytes into a UTF8 string
- (NSString *)asUTF8String;

@end

а также

@implementation NSData (EasyUTF8)

- (NSString *)asUTF8String {
    return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];    
}

@end

(Обратите внимание: если вы не используете ARC, вам понадобится autorelease.)

Теперь вместо ужасающе многословной:

NSData *data = ...
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

Ты можешь сделать:

NSData *data = ...
[data asUTF8String];
person Claudiu    schedule 01.10.2013

Версия Swift из String в Data и обратно в String:

Xcode 10.1 • Swift 4.2.1

extension Data {
    var string: String? {
        return String(data: self, encoding: .utf8)
    }
}

extension StringProtocol {
    var data: Data {
        return Data(utf8)
    }
}

extension String {
    var base64Decoded: Data? {
        return Data(base64Encoded: self)
    }
}

Детская площадка

let string = "Hello World"                                  // "Hello World"
let stringData = string.data                                // 11 bytes
let base64EncodedString = stringData.base64EncodedString()  // "SGVsbG8gV29ybGQ="
let stringFromData = stringData.string                      // "Hello World"

let base64String = "SGVsbG8gV29ybGQ="
if let data = base64String.base64Decoded {
    print(data)                                    //  11 bytes
    print(data.base64EncodedString())              // "SGVsbG8gV29ybGQ="
    print(data.string ?? "nil")                    // "Hello World"
}

let stringWithAccent = "Olá Mundo"                          // "Olá Mundo"
print(stringWithAccent.count)                               // "9"
let stringWithAccentData = stringWithAccent.data            // "10 bytes" note: an extra byte for the acute accent
let stringWithAccentFromData = stringWithAccentData.string  // "Olá Mundo\n"
person Leo Dabus    schedule 15.02.2015

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

Цель-C

NSData *signature;
NSString *signatureString = [signature base64EncodedStringWithOptions:0];

Swift

let signatureString = signature.base64EncodedStringWithOptions(nil)
person mikeho    schedule 19.05.2014
comment
как получить эту строку в nsdata? - person Darshan Kunjadiya; 09.02.2015
comment
@DarshanKunjadiya: Цель-C: [[NSData alloc] initWithBase64EncodedString:signatureString options:0]; Swift: NSData(base64EncodedString: str options: nil) - person mikeho; 11.02.2015

Подводя итог, вот полный ответ, который сработал для меня.

Моя проблема заключалась в том, что когда я использовал

[NSString stringWithUTF8String:(char *)data.bytes];

Строка, которую я получил, была непредсказуемой: около 70% она действительно содержала ожидаемое значение, но слишком часто это приводило к Null или даже хуже: мусор в конце строки.

Покопавшись, я переключился на

[[NSString alloc] initWithBytes:(char *)data.bytes length:data.length encoding:NSUTF8StringEncoding];

И каждый раз получал ожидаемый результат.

person Gal    schedule 29.11.2015
comment
Важно, чтобы вы понимали ‹i›, почему ‹/i› вы получили «мусорные» результаты. - person Edgar Aroutiounian; 27.08.2016

В Swift 5 вы можете использовать инициализатор String init(data:encoding:) для преобразования экземпляр Data в экземпляр String с использованием UTF-8. init(data:encoding:) имеет следующее объявление:

init?(data: Data, encoding: String.Encoding)

Возвращает String, инициализированный преобразованием заданных данных в символы Юникода с использованием заданной кодировки.

Следующий код игровой площадки показывает, как его использовать:

import Foundation

let json = """
{
"firstName" : "John",
"lastName" : "Doe"
}
"""

let data = json.data(using: String.Encoding.utf8)!

let optionalString = String(data: data, encoding: String.Encoding.utf8)
print(String(describing: optionalString))

/*
 prints:
 Optional("{\n\"firstName\" : \"John\",\n\"lastName\" : \"Doe\"\n}")
*/
person Imanou Petit    schedule 12.02.2019