如何将设备令牌(NSData)转换为 NSString?

我正在实现推送通知。我想保存我的 APNS 令牌为字符串。

- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken
{
NSString *tokenString = [NSString stringWithUTF8String:[newDeviceToken bytes]]; //[[NSString alloc]initWithData:newDeviceToken encoding:NSUTF8StringEncoding];
NSLog(@"%@", tokenString);
NSLog(@"%@", newDeviceToken);
}

第一行代码打印空。第二个打印令牌。如何获得作为 NSString 的 newDeviceToken?

100701 次浏览

有人帮了我,我只是顺路过来看看

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {


const unsigned *tokenBytes = [deviceToken bytes];
NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];


[[MyModel sharedModel] setApnsToken:hexToken];
}

试试这个,除非数据以空结尾。

NSString * newStr = [[ NSString alloc ] initWithData: newDeviceToken 编码: NSUTF8StringEncoding ] ;

注意: 在使用 iOS13或更高版本的 SDK 进行编译时,这将不起作用

使用:

NSString * deviceTokenString = [[[[deviceToken description]
stringByReplacingOccurrencesOfString: @"<" withString: @""]
stringByReplacingOccurrencesOfString: @">" withString: @""]
stringByReplacingOccurrencesOfString: @" " withString: @""];
        

NSLog(@"The generated device token string is : %@",deviceTokenString);
NSString *tokenstring = [[NSString alloc] initWithData:token encoding:NSUTF8StringEncoding];

你可以用这个

- (NSString *)stringWithDeviceToken:(NSData *)deviceToken {
const char *data = [deviceToken bytes];
NSMutableString *token = [NSMutableString string];


for (NSUInteger i = 0; i < [deviceToken length]; i++) {
[token appendFormat:@"%02.2hhX", data[i]];
}


return [token copy];
}

这是我的解决方案,它在我的应用程序中运行良好:

    NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
  • stringWithFormatNSData转换为 NSString
  • 修剪“ < >”
  • 去掉空格
-(NSString *)deviceTokenWithData:(NSData *)data
{
NSString *deviceToken = [[data description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
deviceToken = [deviceToken stringByReplacingOccurrencesOfString:@" " withString:@""];
return deviceToken;
}

如果有人想在 Swift 中找到这样的方法:

Swift 3引入了 Data类型,带有值语义。要将 deviceToken转换为 String,可以执行以下操作:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print(token)
}

使用 NSData的老答案:

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""


for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}


print("tokenString: \(tokenString)")
}
var token: String = ""
for i in 0..<deviceToken.count {
token += String(format: "%02.2hhx", deviceToken[i] as CVarArg)
}


print(token)

这是一个简短的解决方案:

NSData *token = // ...
const uint64_t *tokenBytes = token.bytes;
NSString *hex = [NSString stringWithFormat:@"%016llx%016llx%016llx%016llx",
ntohll(tokenBytes[0]), ntohll(tokenBytes[1]),
ntohll(tokenBytes[2]), ntohll(tokenBytes[3])];
NSString *tokenString = [[newDeviceToken description] stringByReplacingOccurrencesOfString:@"[<> ]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [[newDeviceToken description] length])];

斯威夫特:

var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
var deviceTokenString: String = ( deviceToken.description as NSString )
.stringByTrimmingCharactersInSet( characterSet )
.stringByReplacingOccurrencesOfString( " ", withString: "" ) as String


println( deviceTokenString )

斯威夫特:

let tokenString = deviceToken.description.stringByReplacingOccurrencesOfString("[ <>]", withString: "", options: .RegularExpressionSearch, range: nil)

我认为将 deviceToken 转换为十六进制字节串没有任何意义。为什么?您将把它发送到后端,在那里它将被转换回字节并推送到 APNS。因此,使用 NSData的方法 base64EncodedStringWithOptions,将其推送到服务器,然后使用反向 base64解码数据:)这就容易多了:)

NSString *tokenString = [tokenData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];

一行解决方案怎么样?

目标 C

NSString *token = [[data.description componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet]invertedSet]]componentsJoinedByString:@""];

斯威夫特

let token = data.description.components(separatedBy: CharacterSet.alphanumerics.inverted).joined()

斯威夫特

    // make sure that we have token for the devie on the App
func application(application: UIApplication
, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {


var tokenStr = deviceToken.description
tokenStr = tokenStr.stringByReplacingOccurrencesOfString("<", withString: "", options: [], range: nil)
tokenStr = tokenStr.stringByReplacingOccurrencesOfString(">", withString: "", options: [], range: nil)
tokenStr = tokenStr.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)






print("my token is: \(tokenStr)")


}

使用优秀的类别!

//. h 文件

@interface NSData (DeviceToken)


- (NSString *)stringDeviceToken;


@end

//. m 文件

#import "NSData+DeviceToken.h"


@implementation NSData (DeviceToken)


- (NSString *)stringDeviceToken {
const unsigned *deviceTokenBytes = [deviceToken bytes];
NSString *deviceToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(deviceTokenBytes[0]), ntohl(deviceTokenBytes[1]), ntohl(deviceTokenBytes[2]),
ntohl(deviceTokenBytes[3]), ntohl(deviceTokenBytes[4]), ntohl(deviceTokenBytes[5]),
ntohl(deviceTokenBytes[6]), ntohl(deviceTokenBytes[7])];
return deviceToken;
}

@ end

//AppGenerate.m

#import "NSData+DeviceToken.h"


- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *token = deviceToken.stringDeviceToken;
}

没问题!

功能性 Swift 版本

一句话:

let hexString = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes),
count: data.length).map { String(format: "%02x", $0) }.joinWithSeparator("")

下面是一个可重用的自我文档扩展表单:

extension NSData {
func base16EncodedString(uppercase uppercase: Bool = false) -> String {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes),
count: self.length)
let hexFormat = uppercase ? "X" : "x"
let formatString = "%02\(hexFormat)"
let bytesAsHexStrings = buffer.map {
String(format: formatString, $0)
}
return bytesAsHexStrings.joinWithSeparator("")
}
}

或者,可以使用 reduce("", combine: +)而不是 joinWithSeparator("")来被您的同行视为功能主机。


编辑: 我将 String ($0,radix: 16)更改为 String (格式: “% 02x”,$0) ,因为需要一个数字来填充零

(我还不知道如何将一个问题标记为 另一个的副本,所以我只是再次发布了我的答案)

斯威夫特3:

如果有人想在 Swift 3中获取设备令牌,请使用下面修改过的代码片段。

    let characterSet: CharacterSet = CharacterSet( charactersIn: "<>" )


let deviceTokenString: String = (deviceToken.description as NSString)
.trimmingCharacters(in: characterSet as CharacterSet)
.replacingOccurrences(of: " ", with: "")
.uppercased()


print(deviceTokenString)

把我的答案扔到一堆。避免使用字符串解析; 文档并不保证 NSData.description 总是这样工作。

迅速3实施:

extension Data {
func hexString() -> String {
var bytesPointer: UnsafeBufferPointer<UInt8> = UnsafeBufferPointer(start: nil, count: 0)
self.withUnsafeBytes { (bytes) in
bytesPointer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(bytes), count:self.count)
}
let hexBytes = bytesPointer.map { return String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}

对于那些想在 Swift 3和最容易的方法

func extractTokenFromData(deviceToken:Data) -> String {
let token = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
return token.uppercased();
}

我试图用格式 "%02.2hhx""%02x"测试两种不同的方法

    var i :Int = 0
var j: Int = 0
let e: Int = Int(1e4)
let time = NSDate.timeIntervalSinceReferenceDate
while i < e {
_ =  deviceToken.map { String(format: "%02x", $0) }.joined()
i += 1
}
let time2 = NSDate.timeIntervalSinceReferenceDate
let delta = time2-time
print(delta)


let time3 = NSDate.timeIntervalSinceReferenceDate
while j < e {
_ =  deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
j += 1
}
let time4 = NSDate.timeIntervalSinceReferenceDate
let delta2 = time4-time3
print(delta2)

结果是,最快的是 "%02x",平均速度为2.0,而简化版本为2.6:

deviceToken.reduce("", {$0 + String(format: "%02x", $1)})

在高票中对 %02.2hhx的解释 回答:

  • %: 介绍 x转换说明符。
  • 02: 转换值的最小宽度为2。如果转换后的值的字节数少于字段宽度,则应在左侧用 0填充该值。
  • .2: 给出 x转换说明符显示的最小位数。
  • hh: 指定 x转换说明符应用于有符号的 char 或无符号的 char 参数(参数将根据整数提升进行提升,但其值在打印前应转换为有符号的 char 或无符号的 char)。
  • x: 无符号参数应转换为“ dddd”样式的无符号十六进制格式; 使用字母“ abcdef”。精度指定要显示的最小位数; 如果要转换的值可以用较少位数表示,则应该用前导零展开。默认精度为1。显式精度为零的零转换的结果不应该是字符。

有关详细信息,请参阅 IEEE printf 规范


基于以上的解释,我认为最好将 %02.2hhx改为 %02x%.2x

对于 Swift 5,以下方法都是可行的:

deviceToken.map({String(format: "%02x", $0)}).joined()
deviceToken.map({String(format: "%.2x", $0)}).joined()
deviceToken.reduce("", {$0 + String(format: "%02x", $1)})
deviceToken.reduce("", {$0 + String(format: "%.2x", $1)})

测试方法如下:

let deviceToken = (0..<32).reduce(Data(), {$0 + [$1]})
print(deviceToken.reduce("", {$0 + String(format: "%.2x", $1)}))
// Print content:
// 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f

在 Xamarin.iOS 中是这样做的

public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
var tokenStringBase64 = deviceToken.GetBase64EncodedString(NSDataBase64EncodingOptions.None);
//now you can store it for later use in local storage
}

使用 更新累积结果比这里发现的其他各种方法更有效率,因此这里介绍了 最快的Data字节字符串化的方法:

func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.reduce(into: "") { $0 += String(format: "%.2x", $1) }
print(token)
}

这里发布的解决方案@kulss,虽然缺乏优雅,但具有简单的优点,不再适用于 iOS 13,因为 description将不同于 NSData。但是你仍然可以使用 debugDescription

NSString * deviceTokenString = [[[[deviceToken debugDescription]
stringByReplacingOccurrencesOfString: @"<" withString: @""]
stringByReplacingOccurrencesOfString: @">" withString: @""]
stringByReplacingOccurrencesOfString: @" " withString: @""];

在 iOS13description将会中断,所以使用这个

let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()

为了清楚起见,让我们把这个分解并解释每个部分:

Map 方法对序列的每个元素进行操作。因为 Data 是 Swift 中的字节序列,所以对 deviceToken 中的每个字节计算传递的闭包。 String (format:)初始化器使用% 02x 格式说明符计算数据中的每个字节(由匿名参数 $0表示) ,以生成字节/8位整数的零填充的2位十六进制表示形式。 在收集了 map 方法创建的每个字节表示之后,join ()将每个元素连接到一个字符串中。

PS 不使用描述在 iOS12和 iOS13中给出不同的字符串,并且不安全,因为未来的范围。开发人员不应该依赖特定的格式来描述对象。

// iOS 12
(deviceToken as NSData).description // "<965b251c 6cb1926d e3cb366f dfb16ddd e6b9086a 8a3cac9e 5f857679 376eab7C>"


// iOS 13
(deviceToken as NSData).description // "{length = 32, bytes = 0x965b251c 6cb1926d e3cb366f dfb16ddd ... 5f857679 376eab7c }"

更多信息请阅读 这个

在 iOS13中,描述将采用不同的格式。请使用下面的代码来获取设备令牌。

- (NSString *)fetchDeviceToken:(NSData *)deviceToken {
NSUInteger len = deviceToken.length;
if (len == 0) {
return nil;
}
const unsigned char *buffer = deviceToken.bytes;
NSMutableString *hexString  = [NSMutableString stringWithCapacity:(len * 2)];
for (int i = 0; i < len; ++i) {
[hexString appendFormat:@"%02x", buffer[i]];
}
return [hexString copy];
}

二零二零年

标记为文本。

let tat = deviceToken.map{ data in String(format: "%02.2hhx", data) }.joined()

或者如果你愿意

let tat2 = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()

(结果相同)

这对你有用,

NSUInteger dataLength = deviceToken.length;
    

const unsigned char *dataBuffer = (const unsigned char *)deviceToken.bytes;
NSMutableString *deviceTokenString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i) {
[deviceTokenString appendFormat:@"%02x", dataBuffer[i]];
}
    

NSLog(@"The generated device token string is : %@",deviceTokenString);