Работа со строками 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)

Другие вопросы по тегам