Преобразовать 'UnsafePointer & lt; CChar & gt;' в Swift [дубликат]

  public void ReadXmlFile () {string path = HttpContext.Current.Server.MapPath ("~ / App_Data");  // Находит местоположение App_Data на сервере.  XmlTextReader reader = новый XmlTextReader (System.IO.Path.Combine (путь, XMLFile7.xml));  // Объединяет расположение App_Data и имя файла while (reader.Read ()) {switch (reader.NodeType) {case XmlNodeType.Element: break;  case XmlNodeType.Text: columnNames.Add (reader.Value);  ломать;  case XmlNodeType.EndElement: break;  }}}  

Вы можете избежать первого оператора и просто указать имя пути в конструкторе XmlTextReader.

20
задан msrd0 14 December 2014 в 19:46
поделиться

7 ответов

Массив 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-концами.

24
ответ дан Martin R 16 August 2018 в 02:31
поделиться
  • 1
    Да, это работает. Я просто добавлю, что record нуждается в , чтобы быть изменчивым (объявлено с помощью var), или компилятор Swift запускает странную ошибку. – zneak 13 December 2014 в 07:36
  • 2
    @zneak: Вы правы, я добавил эту информацию к ответу. – Martin R 13 December 2014 в 07:47
  • 3
    Я считаю, что это сработает, но документировано, что кортежи смежны в памяти? С массивами вы не получаете эту гарантию, если не назовете withUnsafeBufferPointer, но реализация кортежа не была документирована, насколько я могу судить. – Nate Cook 14 December 2014 в 07:20
  • 4
    @NateCook: Хороший вопрос. Мне кажется правдоподобным, потому что кортеж (в данном конкретном случае) представляет собой Swift-представление массива C char name[8]. – Martin R 14 December 2014 в 11:00
  • 5
    @NateCook: На самом деле он является гарантирован, если структура импортирована из C. Я обновил ответ соответственно ссылкой и упростил код. – Martin R 23 November 2016 в 23:53

Вот решение, с которым я пришел, с использованием отражения, чтобы фактически преобразовать кортеж в [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
    }
}
1
ответ дан Community 16 August 2018 в 02:31
поделиться

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

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
}
2
ответ дан jwlaughton 16 August 2018 в 02:31
поделиться
  • 1
    Строка C не является UnsafePointer<UInt8>, это набор из 8 Int8 элементов, поэтому этот метод не решает мою проблему. Кроме того, класс String имеет инициализатор init?(UTF8String: UnsafePointer<CChar>) failable, который делает это точно. – zneak 13 December 2014 в 06:35
  • 2
    В моем приложении мне нужно записать текущую версию OpenGL, чтобы убедиться, что формат пикселей установлен правильно. Я делаю это с let glVersionCString: UnsafePointer & lt; UInt8 & gt; = glGetString (GLenum (GL_VERSION)). Компилятор не позволит мне вернуть результат из glGetString как UnsafePointer & lt; CChar & gt ;, поэтому я не могу использовать инициализатор Swift String. В этом причина этой функции. – jwlaughton 13 December 2014 в 06:54
  • 3
    Я не стану сомневаться в полезности вашей функции в вашем конкретном прецеденте, но здесь она не применяется. Это работает для вас, потому что glGetString возвращает указатель. В struct я имею дело с полем массива, которое сильно отличается от поля указателя. Как я уже сказал, тип поля, видимый Свифтом, (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8), а не UnsafePointer<UInt8>. – zneak 13 December 2014 в 07:05

Фактически вы можете собрать кортеж в массив с помощью синтаксиса вариационных параметров 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
ответ дан Nate Cook 16 August 2018 в 02:31
поделиться
  • 1
    Это выглядит хорошо (не знаю о синтаксисе вариационного кортежа), хотя в реальном случае кортеж имеет 32 элемента, и я мог видеть, что мне нужно это для больших массивов (например, 128 элементов). Это сделало бы для аннотации раздражающе большого типа. Вы видите способ сделать его независимым от количества элементов? – zneak 13 December 2014 в 07:28
  • 2
    Какая конструкция let (int8s: Int8...)? Действительно ли let (name : type) = exp совпадает с let name : type = expr? – Martin R 13 December 2014 в 12:14
  • 3
    @zneak В этом случае вам нужно будет сделать это inline, так как с помощью функции вам нужно будет ввести нужное количество элементов, чтобы они соответствовали каждому конкретному атрибуту кортежа. Я изменил свой ответ, чтобы показать, что вы можете сделать. – Nate Cook 14 December 2014 в 07:12
  • 4
    @MartinR. Имя для него - это разложение кода . Вы можете потянуть вещи интересными способами, используя эту технику, попробуйте: let (_, second, _, fourth, theRest: Int...) = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) – Nate Cook 14 December 2014 в 07:15
  • 5
    С Swift 2 в Xcode 7b1 beta это больше не работает. – zneak 21 June 2015 в 16:12

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
}
1
ответ дан rosstulloch 16 August 2018 в 02:31
поделиться

Я только что испытал подобную проблему, используя 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 также является необязательным ... что означает, что оба могут быть ноль, поэтому также следует использовать проверку ошибок.

1
ответ дан Steve Rogers 16 August 2018 в 02:31
поделиться
2
ответ дан jwlaughton 6 September 2018 в 01:48
поделиться