快速读取 char(不是 char *)作为 C 字符串指针

Posted

技术标签:

【中文标题】快速读取 char(不是 char *)作为 C 字符串指针【英文标题】:Swift reading char (not char *) as C string pointer 【发布时间】:2018-03-16 13:10:24 【问题描述】:

这周我刚开始使用 Swift,特别是 Swift 4,我正在通过一个桥接头使用 C 库,liblo,它处理通过网络套接字发送/接收 OSC(开放声音控制)格式的消息.

我能够启动一个服务器线程,通过 C 回调->Swift 闭包接收 OSC 消息,并使用 Swift 读取数值参数值。但是,我在读取字符串值时遇到了麻烦。

liblo 消息参数类型lo_arg is a C typedef for a union 和字符串参数类型被声明为简单的字符,它们作为 Int8 映射到 Swift。

在 C 中,您可以通过 &argv[i]->s 从回调的 lo_arg **argv 数组中获取字符串。在一个带有 liblo 的 Obj-C 项目中,我使用:

// get string value of first argument
lo_arg arg = argv[0];
NSString *s = [NSString stringWithUTF8String:&arg->s];
// do something with s

在 Swift 中,我尝试获取 Int8 的地址并将其提供给有效的 String,但只获取第一个字符:

// get string value of first argument
if var arg : lo_arg = argv?.pointee![0] 
    withUnsafePointer(to: &arg.s) 
        let s = String(cString: $0)
        // so something with s
    

我做错了吗?我认为这些将是等效的,但将 $0 传递给 strlen() ala print("strlen: \(strlen($0)") 只会打印“1”的长度。我已经验证了一个多字符串确实是通过非 Swift 测试程序发送的。我现在想知道 Swift 是否以某种方式假设字符串是单个字符而不是 C 字符串头地址和/或我需要进一步的指针转换。

【问题讨论】:

【参考方案1】:

经过一番挖掘,我可以确认 Swift 在我的 64 位系统(即 sizeof(char))上将 lo_arg->slo_arg->S 字符串值截断为 8 个字节。当试图从 来自 Swift 的 lo_arg 中读取字符串时会发生这种情况。 在 C 中读取相同的值可以正常工作,因此 Swift 似乎只保留/允许从单个字符的空间中读取。将 lo_arg 从 Swift 转发到 C 并通过 printf() 打印字符串也会显示最多 8 个字符的截断字符串。

快速解决方法是避免从 Swift 生成的 lo_arg 中读取字符串,从 C 中的原始 lo_message 中获取 lo_arg,并将 char“指针”转换为 const char* Swift 将理解为可变长度字符串。以下是我添加到桥接头中的一些实用实用程序功能:

/// return an lo_message argv[i]->s in a format Swift can understand as a String
const char* lo_message_get_string(lo_message message, int at) 
    return (const char *)&lo_message_get_argv(message)[at]->s;


/// return an lo_message argv[i]->S in a format Swift can understand as a String
const char* lo_message_get_symbol(lo_message message, int at) 
    return (const char *)&lo_message_get_argv(message)[at]->S;

在 Swift 中,我可以转换为字符串:

let s = String(cString: lo_message_get_string(msg, 0))
// do something with s

【讨论】:

以上是关于快速读取 char(不是 char *)作为 C 字符串指针的主要内容,如果未能解决你的问题,请参考以下文章

将字节数组从 C# 传递到 C++ DLL 作为 char*

为啥将字符串作为文件名而不是 char* 传递时出现错误?

从具有指定字节顺序的二进制 unsigned char* 读取 C 中的 double

检查字符串中是不是存在所有 char 值

C++ REG_SZ 到 char* 并在没有提升权限的情况下读取 HKLM

为啥 putchar、toupper、tolow 等采用 int 而不是 char?