简体   繁体   中英

Swift Type 'string.index' has no subscript members

I'm currently converting C++ code to Swift and I've gotten stuck on one part. The parameter passed into the function is a string and the area where I'm stuck is when attempting to set a variable based on the second to last character of a string to check for a certain character.

The error shows up on this line:

line[i-1]

I've tried casting this value to an Int but this didn't work:

Int(line[i - 1])

I've also tried to see if the string's startIndex function which takes a Int would work but it didn't:

line.startIndex[i - 1]

Here is the full function:

func scanStringForSpecificCharacters(line: String){

    var maxOpen: Int = 0;
    var minOpen: Int = 0;

    minOpen = 0;
    maxOpen = 0;
    var i = 0
    while i < line.characters.count {
        for character in line.characters {

            //var c: Character = line[i];
            if character == "(" {
                maxOpen += 1;
                if i == 0 || line[i - 1] != ":" {
                    minOpen += 1;
                }
            }
            else if character == ")"{
                minOpen = max(0,minOpen-1);
                if i == 0 || line[i-1] != ":"{
                    maxOpen -= 1;
                }
                if maxOpen < 0{
                    break;
                }
            }
        }

        if maxOpen >= 0 && minOpen == 0{
            print("YES")
        }else{
            print("NO")
        }
    }
}

Strings in Swift aren't indexed collections and instead you can access one of four different views: characters, UTF8, UTF16, or unicodescalars.

This is because Swift supports unicode, where an individual characters may actually be composed of multiple unicode scalars.

Here's a post that really helped me wrap my head around this: https://oleb.net/blog/2016/08/swift-3-strings/

Anyway, to answer you question you'll need to create an index using index(after:) , index(before:) , or index(_, offsetBy:) .

In your case you'd want to do something like this:

line.index(line.endIndex, offsetBy: -2) // second to last character

Also, you'll probably find it easier to iterate directly using a String.Index type rather than Int :

let line = "hello"

var i = line.startIndex
while i < line.endIndex {
    print(line[i])
    i = line.index(after: i)
}
// prints -> 
// h
// e
// l
// l
// o

Working with Strings in Swift was changed several times during it's evolution and it doesn't look like C++ at all. You cannot subscript string to obtain individual characters, you should use index class for that. I recommend you read this article: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

As already pointed out in the other answers, the compiler error is caused by the problem that you cannot index a Swift String with integers.

Another problem in your code is that you have a nested loop which is probably not intended.

Actually I would try to avoid string indexing at all and only enumerate the characters, if possible. In your case, you can easily keep track of the preceding character in a separate variable:

var lastChar: Character = " " // Anything except ":"

for char in line.characters {
    if char == "(" {
        maxOpen += 1;
        if lastChar != ":" {
            minOpen += 1;
        }
    }

    // ...

    lastChar = char
}

Or, since you only need to know if the preceding character is a colon:

var lastIsColon = false

for char in string.characters {
    if char == "(" {
        maxOpen += 1;
        if !lastIsColon {
            minOpen += 1;
        }
    }

    // ...

    lastIsColon = char == ":"
}

Another possible approach is to iterate over the string and a shifted view of the string in parallel:

for (lastChar, char) in zip([" ".characters, line.characters].joined(), line.characters) {

    // ...

}

As others have already explained, trying to index into Swift strings is a pain. As a minimal change to your code, I would recommend that you just create an array of the characters in your line up front:

let linechars = Array(line.characters)

And then anywhere you need to index into the line, use linechars :

This:

if i == 0 || line[i-1] != ":" {

becomes:

if i == 0 || linechars[i-1] != ":" {

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM