Работа со строками C в Swift или: Как преобразовать UnsafePointer<CChar> в CString
Играя со стандартными функциями библиотеки C в Swift, я столкнулся с проблемами при передаче строк C. В качестве простого примера (просто для демонстрации проблемы), функция Standard C Library
char * strdup(const char *s1);
подвергается Swift как
func strdup(_: CString) -> UnsafePointer<CChar>
Это означает, что возвращаемое значение strdup()
не может быть передан другому strdup()
вызов:
let s1 : CString = "abc"
let s2 = strdup(s1) // OK, s2 is a UnsafePointer<CChar>
let s3 = strdup(s2) // error: could not find an overload for '__conversion' that accepts the supplied arguments
Мой вопрос: как создать Swift CString
из UnsafePointer<CChar>
, так что строка C, возвращаемая одной стандартной библиотечной функцией, может быть передана другой функции?
Единственный способ, который я смог найти - это (используя код из Как преобразовать строку в строку CSt на языке Swift?):
let s2a = String.fromCString(s2).bridgeToObjectiveC().UTF8String
let s3 = strdup(s2a)
Но я не нахожу это удовлетворяющим по двум причинам:
- Это слишком сложно для простой задачи.
- (Основная причина:) Вышеприведенные преобразования работают только в том случае, если строка C является допустимой строкой UTF-8, в противном случае произойдет сбой с исключением времени выполнения. Но строка C - это произвольная последовательность символов, разделенная символом NUL.
Примечания / Предпосылки: Конечно, высокоуровневые функции используют высокоуровневые структуры данных, такие как Swift String
или Objective-C NSString
предпочтительнее Но в библиотеке Standard C есть функции BSD, которые не имеют точного аналога в платформах Foundation.
Я столкнулся с этой проблемой, пытаясь ответить на Доступ к временному каталогу в Swift. Вот, mkdtemp()
это функция BSD, для которой нет точного NSFileManager
замена существует (насколько я знаю). mkdtemp()
возвращает UnsafePointer<CChar>
который должен быть переданNSFileManager
функция stringWithFileSystemRepresentation
который занимает CString
аргумент.
Обновление: Начиная с Xcode 6 beta 6, эта проблема больше не существует, потому что отображение C-Strings в Swift было упрощено. Вы можете просто написать
let s1 = "abc" // String
let s2 = strdup(s1) // UnsafeMutablePointer<Int8>
let s3 = strdup(s2) // UnsafeMutablePointer<Int8>
let s4 = String.fromCString(s3) // String
1 ответ
Swift 1.1 (или, возможно, ранее) имеет еще лучшее соединение C-строк:
let haystack = "This is a simple string"
let needle = "simple"
let result = String.fromCString(strstr(haystack, needle))
CString
Тип ушел полностью.
В структуре String в Swift есть процедура инициализации, которую вы можете использовать, например:
let myString = String(cString: myUnsafePointer)
см. также init(cString: UnsafePointer)