[swift] Swift에서 String.Index는 어떻게 작동합니까?
Swift 3로 이전 코드와 답변 중 일부를 업데이트했지만 Swift Strings and Indexing에 도달했을 때 사물을 이해하는 것이 고통 스러웠습니다.
구체적으로 다음을 시도했습니다.
let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error
두 번째 줄에서 다음 오류가 발생했습니다.
‘advancedBy’를 사용할 수 없음 : 인덱스를 n 단계 앞으로 진행하려면 인덱스를 생성 한 CharacterView 인스턴스에서 ‘index (_ : offsetBy :)’를 호출합니다.
그보고 String
다음과 같은 방법이있다.
str.index(after: String.Index)
str.index(before: String.Index)
str.index(String.Index, offsetBy: String.IndexDistance)
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)
처음에는 정말 혼란 스러웠 기 때문에 이해할 때까지 놀기 시작했습니다. 사용 방법을 보여주기 위해 아래에 답변을 추가하고 있습니다.
답변
다음 예제는 모두
var str = "Hello, playground"
startIndex
과 endIndex
startIndex
첫 번째 문자의 색인입니다.endIndex
마지막 문자 뒤 의 색인 입니다.
예
// character
str[str.startIndex] // H
str[str.endIndex] // error: after last character
// range
let range = str.startIndex..<str.endIndex
str[range] // "Hello, playground"
Swift 4의 단측 범위 를 사용하면 범위를 다음 형식 중 하나로 단순화 할 수 있습니다.
let range = str.startIndex...
let range = ..<str.endIndex
명확성을 위해 다음 예제에서는 전체 형식을 사용하지만 가독성을 위해 코드에서 단측 범위를 사용하는 것이 좋습니다.
after
에서와 같이 : index(after: String.Index)
after
주어진 색인 바로 뒤의 문자 색인을 참조합니다.
예
// character
let index = str.index(after: str.startIndex)
str[index] // "e"
// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range] // "ello, playground"
before
에서와 같이 : index(before: String.Index)
before
주어진 색인 바로 앞의 문자 색인을 참조합니다.
예
// character
let index = str.index(before: str.endIndex)
str[index] // d
// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range] // Hello, playgroun
offsetBy
에서와 같이 : index(String.Index, offsetBy: String.IndexDistance)
offsetBy
값은 주어진 인덱스에서 긍정적 또는 부정적 개시 될 수있다. 그것은 유형이지만String.IndexDistance
, 당신은 그것에게를 제공 할 수 있습니다Int
.
예
// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index] // p
// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range] // play
limitedBy
에서와 같이 : index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)
- 는
limitedBy
반드시 오프셋 인덱스가 범위의 외출이 발생하지 않음을 만들기위한 유용합니다. 경계 인덱스입니다. 오프셋이 제한을 초과 할 수 있으므로이 메서드는 Optional을 반환합니다.nil
인덱스가 범위를 벗어난 경우 반환 됩니다.
예
// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
str[index] // p
}
있었다 오프셋 경우 77
대신 7
다음 if
문을했을 것이다 건너 뜁니다.
String.Index가 필요한 이유는 무엇입니까?
문자열에 대한 색인 을 사용하는 것이 훨씬 쉬울 것 Int
입니다. String.Index
모든 문자열에 대해 새 문자열 을 만들어야하는 이유 는 Swift의 문자가 후드 아래에서 모두 같은 길이가 아니기 때문입니다. 단일 Swift 문자는 하나, 둘 또는 그 이상의 유니 코드 코드 포인트로 구성 될 수 있습니다. 따라서 각각의 고유 한 문자열은 해당 문자의 인덱스를 계산해야합니다.
Int 인덱스 확장 뒤에 이러한 복잡성을 숨기는 것이 가능하지만 그렇게하는 것을 꺼려합니다. 실제로 일어나고있는 일을 상기시키는 것이 좋습니다.
답변
func change(string: inout String) {
var character: Character = .normal
enum Character {
case space
case newLine
case normal
}
for i in stride(from: string.count - 1, through: 0, by: -1) {
// first get index
let index: String.Index?
if i != 0 {
index = string.index(after: string.index(string.startIndex, offsetBy: i - 1))
} else {
index = string.startIndex
}
if string[index!] == "\n" {
if character != .normal {
if character == .newLine {
string.remove(at: index!)
} else if character == .space {
let number = string.index(after: string.index(string.startIndex, offsetBy: i))
if string[number] == " " {
string.remove(at: number)
}
character = .newLine
}
} else {
character = .newLine
}
} else if string[index!] == " " {
if character != .normal {
string.remove(at: index!)
} else {
character = .space
}
} else {
character = .normal
}
}
// startIndex
guard string.count > 0 else { return }
if string[string.startIndex] == "\n" || string[string.startIndex] == " " {
string.remove(at: string.startIndex)
}
// endIndex - here is a little more complicated!
guard string.count > 0 else { return }
let index = string.index(before: string.endIndex)
if string[index] == "\n" || string[index] == " " {
string.remove(at: index)
}
}
답변
tableViewController 내부에 UITextView를 만듭니다. 나는 function : textViewDidChange를 사용한 다음 return-key-input을 확인했습니다. 리턴 키 입력이 감지되면 리턴 키 입력을 삭제하고 키보드를 닫습니다.
func textViewDidChange(_ textView: UITextView) {
tableView.beginUpdates()
if textView.text.contains("\n"){
textView.text.remove(at: textView.text.index(before: textView.text.endIndex))
textView.resignFirstResponder()
}
tableView.endUpdates()
}