【问题标题】:How to create range in Swift?如何在 Swift 中创建范围?
【发布时间】:2015-07-17 14:10:17
【问题描述】:

在 Objective-c 中,我们使用 NSRange 创建范围

NSRange range;

那么如何在 Swift 中创建范围呢?

【问题讨论】:

  • 你需要这个范围做什么? Swift 字符串使用Range<String.Index>,但有时需要使用NSStringNSRange,所以更多的上下文会很有用。 – 但是看看stackoverflow.com/questions/24092884/…

标签: swift range


【解决方案1】:

这样使用

var start = str.startIndex // Start at the string's start index
var end = advance(str.startIndex, 5) // Take start index and advance 5 characters forward
var range: Range<String.Index> = Range<String.Index>(start: start,end: end)

let firstFiveDigit =  str.substringWithRange(range)

print(firstFiveDigit)

输出:你好

【讨论】:

  • 我看到他们有“范围”数据类型。那么如何使用呢?
  • @vichhai 你能详细说明你想在哪里使用吗?
  • 像目标c:NSRange范围;
【解决方案2】:

你可以这样使用

let nsRange = NSRange(location: someInt, length: someInt)

let myNSString = bigTOTPCode as NSString //12345678
let firstDigit = myNSString.substringWithRange(NSRange(location: 0, length: 1)) //1
let secondDigit = myNSString.substringWithRange(NSRange(location: 1, length: 1)) //2
let thirdDigit = myNSString.substringWithRange(NSRange(location: 2, length: 4)) //3456

【讨论】:

    【解决方案3】:

    Xcode 8 beta 2 • Swift 3

    let myString = "Hello World"
    let myRange = myString.startIndex..<myString.index(myString.startIndex, offsetBy: 5)
    let mySubString = myString.substring(with: myRange)   // Hello
    

    Xcode 7 • Swift 2.0

    let myString = "Hello World"
    let myRange = Range<String.Index>(start: myString.startIndex, end: myString.startIndex.advancedBy(5))
    
    let mySubString = myString.substringWithRange(myRange)   // Hello
    

    或者干脆

    let myString = "Hello World"
    let myRange = myString.startIndex..<myString.startIndex.advancedBy(5)
    let mySubString = myString.substringWithRange(myRange)   // Hello
    

    【讨论】:

      【解决方案4】:

      (1..

      返回...

      范围 = 1..

      【讨论】:

      • 这如何回答这个问题?
      【解决方案5】:

      为 Swift 4 更新

      Swift 范围比 NSRange 更复杂,而且在 Swift 3 中也没有变得更容易。如果您想尝试了解其中一些复杂性背后的原因,请阅读 thisthis。我将向您展示如何创建它们以及何时可以使用它们。

      封闭范围:a...b

      这个range operator 创建一个包含元素a 元素b 的Swift 范围,即使b 是一个类型的最大可能值(例如Int.max) .有两种不同类型的封闭范围:ClosedRangeCountableClosedRange

      1。 ClosedRange

      Swift 中所有范围的元素都是可比较的(即它们符合 Comparable 协议)。这允许您访问集合中范围内的元素。这是一个例子:

      let myRange: ClosedRange = 1...3
      
      let myArray = ["a", "b", "c", "d", "e"]
      myArray[myRange] // ["b", "c", "d"]
      

      但是,ClosedRange 是不可数的(即,它不符合序列协议)。这意味着您不能使用 for 循环遍历元素。为此,您需要CountableClosedRange

      2。 CountableClosedRange

      这与上一个类似,只是现在范围也可以迭代。

      let myRange: CountableClosedRange = 1...3
      
      let myArray = ["a", "b", "c", "d", "e"]
      myArray[myRange] // ["b", "c", "d"]
      
      for index in myRange {
          print(myArray[index])
      }
      

      半开范围:a..&lt;b

      此范围运算符包含元素a,但不包含元素b。和上面一样,有两种不同类型的半开范围:RangeCountableRange

      1。 Range

      ClosedRange 一样,您可以使用Range 访问集合的元素。示例:

      let myRange: Range = 1..<3
      
      let myArray = ["a", "b", "c", "d", "e"]
      myArray[myRange] // ["b", "c"]
      

      同样,您不能迭代 Range,因为它只是可比较的,而不是可跨步的。

      2。 CountableRange

      CountableRange 允许迭代。

      let myRange: CountableRange = 1..<3
      
      let myArray = ["a", "b", "c", "d", "e"]
      myArray[myRange] // ["b", "c"]
      
      for index in myRange {
          print(myArray[index])
      }
      

      NSRange

      你可以(必须)在 Swift 中有时仍然使用 NSRange(例如,在制作 attributed strings 时),所以知道如何制作是很有帮助的。

      let myNSRange = NSRange(location: 3, length: 2)
      

      注意这是位置和长度,而不是开始索引和结束索引。此处的示例与 Swift 范围 3..&lt;5 的含义相似。但是,由于类型不同,它们是不可互换的。

      字符串范围

      .....&lt; 范围运算符是创建范围的简写方式。例如:

      let myRange = 1..<3
      

      创建相同范围的常用方法是

      let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
      

      可以看到这里的索引类型是Int。不过,这不适用于String,因为字符串是由字符组成的,而且并非所有字符的大小都相同。 (阅读this 了解更多信息。)例如,像? 这样的表情符号比字母“b”占用更多空间。

      NSRange 的问题

      尝试使用 NSRange 和带有表情符号的 NSString 进行试验,您就会明白我的意思了。头痛。

      let myNSRange = NSRange(location: 1, length: 3)
      
      let myNSString: NSString = "abcde"
      myNSString.substring(with: myNSRange) // "bcd"
      
      let myNSString2: NSString = "a?cde"
      myNSString2.substring(with: myNSRange) // "?c"    Where is the "d"!?
      

      笑脸需要两个 UTF-16 编码单元来存储,所以它给出了不包括“d”的意外结果。

      快速解决方案

      因此,对于 Swift 字符串,您使用 Range&lt;String.Index&gt;,而不是 Range&lt;Int&gt;。字符串索引是根据特定字符串计算的,以便它知道是否有任何表情符号或扩展字素簇。

      例子

      var myString = "abcde"
      let start = myString.index(myString.startIndex, offsetBy: 1)
      let end = myString.index(myString.startIndex, offsetBy: 4)
      let myRange = start..<end
      myString[myRange] // "bcd"
      
      myString = "a?cde"
      let start2 = myString.index(myString.startIndex, offsetBy: 1)
      let end2 = myString.index(myString.startIndex, offsetBy: 4)
      let myRange2 = start2..<end2
      myString[myRange2] // "?cd"
      

      单边范围:a......b..&lt;b

      在 Swift 4 中,事情被简化了一点。只要可以推断出范围的起点或终点,您就可以不使用它。

      内部

      您可以使用单边整数范围来迭代集合。以下是来自documentation 的一些示例。

      // iterate from index 2 to the end of the array
      for name in names[2...] {
          print(name)
      }
      
      // iterate from the beginning of the array to index 2
      for name in names[...2] {
          print(name)
      }
      
      // iterate from the beginning of the array up to but not including index 2
      for name in names[..<2] {
          print(name)
      }
      
      // the range from negative infinity to 5. You can't iterate forward
      // over this because the starting point in unknown.
      let range = ...5
      range.contains(7)   // false
      range.contains(4)   // true
      range.contains(-1)  // true
      
      // You can iterate over this but it will be an infinate loop 
      // so you have to break out at some point.
      let range = 5...
      

      字符串

      这也适用于字符串范围。如果您在一端使用str.startIndexstr.endIndex 创建范围,则可以将其关闭。编译器会推断出来。

      给定

      var str = "Hello, playground"
      let index = str.index(str.startIndex, offsetBy: 5)
      
      let myRange = ..<index    // Hello
      

      您可以使用...从索引转到str.endIndex

      var str = "Hello, playground"
      let index = str.index(str.endIndex, offsetBy: -10)
      let myRange = index...        // playground
      

      另见:

      备注

      • 您不能使用由一个字符串在不同字符串上创建的范围。
      • 如您所见,字符串范围在 Swift 中是一个难题,但它们确实可以更好地处理表情符号和其他 Unicode 标量。

      进一步研究

      【讨论】:

      • 我的评论与“NSRange 问题”小节有关。 NSString 在内部以 UTF-16 编码存储其字符。完整的 unicode 标量是 21 位的。笑脸字符 (U+1F600) 不能存储在单个 16 位代码单元中,因此它分布在 2 上。NSRange 基于 16 位代码单元进行计数。在这个例子中,3 个代码单元恰好只代表 2 个字符
      【解决方案6】:

      我创建了以下扩展:

      extension String {
          func substring(from from:Int, to:Int) -> String? {
              if from<to && from>=0 && to<self.characters.count {
                  let rng = self.startIndex.advancedBy(from)..<self.startIndex.advancedBy(to)
                  return self.substringWithRange(rng)
              } else {
                  return nil
              }
          }
      }
      

      使用示例:

      print("abcde".substring(from: 1, to: 10)) //nil
      print("abcde".substring(from: 2, to: 4))  //Optional("cd")
      print("abcde".substring(from: 1, to: 0))  //nil
      print("abcde".substring(from: 1, to: 1))  //nil
      print("abcde".substring(from: -1, to: 1)) //nil
      

      【讨论】:

        【解决方案7】:
        func replace(input: String, start: Int,lenght: Int, newChar: Character) -> String {
            var chars = Array(input.characters)
        
            for i in start...lenght {
                guard i < input.characters.count else{
                    break
                }
                chars[i] = newChar
            }
            return String(chars)
        }
        

        【讨论】:

          【解决方案8】:

          令我惊讶的是,即使在 Swift 4 中,仍然没有使用 Int 表示字符串范围的简单本地方法。只有prefixsuffix 是让您提供 Int 作为按范围获取子字符串的一种方式的 String 方法。

          手头有一些转换工具很有用,这样我们在与字符串对话时可以像 NSRange 一样对话。这是一个获取位置和长度的实用程序,就像 NSRange 一样,并返回 Range&lt;String.Index&gt;

          func range(_ start:Int, _ length:Int) -> Range<String.Index> {
              let i = self.index(start >= 0 ? self.startIndex : self.endIndex,
                  offsetBy: start)
              let j = self.index(i, offsetBy: length)
              return i..<j
          }
          

          例如,"hello".range(0,1)" 是包含"hello" 的第一个字符的Range&lt;String.Index&gt;。作为奖励,我允许否定位置:"hello".range(-1,1)" 是包含"hello" 的最后一个字符的Range&lt;String.Index&gt;

          Range&lt;String.Index&gt; 转换为 NSRange 也很有用,在您必须与 Cocoa 对话时(例如,在处理 NSAttributedString 属性范围时)。 Swift 4 提供了一种本地方式来做到这一点:

          let nsrange = NSRange(range, in:s) // where s is the string
          

          因此,我们可以编写另一个实用程序,直接从字符串位置和长度转到 NSRange:

          extension String {
              func nsRange(_ start:Int, _ length:Int) -> NSRange {
                  return NSRange(self.range(start,length), in:self)
              }
          }
          

          【讨论】:

            【解决方案9】:

            如果有人想创建 NSRange 对象可以创建为:

            let range: NSRange = NSRange.init(location: 0, length: 5)
            

            这将创建位置为 0、长度为 5 的范围

            【讨论】:

              【解决方案10】:

              我想这样做:

              print("Hello"[1...3])
              // out: Error
              

              但不幸的是,我不能自己写下标,因为讨厌的下标占用了名称空间。

              我们可以这样做:

              print("Hello"[range: 1...3])
              // out: ell 
              

              只需将其添加到您的项目中:

              extension String {
                  subscript(range: ClosedRange<Int>) -> String {
                      get {
                          let start = String.Index(utf16Offset: range.lowerBound, in: self)
                          let end = String.Index(utf16Offset: range.upperBound, in: self)
                          return String(self[start...end])
                      }
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 2014-07-23
                • 1970-01-01
                • 2010-10-07
                • 1970-01-01
                • 2014-10-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多