web-dev-qa-db-ja.com

'subscript'は使用できません:CountableClosedRange <Int>で文字列に添え字を付けることはできません。説明についてはドキュメントのコメントを参照してください

Swift 4では、添え字構文を使用してSubstringStringを取得しようとすると、このエラーが発生します。

'subscript'は使用できません:CountableClosedRangeを使用して文字列に添え字を付けることはできません。説明についてはドキュメントのコメントを参照してください

例えば:

let myString: String = "foobar"
let mySubstring: Substring = myString[1..<3]

2つの質問:

  1. このエラーを解決するにはどうすればよいですか?
  2. エラーで言及された「議論のためのドキュメンテーションコメント」はどこにありますか?
35
Barry Jones
  1. "palindrome"[1..<3]"palindrome"[1...3]などの文字列に添え字を使用する場合は、これらの拡張機能を使用します。

Swift 4

extension String {
    subscript (bounds: CountableClosedRange<Int>) -> String {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return String(self[start...end])
    }

    subscript (bounds: CountableRange<Int>) -> String {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return String(self[start..<end])
    }
}

Swift 3

Swift 3の場合は、return self[start...end]およびreturn self[start..<end]に置き換えます。

  1. AppleはこれをSwift言語に組み込みませんでした。「文字」の定義は、文字列のエンコード方法に依存するためです。文字は8〜64ビットで、デフォルトは通常UTF-16です。 String.Indexで他の文字列エンコーディングを指定できます。

これはドキュメントです Xcodeエラーが参照しています。

TF-8やUTF-16などの文字列エンコーディングの詳細

59
p-sun

質問(および自己回答)には2つの問題があります。

Intを使用した文字列の添字付けは、Swiftの標準ライブラリでは使用できませんでした。 Swiftが存在する限り、このコードは無効です。

let mySubstring: Substring = myString[1..<3]

新しいString.Index(encodedOffset: )は、UTF-16(16ビット)エンコーディングでインデックスを返します。 Swiftの文字列はExtended Grapheme Clusterを使用します。これは、8〜64ビットで文字を保存できます。絵文字は非常に優れたデモンストレーションに役立ちます。

let myString = "????????????????????????????????"
let lowerBound = String.Index(encodedOffset: 1)
let upperBound = String.Index(encodedOffset: 3)
let mySubstring = myString[lowerBound..<upperBound]

// Expected: Canadian and UK flags
// Actual  : gibberish
print(mySubstring)

実際、Swift 4でString.Indexを取得しても、良くも悪くもまったく変わりません。

let myString = "????????????????????????????????"
let lowerBound = myString.index(myString.startIndex, offsetBy: 1)
let upperBound = myString.index(myString.startIndex, offsetBy: 3)
let mySubstring = myString[lowerBound..<upperBound]

print(mySubstring)
19
Code Different
  1. このエラーを解決するにはどうすればよいですか?

このエラーは、添え字形式でIntを使用できないことを意味します。String.Indexを使用する必要があります。これは、encodedOffset Intで初期化できます。

let myString: String = "foobar"
let lowerBound = String.Index.init(encodedOffset: 1)
let upperBound = String.Index.init(encodedOffset: 3)
let mySubstring: Substring = myString[lowerBound..<upperBound]
  1. エラーで言及された「議論のためのドキュメンテーションコメント」はどこにありますか?

GitHubのSwift St​​andard LibraryリポジトリのUnavailableStringAPIs.Swift.gybというファイルにあるロックされたファイリングキャビネットの底にあり、使用されていないトイレに立ち往生しているドアに「ヒョウに気をつけて」というサインがあります。 リンク

11
Barry Jones

p-Sunの回答 に基づく

Swift 4

extension StringProtocol {
    subscript(bounds: CountableClosedRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(start, offsetBy: bounds.count)
        return self[start..<end]
    }

    subscript(bounds: CountableRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(start, offsetBy: bounds.count)
        return self[start..<end]
    }
}

注目すべき変更:

  • StringProtocolの拡張子になりました。これにより、Substringなどの採用者もこれらの添え字を取得できます。
  • 終了インデックスは、文字列の開始ではなく、境界の開始インデックスからオフセットされます。これにより、Stringの開始から2回の移動が防止されます。 indexメソッドはO(n)です。nはiからのオフセットです。
2
Justin Oroz

p-Sun'sJustin Oroz の両方の答えに基づいて、文字列の開始と終了を超える無効なインデックスから保護する2つの拡張機能があります(これらの拡張機能は、文字列の再スキャンも回避します範囲の終わりでインデックスを見つけるためだけに最初から):

extension String {

    subscript(bounds: CountableClosedRange<Int>) -> String {
        let lowerBound = max(0, bounds.lowerBound)
        guard lowerBound < self.count else { return "" }

        let upperBound = min(bounds.upperBound, self.count-1)
        guard upperBound >= 0 else { return "" }

        let i = index(startIndex, offsetBy: lowerBound)
        let j = index(i, offsetBy: upperBound-lowerBound)

        return String(self[i...j])
    }

    subscript(bounds: CountableRange<Int>) -> String {
        let lowerBound = max(0, bounds.lowerBound)
        guard lowerBound < self.count else { return "" }

        let upperBound = min(bounds.upperBound, self.count)
        guard upperBound >= 0 else { return "" }

        let i = index(startIndex, offsetBy: lowerBound)
        let j = index(i, offsetBy: upperBound-lowerBound)

        return String(self[i..<j])
    }
}
2
Chris Frederick
extension String {

    subscript(bounds: CountableClosedRange<Int>) -> String {
        let lowerBound = max(0, bounds.lowerBound)
        guard lowerBound < self.count else { return "" }

        let upperBound = min(bounds.upperBound, self.count-1)
        guard upperBound >= 0 else { return "" }

        let i = index(startIndex, offsetBy: lowerBound)
        let j = index(i, offsetBy: upperBound-lowerBound)

        return String(self[i...j])
    }

    subscript(bounds: CountableRange<Int>) -> String {
        let lowerBound = max(0, bounds.lowerBound)
        guard lowerBound < self.count else { return "" }

        ***let upperBound = min(bounds.upperBound, self.count-1)***
        guard upperBound >= 0 else { return "" }

        let i = index(startIndex, offsetBy: lowerBound)
        let j = index(i, offsetBy: upperBound-lowerBound)

        return String(self[i..<j])
    }
}
0
chan sam

範囲付きの添字の結果がSubstringではなくSubstring?であるため、このエラーが発生します。

次のコードを使用する必要があります。

let myString: String = "foobar"
let mySubstring: Substring? = myString[1..<3]
0
Andrey M.