Cập nhật cho Swift 3
String và nhân vật
Đối với hầu hết mọi người trong tương lai ai truy cập vào câu hỏi này, String
and Character
sẽ là câu trả lời cho bạn.
giá trị Set Unicode trực tiếp trong mã:
var str: String = "I want to visit 北京, Москва, मुंबई, القاهرة, and 서울시. "
var character: Character = ""
Sử dụng hệ thập lục phân để thiết lập giá trị
var str: String = "\u{61}\u{5927}\u{1F34E}\u{3C0}" // a大π
var character: Character = "\u{65}\u{301}" // é = "e" + accent mark
Lưu ý rằng các nhân vật Swift có thể bao gồm nhiều điểm mã Unicode, nhưng xuất hiện là một nhân vật duy nhất. Đây được gọi là Cluster Grapheme mở rộng.
Xem this question.
Chuyển đổi sang giá trị Unicode:
str.utf8
str.utf16
str.unicodeScalars // UTF-32
String(character).utf8
String(character).utf16
String(character).unicodeScalars
Chuyển đổi từ giá trị hex Unicode:
let hexValue: UInt32 = 0x1F34E
// convert hex value to UnicodeScalar
guard let scalarValue = UnicodeScalar(hexValue) else {
// early exit if hex does not form a valid unicode value
return
}
// convert UnicodeScalar to String
let myString = String(scalarValue) //
Hoặc cách khác:
let hexValue: UInt32 = 0x1F34E
if let scalarValue = UnicodeScalar(hexValue) {
let myString = String(scalarValue)
}
Một vài ví dụ khác
let value0: UInt8 = 0x61
let value1: UInt16 = 0x5927
let value2: UInt32 = 0x1F34E
let string0 = String(UnicodeScalar(value0)) // a
let string1 = String(UnicodeScalar(value1)) // 大
let string2 = String(UnicodeScalar(value2)) //
// convert hex array to String
let myHexArray = [0x43, 0x61, 0x74, 0x203C, 0x1F431] // an Int array
var myString = ""
for hexValue in myHexArray {
myString.append(UnicodeScalar(hexValue))
}
print(myString) // Cat‼
Lưu ý rằng đối với UTF-8 và UTF-16 việc chuyển đổi không phải lúc nào cũng dễ dàng như vậy. (Xem UTF-8, UTF-16, và UTF-32 câu hỏi.)
NSString và unichar
Nó cũng có thể làm việc với NSString
và unichar
trong Swift, nhưng bạn nên nhận ra rằng trừ khi bạn đã quen thuộc với C Mục tiêu và giỏi chuyển đổi cú pháp thành Swift, rất khó để tìm tài liệu tốt.
Ngoài ra, unichar
là một mảng UInt16
và như đã đề cập ở trên việc chuyển đổi từ UInt16
các giá trị vô hướng Unicode không phải lúc nào cũng dễ dàng (ví dụ, chuyển đổi cặp thay thế cho những thứ như biểu tượng cảm xúc và các nhân vật khác trong những chiếc máy bay đang trên).
cấu trúc chuỗi tùy chỉnh
Đối với những lý do nêu trong câu hỏi, tôi đã kết thúc không sử dụng bất kỳ các phương pháp trên. Thay vào đó tôi đã viết cấu trúc chuỗi của riêng mình, về cơ bản là một mảng của UInt32
để giữ các giá trị vô hướng mã Unicode.
Một lần nữa, đây không phải là giải pháp cho hầu hết mọi người. Trước tiên, hãy cân nhắc sử dụng extensions nếu bạn chỉ cần mở rộng chức năng của String
hoặc Character
một chút.
Nhưng nếu bạn thực sự cần làm việc độc quyền với các giá trị vô hướng Unicode, bạn có thể viết cấu trúc tùy chỉnh.
Những lợi thế là:
- Bạn không cần phải liên tục chuyển đổi giữa các loại (
String
, Character
, UnicodeScalar
, UInt32
, vv) khi thực hiện chuỗi thao tác.
- Sau khi thao tác Unicode hoàn tất, chuyển đổi cuối cùng thành
String
thật dễ dàng.
- Dễ dàng thêm các phương pháp hơn khi họ là cần thiết
- đơn giản hoá chuyển đổi mã từ Java hoặc các ngôn ngữ khác
Disadavantages là:
- làm cho mã ít di động và ít có thể đọc cho các nhà phát triển Swift khác
- không được kiểm tra và tối ưu hóa tốt như các loại Swift gốc
- đó là một tệp khác phải được bao gồm trong dự án mỗi khi bạn cần nó
Bạn có thể tự tạo, nhưng đây là tài liệu tham khảo của tôi. Phần khó nhất là making it Hashable.
// This struct is an array of UInt32 to hold Unicode scalar values
// Version 3.4.0 (Swift 3 update)
struct ScalarString: Sequence, Hashable, CustomStringConvertible {
fileprivate var scalarArray: [UInt32] = []
init() {
// does anything need to go here?
}
init(_ character: UInt32) {
self.scalarArray.append(character)
}
init(_ charArray: [UInt32]) {
for c in charArray {
self.scalarArray.append(c)
}
}
init(_ string: String) {
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// Generator in order to conform to SequenceType protocol
// (to allow users to iterate as in `for myScalarValue in myScalarString` { ... })
func makeIterator() -> AnyIterator<UInt32> {
return AnyIterator(scalarArray.makeIterator())
}
// append
mutating func append(_ scalar: UInt32) {
self.scalarArray.append(scalar)
}
mutating func append(_ scalarString: ScalarString) {
for scalar in scalarString {
self.scalarArray.append(scalar)
}
}
mutating func append(_ string: String) {
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// charAt
func charAt(_ index: Int) -> UInt32 {
return self.scalarArray[index]
}
// clear
mutating func clear() {
self.scalarArray.removeAll(keepingCapacity: true)
}
// contains
func contains(_ character: UInt32) -> Bool {
for scalar in self.scalarArray {
if scalar == character {
return true
}
}
return false
}
// description (to implement Printable protocol)
var description: String {
return self.toString()
}
// endsWith
func endsWith() -> UInt32? {
return self.scalarArray.last
}
// indexOf
// returns first index of scalar string match
func indexOf(_ string: ScalarString) -> Int? {
if scalarArray.count < string.length {
return nil
}
for i in 0...(scalarArray.count - string.length) {
for j in 0..<string.length {
if string.charAt(j) != scalarArray[i + j] {
break // substring mismatch
}
if j == string.length - 1 {
return i
}
}
}
return nil
}
// insert
mutating func insert(_ scalar: UInt32, atIndex index: Int) {
self.scalarArray.insert(scalar, at: index)
}
mutating func insert(_ string: ScalarString, atIndex index: Int) {
var newIndex = index
for scalar in string {
self.scalarArray.insert(scalar, at: newIndex)
newIndex += 1
}
}
mutating func insert(_ string: String, atIndex index: Int) {
var newIndex = index
for scalar in string.unicodeScalars {
self.scalarArray.insert(scalar.value, at: newIndex)
newIndex += 1
}
}
// isEmpty
var isEmpty: Bool {
return self.scalarArray.count == 0
}
// hashValue (to implement Hashable protocol)
var hashValue: Int {
// DJB Hash Function
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
// length
var length: Int {
return self.scalarArray.count
}
// remove character
mutating func removeCharAt(_ index: Int) {
self.scalarArray.remove(at: index)
}
func removingAllInstancesOfChar(_ character: UInt32) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar != character {
returnString.append(scalar)
}
}
return returnString
}
func removeRange(_ range: CountableRange<Int>) -> ScalarString? {
if range.lowerBound < 0 || range.upperBound > scalarArray.count {
return nil
}
var returnString = ScalarString()
for i in 0..<scalarArray.count {
if i < range.lowerBound || i >= range.upperBound {
returnString.append(scalarArray[i])
}
}
return returnString
}
// replace
func replace(_ character: UInt32, withChar replacementChar: UInt32) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar == character {
returnString.append(replacementChar)
} else {
returnString.append(scalar)
}
}
return returnString
}
func replace(_ character: UInt32, withString replacementString: String) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar == character {
returnString.append(replacementString)
} else {
returnString.append(scalar)
}
}
return returnString
}
func replaceRange(_ range: CountableRange<Int>, withString replacementString: ScalarString) -> ScalarString {
var returnString = ScalarString()
for i in 0..<scalarArray.count {
if i < range.lowerBound || i >= range.upperBound {
returnString.append(scalarArray[i])
} else if i == range.lowerBound {
returnString.append(replacementString)
}
}
return returnString
}
// set (an alternative to myScalarString = "some string")
mutating func set(_ string: String) {
self.scalarArray.removeAll(keepingCapacity: false)
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// split
func split(atChar splitChar: UInt32) -> [ScalarString] {
var partsArray: [ScalarString] = []
if self.scalarArray.count == 0 {
return partsArray
}
var part: ScalarString = ScalarString()
for scalar in self.scalarArray {
if scalar == splitChar {
partsArray.append(part)
part = ScalarString()
} else {
part.append(scalar)
}
}
partsArray.append(part)
return partsArray
}
// startsWith
func startsWith() -> UInt32? {
return self.scalarArray.first
}
// substring
func substring(_ startIndex: Int) -> ScalarString {
// from startIndex to end of string
var subArray: ScalarString = ScalarString()
for i in startIndex..<self.length {
subArray.append(self.scalarArray[i])
}
return subArray
}
func substring(_ startIndex: Int, _ endIndex: Int) -> ScalarString {
// (startIndex is inclusive, endIndex is exclusive)
var subArray: ScalarString = ScalarString()
for i in startIndex..<endIndex {
subArray.append(self.scalarArray[i])
}
return subArray
}
// toString
func toString() -> String {
var string: String = ""
for scalar in self.scalarArray {
if let validScalor = UnicodeScalar(scalar) {
string.append(Character(validScalor))
}
}
return string
}
// trim
// removes leading and trailing whitespace (space, tab, newline)
func trim() -> ScalarString {
//var returnString = ScalarString()
let space: UInt32 = 0x00000020
let tab: UInt32 = 0x00000009
let newline: UInt32 = 0x0000000A
var startIndex = self.scalarArray.count
var endIndex = 0
// leading whitespace
for i in 0..<self.scalarArray.count {
if self.scalarArray[i] != space &&
self.scalarArray[i] != tab &&
self.scalarArray[i] != newline {
startIndex = i
break
}
}
// trailing whitespace
for i in stride(from: (self.scalarArray.count - 1), through: 0, by: -1) {
if self.scalarArray[i] != space &&
self.scalarArray[i] != tab &&
self.scalarArray[i] != newline {
endIndex = i + 1
break
}
}
if endIndex <= startIndex {
return ScalarString()
}
return self.substring(startIndex, endIndex)
}
// values
func values() -> [UInt32] {
return self.scalarArray
}
}
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.scalarArray == right.scalarArray
}
func +(left: ScalarString, right: ScalarString) -> ScalarString {
var returnString = ScalarString()
for scalar in left.values() {
returnString.append(scalar)
}
for scalar in right.values() {
returnString.append(scalar)
}
return returnString
}
Câu hỏi của bạn không rõ ràng với tôi. 'count (string)' cung cấp số lượng "các cụm đồ thị unicode mở rộng", 'count (string.utf16)' cho số lượng các điểm mã UTF-16 được yêu cầu cho cùng một chuỗi (đó là độ dài của 'NSString' tương ứng. hoặc 'CFString'). (Và 'count (string.utf8)' sẽ cho biết số điểm mã UTF-8). - Câu hỏi * "Tôi có nên làm một cái gì đó như suForm1.utf16 mỗi khi tôi tham khảo một String?" * Không thể được trả lời nói chung, nó phụ thuộc vào những gì bạn cần đếm cho. –
Tôi cần làm việc độc quyền (tôi nghĩ) với các điểm mã UTF-16. Đếm chiều dài của một chuỗi (trong UTF-16) là một điều tôi sẽ cần phải làm, nhưng nó chỉ là một ví dụ. Tôi cũng sẽ cần phải làm những việc như so sánh các ký tự (đó là các điểm mã UTF-16, không chỉ các cụm grapheme mà Swift xem xét bằng nhau). Tôi có nên sử dụng 'Chuỗi' hoặc' NSString' hay cái gì khác không? – Suragch
Ý của bạn là gì theo "độ dài" ... Số byte? Đơn vị mã? Mã điểm? Cụm đồ thị? Điểm ảnh? Và tương tự cho "so sánh" ... Mã điểm bình đẳng? Tương đương Canonical? Tương thích tương thích? –