Подтвердить что ты не робот

Преобразование массива C char в строку

У меня есть программа Swift, которая взаимодействует с библиотекой C. Эта библиотека C возвращает структуру с массивом char[] внутри, например:

struct record
{
    char name[8];
};

Определение корректно импортируется в Swift. Однако поле интерпретируется как набор из 8 Int8 элементов (набранных (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8)), которые я понятия не имею, как преобразовать в String со Swift.

Нет инициализатора String, который принимает кортеж Int8, и не представляется возможным получить указатель на первый элемент кортежа (поскольку типы могут быть гетерогенными, что не удивительно).

Прямо сейчас, моя лучшая идея - создать крошечную функцию C, которая принимает указатель на саму структуру и возвращает name как указатель char* вместо массива, и пойдет с этим.

Есть ли, однако, чистый способ Swift для этого?

4b9b3361

Ответ 1

Массив C char name[8] импортируется в Swift как кортеж:

(Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8)

Адрес name совпадает с адресом name[0] и Swift сохраняет структуру памяти структур, импортированных из C, так как подтвержденный инженером Apple Джо Гроффом:

... Вы можете оставить структуру, определенную на C, и импортировать ее в Swift. Swift будет уважать макет C.

Как следствие, мы можем передать адрес record.name, преобразован в указатель UInt8, чтобы Инициализатор строки:

var record = someFunctionReturningAStructRecord()

// Swift 2:
let name = withUnsafePointer(&record.name) {
    String.fromCString(UnsafePointer($0))!
}

// Swift 3:
let name = withUnsafePointer(to: &record.name) {
    $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: record.name)) {
        String(cString: $0)
    }
}

ПРИМЕЧАНИЕ.. Предполагается, что байты в name[] являются допустимой последовательностью UTF-8 с NUL-завершением.

Ответ 2

Фактически вы можете собрать кортеж в массив с помощью синтаксиса синтаксических переменных Swift:

let record = getRecord()
let (int8s: Int8...) = myRecord          // int8s is an [Int8]
let uint8s = int8s.map { UInt8($0) }
let string = String(bytes: uint8s, encoding: NSASCIIStringEncoding)
// myString == Optional("12345678")

Ответ 3

Я также заинтересован в том, чтобы это сделать для моих собственных целей, поэтому я добавил новую функцию:

func asciiCArrayToSwiftString(cString:Int8...) -> String
{
    var swiftString = String()            // The Swift String to be Returned is Intialized to an Empty String
    var workingCharacter:UnicodeScalar = UnicodeScalar(UInt8(cString[0]))
    var count:Int = cString.count

    for var i:Int = 0; i < count; i++
    {
        workingCharacter = UnicodeScalar(UInt8(cString[i])) // Convert the Int8 Character to a Unicode Scalar
        swiftString.append(workingCharacter)             // Append the Unicode Scalar

    }

    return swiftString                     // Return the Swift String
}

Я вызываю эту функцию с помощью:

    let t:Int8 = Int8(116)
    let e:Int8 = Int8(101)
    let s:Int8 = Int8(115)
    let testCString = (t, e, s, t)
    let testSwiftString = wispStringConverter.asciiCArrayToSwiftString(testCString.0, testCString.1, testCString.2, testCString.3)
    println("testSwiftString = \(testSwiftString)")

результирующий результат:

testSwiftString = test

Ответ 4

Swift 3. Использует только отражение. Эта версия перестает строить строку, когда она встречает нулевой байт. Испытано.

func TupleOfInt8sToString( _ tupleOfInt8s:Any ) -> String? {
    var result:String? = nil
    let mirror = Mirror(reflecting: tupleOfInt8s)

    for child in mirror.children {
        guard let characterValue = child.value as? Int8, characterValue != 0 else {
            break
        }

        if result == nil {
            result = String()
        }
        result?.append(Character(UnicodeScalar(UInt8(characterValue))))
    }

    return result
}

Ответ 5

Я только что испытал подобную проблему, используя Swift 3. (3.0.2). Я пытался преобразовать массив CChar, [CChar] в строку в Swift. Оказывается, Swift 3 имеет инициализатор строки, который примет cString.

Пример:

let a = "abc".cString(using: .utf8) // type of a is [CChar]
let b = String(cString: a!, encoding: .utf8) // type of b is String
print("a = \(a)")
print("b = \(b)")

приводит к

a = Необязательный ([97, 98, 99, 0])

b = Необязательный ( "abc" )

Обратите внимание, что функция cString в String приводит к необязательной. Он должен быть принудительно развернут при использовании в функции String.init, создающей b. И b также является необязательным... что означает, что оба могут быть равны нулю, поэтому также следует использовать проверку ошибок.

Ответ 6

Попробуйте следующее:

func asciiCStringToSwiftString(cString:UnsafePointer<UInt8>, maxLength:Int) -> String
{
    var swiftString = String()  // The Swift String to be Returned is Intialized to an Empty String
    var workingCharacter:UnicodeScalar = UnicodeScalar(cString[0])
    var count:Int = 0           // An Index Into the C String Array Starting With the First Character

    while cString[count] != 0             // While We Haven't reached the End of the String
    {
        workingCharacter = UnicodeScalar(cString[count]) // Convert the ASCII Character to a Unicode Scalar
        swiftString.append(workingCharacter)             // Append the Unicode Scalar Version of the ASCII Character
        count++                                          // Increment the Index to Look at the Next ASCII Character

        if count > maxLength                            // Set a Limit In Case the C string was Not NULL Terminated
        {
            if printDebugLogs == true
            {
                swiftString="Reached String Length Limit in Converting ASCII C String To Swift String"
            }
            return swiftString
        }
    }

    return swiftString                     // Return the Swift String
}

Ответ 7

Здесь появилось решение, в котором используется отражение, чтобы фактически преобразовать кортеж в [Int8] (см. Любой способ перебора кортежа в swift?) и затем преобразует его в строку, используя методыCString...().

func arrayForTuple<T,E>(tuple:T) -> [E] {
    let reflection = reflect(tuple)
    var arr : [E] = []
    for i in 0..<reflection.count {
        if let value = reflection[i].1.value as? E {
            arr.append(value)
        }
    }
    return arr
}

public extension String {
    public static func fromTuple<T>(tuple:T) -> String? {
        var charArray = arrayForTuple(tuple) as [Int8]
        var nameString = String.fromCString(UnsafePointer<CChar>(charArray))
        if nameString == nil {
            nameString = String.fromCStringRepairingIllFormedUTF8(UnsafePointer<CChar>(charArray)).0
        }
        return nameString
    }
}