[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"

startIndexendIndex

  • 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()
}


답변