如何将字符串转换为UnsafePointer<UInt8>和长度

30

当我使用NSOutputStreamwrite方法时

func write(_ buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int

我不知道如何将String转换为UnsafePointer<UInt8>并获取长度。

在Swift中,我该怎么做?

10个回答

24

你需要先将字符串转换为UTF-8数据

let string = "foo bar"
let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!

然后将其写入输出流

let outputStream: NSOutputStream = ... // the stream that you want to write to
let bytesWritten = outputStream.write(UnsafePointer(data.bytes), maxLength: data.length)

UnsafePointer() 强制转换是必需的,因为 data.bytes 的类型为 UnsafePointer<Void>,而不是 UnsafePointer<UInt8>,这与 write() 方法所期望的类型不符。


Swift 3 更新:

let string = "foo bar"
// Conversion to UTF-8 data (cannot fail):
let data = string.data(using: String.Encoding.utf8)! 

// Write to output stream:
let outputStream: NSOutputStream = ... // the stream that you want to write to
let bytesWritten = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) }

1
嗨,马丁,那么我如何将Swift字符串转换为UnsafePointer<UInt8>?与UnsafePointer<Void>有什么不同吗?我发现有一个UTF8结构体和一个String.UTF8View嵌套类型,但不知道如何使用它们。 - Isuru
5
简单来说,我有一个类型为 String 的 Swift 变量,我需要将其转换为 UnsafePointer<UInt8> - Isuru
@Awesome-o:这就是问题所在的NSOutputStream。 - Martin R
1
是的,但它既没有在你的答案中声明/实例化,也没有在问题中声明/实例化。由于正确实例化输出流还有其他语义,因此你的答案不完整。 - Awesome-o
1
@Awesome-o:问题是如何将字符串转换为可以传递给NSOutputStream的写入方法的内容,而不是如何创建NSOutputStream。但我已经尝试澄清outputStream是什么,感谢反馈。 - Martin R
显示剩余6条评论

17

以下是如何在Swift 3中完成。 在Swift 4中也能正常运行。

extension String {

  func toPointer() -> UnsafePointer<UInt8>? {
    guard let data = self.data(using: String.Encoding.utf8) else { return nil }

    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
    let stream = OutputStream(toBuffer: buffer, capacity: data.count)

    stream.open()
    data.withUnsafeBytes({ (p: UnsafePointer<UInt8>) -> Void in
      stream.write(p, maxLength: data.count)
    })

    stream.close()

    return UnsafePointer<UInt8>(buffer)
  }
}

String转换为UnsafeMutablePointer<Int8>

let cString = strdup("Hello") // UnsafeMutablePointer<Int8>

UnsafeMutablePointer<Int8> 转换为 String

let string = String(cString: cString!) // String

1
重要的是要记住,toPointer()函数返回指向在堆上分配的内存的指针,因此,在使用完毕后应该使用free()释放它,以避免内存泄漏。 - Nathan F.
1
我想转换为UnsafeMutablePointer<UInt8>,请帮忙。 - famfamfam

7

Swift 4

将String转换为NSString,然后使用NSString的方法。

let text = "Hello"
let pointer: UnsafePointer<Int8>? = NSString(string: text).utf8String
let length = NSString(string: text).length

6
现在给那些使用Swift 4的人一个答案。你不能再从Data对象中获取字节,你需要将它们复制到UnsafeMutablePointer中。
let helloWorld = "Hello World!"

let data = helloWorld.data(using: String.Encoding.utf8, allowLossyConversion: false)!
var dataMutablePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)

//Copies the bytes to the Mutable Pointer
data.copyBytes(to: dataMutablePointer, count: data.count)

//Cast to regular UnsafePointer
let dataPointer = UnsafePointer<UInt8>(dataMutablePointer)

//Your stream
oStream.write(dataPointer, maxLength: data.count)

6
你也可以让Swift来完成它!
import Foundation

// Example function:
func printUTF8Vals(_ ptr: UnsafePointer<UInt8>, _ len: Int) { 
    for i in 0..<len { 
        print(ptr[i]) 
    } 
} 

// Call it:
let str = "Hello"
printUTF8Vals(str, str.lengthOfBytes(using: String.Encoding.utf8))

// Prints:
// 72
// 101
// 108
// 108
// 111

为什么不直接使用 str.utf8.count 呢? - Ilya
1
传递 len?是的,那也可以!我试图说明您可以将String类型传递给一个期望UnsafePointer<UInt8>的函数,而无需进行任何显式转换。 - Lou Zell

5

这里是一个针对 Swift 5 的字符串扩展,可以将字符串转换为 UnsafePointer<UInt8>UnsafeMutablePointer<Int8>

extension String {
    func toUnsafePointer() -> UnsafePointer<UInt8>? {
        guard let data = self.data(using: .utf8) else {
            return nil
        }

        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
        let stream = OutputStream(toBuffer: buffer, capacity: data.count)
        stream.open()
        let value = data.withUnsafeBytes {
            $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
        }
        guard let val = value else {
            return nil
        }
        stream.write(val, maxLength: data.count)
        stream.close()

        return UnsafePointer<UInt8>(buffer)
    }

    func toUnsafeMutablePointer() -> UnsafeMutablePointer<Int8>? {
        return strdup(self)
    }
}

UnsafeMutablePointer<Int8>转换为String
guard let mutablePointer = "test".toUnsafeMutablePointer() else {
    return
}

let str = String(cString: mutablePointer)

嗨,能帮我解决这个问题吗:https://stackoverflow.com/questions/67701338/swift-pjsip-cannot-convert-value-of-type-pj-str-t-to-expected-argument-type-u - famfamfam

4

迄今为止最简单的方法(在Swift 5中):

let s = "hello, world"
let pointer = UnsafePointer(Array(s.utf8CString))

不确定这个向后兼容性有多强。


这个失败了:无法将类型为“UnsafePointer<CChar>”(即“UnsafePointer<Int8>”)的返回表达式转换为返回类型“UnsafePointer<UInt8>”。 - yerlilbilgin
1
int8_t 实际上就是 char,而 UnsafePointer<CChar> 和 UnsafePointer<Int8> 是相同的,它们都是有符号的 int8。但 UInt8 是无符号的 char,所以你需要将有符号转换为无符号来解决它。 - Emre Tufekci

1
我看到有其他答案和一个被接受的答案,所以似乎你已经得到了你需要的。我来这里是因为我注意到 Swift 5 对于 withUnsafeMutableBytes 等的弃用警告,并开始测试 @abdullahselek 的答案,但我在 Swift 5 中(尚未验证它在之前的版本中是否有效)发现 String 可以直接转换为 UnsafePointer<UInt8>,因此您可以在需要 UnsafePointer<UInt8> 的地方使用它。如果需要看到另一个示例,这是我们旧的和更新的函数,发布在这里:

OLD

let derivationStatus = localDerivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in
  salt.withUnsafeBytes { saltBytes in

    CCKeyDerivationPBKDF(
      CCPBKDFAlgorithm(kCCPBKDF2),
      password,
      passwordData.count,
      saltBytes,
      salt.count,
      algorithm,
      UInt32(rounds),
      derivedKeyBytes,
      derivedKeyData.count
    )
  }
}

新的。
let derivationStatus = localDerivedKeyData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) -> Int32 in
  let status = CCKeyDerivationPBKDF(
    CCPBKDFAlgorithm(kCCPBKDF2),
    password, // a String
    passwordData.count, // just the password String converted to Data
    String(data: salt, encoding: .utf8),  // converts salt (Data) to String
    salt.count,
    algorithm,
    UInt32(rounds),
    outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
    derivedKeyData.count
  )
  return status
}

话虽如此,你可以采用类似的方法获取你的流,具体如下:

let stream = OutputStream(toBuffer: UnsafeMutablePointer(mutating: someString), capacity: someString.data(using: .utf8)!.count)

(感叹号用于消除编译器错误,但应尽可能避免强制解包。)


嗨,localDerivedKeyData的类型是什么?在这段新代码中,哪个是输出变量? - Satish Mavani
localDerivedKeyDataData 类型。derivationStatus 是一个 Int32 类型,用于给出操作的输出状态。这个操作会逐字节地将数据写入到 localDerivedKeyData 中(假设它是一个字节数组(Data),具有适当的长度,但一开始为空)。 - BJ Miller
函数如果抛出任何错误/异常,你能分享完整的函数使用吗? - Satish Mavani

1
我创建了一个Swift扩展,其中包括许多其他功能,用于从字符串生成指针。
它包括完整的测试套件,并支持:
- `myString.stackPointer() -> UnsafePointer?` - `myString.mutableStackPointer() -> UnsafeMutablePointer?` - `myString.withUnsignedStackPointer { (ptr: UnsafePointer?) in` - `myString.withUnsignedMutableStackPointer { (ptr: UnsafeMutablePointer?) in` - `myString.heapPointer() -> UnsafePointer?` - `myString.mutableHeapPointer() -> UnsafeMutablePointer?` - `myString.unsignedHeapPointer() -> UnsafePointer?` - `myString.unsignedMutableHeapPointer() -> UnsafeMutablePointer?`

https://gist.github.com/nathan-fiscaletti/892e074dc14e6707603414cd2d80c287

如果你想要测试它,你应该能够直接将它粘贴到 Swift Playground 中。


-1

file.cString(使用String.Encoding.utf8)


1
添加一些描述,说明原代码存在的问题以及这个代码将如何进行更正。 - jjj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接