【问题标题】:What is the most succinct way to remove the first character from a string in Swift?在 Swift 中从字符串中删除第一个字符的最简洁的方法是什么?
【发布时间】:2015-04-11 07:49:45
【问题描述】:

我想从字符串中删除第一个字符。到目前为止,我想出的最简洁的东西是:

display.text = display.text!.substringFromIndex(advance(display.text!.startIndex, 1))

我知道由于 Unicode,我们无法使用 Int 对字符串进行索引,但这个解决方案似乎非常冗长。还有其他我忽略的方法吗?

【问题讨论】:

  • 实际上你可以通过将display.text! 转换为NSString 来避免整个advance 的事情。我并不是说这是一个好的解决方案——只是纠正一个可能的误解。使用 NSString,您可以 使用 Int 对其进行索引。 - 你不能用 Int 索引的原因不是因为 Unicode;这是因为一个字符可以由多个复合代码点组成。
  • 如果您这样做是为了将String 大写,那么 Swift 3 已将capitalized 函数引入String

标签: string swift


【解决方案1】:

如果您使用的是 Swift 3,则可以忽略此答案的第二部分。好消息是,现在这实际上又简洁了!只需使用 String 的新 remove(at:) 方法即可。

var myString = "Hello, World"
myString.remove(at: myString.startIndex)

myString // "ello, World"

我喜欢这个全局 dropFirst() 函数。

let original = "Hello" // Hello
let sliced = dropFirst(original) // ello

它简短、清晰,适用于任何符合 Sliceable 协议的东西。

如果您使用的是 Swift 2,此答案已更改。您仍然可以使用 dropFirst,但不能不从字符串 characters 属性中删除第一个字符,然后将结果转换回字符串。 dropFirst 也变成了一个方法,而不是一个函数。

let original = "Hello" // Hello
let sliced = String(original.characters.dropFirst()) // ello

另一种方法是使用后缀函数拼接字符串的UTF16View。当然,这之后也必须转换回字符串。

let original = "Hello" // Hello
let sliced = String(suffix(original.utf16, original.utf16.count - 1)) // ello

所有这一切都是说,我最初提供的解决方案在新版本的 Swift 中并不是最简洁的方法。如果您正在寻找一个简短而直观的解决方案,我建议您使用 removeAtIndex() 重新使用 @chris 的解决方案。

var original = "Hello" // Hello
let removedChar = original.removeAtIndex(original.startIndex)

original // ello

正如@vacawama 在下面的 cmets 中指出的那样,另一个不修改原始字符串的选项是使用 substringFromIndex。

let original = "Hello" // Hello
let substring = original.substringFromIndex(advance(original.startIndex, 1)) // ello

或者,如果您碰巧希望从字符串的开头和结尾删除一个字符,您可以使用 substringWithRange。一定要注意startIndex + n > endIndex - m时的情况。

let original = "Hello" // Hello

let newStartIndex = advance(original.startIndex, 1)
let newEndIndex = advance(original.endIndex, -1)

let substring = original.substringWithRange(newStartIndex..<newEndIndex) // ell

最后一行也可以用下标表示。

let substring = original[newStartIndex..<newEndIndex]

【讨论】:

  • 而且它不需要你踏入基金会世界。
  • 而且非常迅速/fp。
  • 谢谢,这样更优雅。多年来,我一直在使用 Objective C 进行编程,并且正在参加 Swift 的 iTunes U 斯坦福 iOS 编程课程。关于新范式,我还有很多东西要学习。
  • 请记住,使用 dropLast 或 dropFirst 时,请解开字符串,否则将无法正常工作。
  • 很好的答案!我认为值得注意的是 remove(at:) 方法的返回值:它是删除的字符,而不是新的字符串。例如,let removedCharacter = myString.remove(at: myString.startIndex)。我犯了返回方法调用结果的错误,忘记了这种方法就是这种情况。祝大家编码愉快!
【解决方案2】:

Swift 4 更新

在 Swift 4 中,String 再次符合Collection,因此可以使用dropFirstdropLast 来修剪字符串的开头和结尾。结果是Substring 类型,因此您需要将其传递给String 构造函数以取回String

let str = "hello"
let result1 = String(str.dropFirst())    // "ello"
let result2 = String(str.dropLast())     // "hell"

dropFirst()dropLast() 也采用 Int 来指定要删除的字符数:

let result3 = String(str.dropLast(3))    // "he"
let result4 = String(str.dropFirst(4))   // "o"

如果您指定要删除的字符多于字符串中的字符,则结果将是空字符串 ("")。

let result5 = String(str.dropFirst(10))  // ""

Swift 3 更新

如果您只想删除第一个字符并想更改原始字符串,请参阅@MickMacCallum 的答案。如果要在此过程中创建新字符串,请使用substring(from:)。使用String 的扩展名,您可以隐藏substring(from:)substring(to:) 的丑陋之处,以创建有用的补充来修剪String 的开头和结尾:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
        return substring(from: index(startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return substring(to: index(endIndex, offsetBy: -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

像之前的dropFirstdropLast 一样,如果字符串中没有足够的可用字母,这些函数将崩溃。调用者有责任正确使用它们。这是一个有效的设计决策。可以编写它们以返回一个可选的,然后必须由调用者解包。


Swift 2.x

唉,Swift 2dropFirstdropLast(之前的最佳解决方案)不像以前那么方便了。通过String的扩展,可以隐藏substringFromIndexsubstringToIndex的丑陋之处:

extension String {
    func chopPrefix(count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

"hello".chopPrefix()    // "ello"
"hello".chopPrefix(3)   // "lo"

"hello".chopSuffix()    // "hell"
"hello".chopSuffix(3)   // "he"

与之前的dropFirstdropLast 一样,如果字符串中没有足够的可用字母,这些函数将崩溃。调用者有责任正确使用它们。这是一个有效的设计决策。可以编写它们以返回一个可选的,然后必须由调用者解包。


Swift 1.2 中,您需要像这样调用chopPrefix

"hello".chopPrefix(count: 3)  // "lo"

或者您可以在函数定义中添加下划线_ 以隐藏参数名称:

extension String {
    func chopPrefix(_ count: Int = 1) -> String {
         return self.substringFromIndex(advance(self.startIndex, count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
        return self.substringToIndex(advance(self.endIndex, -count))
    }
}

【讨论】:

  • 我仍然对 Swift 的内置字符串操作的复杂程度感到震惊。我的意思是这是基本的,基本的东西;它不应该看起来像我正在实现解析器。感谢扩展以简化这一点。令人费解的是,Apple 不会将它添加到 String 类中!也许它仍然处于很大的变化之中。
  • 这就是他们所谓的“进化”孩子。如果我们不必每天都处理它,那将是很有趣的。我知道 String API 之所以如此,是有原因的,但是,真的,这必须推迟很多潜在的转换为这种语言的人。前面的评论是绝对正确的 - 这应该是基本的,基本的东西。
【解决方案3】:

斯威夫特 2.2

'advance' 不可用:在索引上调用 'advancedBy(n)' 方法

    func chopPrefix(count: Int = 1) -> String {
        return self.substringFromIndex(self.startIndex.advancedBy(count))
    }

    func chopSuffix(count: Int = 1) -> String {
        return self.substringFromIndex(self.endIndex.advancedBy(count))
    }

Swift 3.0

    func chopPrefix(_ count: Int = 1) -> String {
        return self.substring(from: self.characters.index(self.startIndex, offsetBy: count))
    }

    func chopSuffix(_ count: Int = 1) -> String {
       return self.substring(to: self.characters.index(self.endIndex, offsetBy: -count))
    }

Swift 3.2

作为字符集合的字符串内容视图。

@available(swift, deprecated: 3.2, message: "Please use String or Substring directly")
public var characters: String.CharacterView
func chopPrefix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(from: String.Index(encodedOffset: count))
    }
    return ""
}

func chopSuffix(_ count: Int = 1) -> String {
    if count >= 0 && count <= self.count {
        return self.substring(to: String.Index(encodedOffset: self.count - count))
    }
    return ""
}

斯威夫特 4

extension String {

    func chopPrefix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexStartOfText = self.index(self.startIndex, offsetBy: count)
            return String(self[indexStartOfText...])
        }
        return ""
    }

    func chopSuffix(_ count: Int = 1) -> String {
        if count >= 0 && count <= self.count {
            let indexEndOfText = self.index(self.endIndex, offsetBy: -count)
            return String(self[..<indexEndOfText])
        }
        return ""
    }
}

【讨论】:

  • 我想你忘了在chopSuffix函数中添加否定计数
【解决方案4】:

在 Swift 2 中,这样做:

let cleanedString = String(theString.characters.dropFirst())

我推荐https://www.mikeash.com/pyblog/friday-qa-2015-11-06-why-is-swifts-string-api-so-hard.html 来了解 Swift 字符串。

【讨论】:

    【解决方案5】:

    取决于您希望最终结果是什么(变异与非变异)。

    从 Swift 4.1 开始:

    变异:

    var str = "hello"
    str.removeFirst() // changes str 
    

    非变异:

    let str = "hello"
    let strSlice = str.dropFirst() // makes a slice without the first letter
    let str2 = String(strSlice)
    

    注意事项:

    • 为了清楚起见,我在nonmutating 示例中添加了一个额外的步骤。主观上,结合最后两个步骤会更简洁。
    • dropFirst 的命名对我来说似乎有点奇怪,因为如果我正确理解 Swift API Design GuidelinesdropFirst 应该真的类似于 dropingFirst,因为它是非变异的。只是一个想法 :)。

    【讨论】:

    • 确实,应该是droppingFirst——他们搞砸了!
    【解决方案6】:

    这个怎么样?

    s.removeAtIndex(s.startIndex)
    

    这当然假设您的字符串是可变的。它返回已删除的字符,但更改了原始字符串。

    【讨论】:

      【解决方案7】:

      前面的答案都还不错,但到今天为止,我认为这可能是 Swift 4 中从字符串中删除第一个字符的最简洁的方法:

      var line: String = "This is a string..."
      var char: Character? = nil
      
      char = line.removeFirst()
      
      print("char = \(char)")  // char = T
      print("line = \(line)")  // line = his is a string ...
      

      【讨论】:

        【解决方案8】:

        我不知道更简洁的开箱即用,但您可以轻松实现前缀++,例如,

        public prefix func ++ <I: ForwardIndexType>(index: I) -> I {
            return advance(index, 1)
        }
        

        之后你可以非常简洁地随心所欲地使用它:

        str.substringFromIndex(++str.startIndex)
        

        【讨论】:

          【解决方案9】:

          在 Swift 2 中使用这个字符串扩展:

          extension String
          {
              func substringFromIndex(index: Int) -> String
              {
                  if (index < 0 || index > self.characters.count)
                  {
                      print("index \(index) out of bounds")
                      return ""
                  }
                  return self.substringFromIndex(self.startIndex.advancedBy(index))
              }
          }
          
          display.text = display.text!.substringFromIndex(1)
          

          【讨论】:

            【解决方案10】:

            "en_US,fr_CA,es_US".chopSuffix(5).chopPrefix(5) // ",fr_CA,"

            extension String {
                func chopPrefix(count: Int = 1) -> String {
                    return self.substringFromIndex(self.startIndex.advancedBy(count))
                }
            
                func chopSuffix(count: Int = 1) -> String {
                    return self.substringToIndex(self.endIndex.advancedBy(-count))
                }
            }
            

            【讨论】:

              【解决方案11】:

              从字符串中删除第一个字符

              let choppedString = String(txtField.text!.characters.dropFirst())
              

              【讨论】:

              • @JasonFoglia 我不知道你是否放弃投票。如果您投反对票,请您详细解释一下。
              • 我重新检查了这个,发现它是正确的。我编辑了答案,这样我就可以正确投票了。
              【解决方案12】:

              这是chopPrefix 扩展的Swift4 崩溃保存版本,将chopSuffix 留给社区...

              extension String {
                  func chopPrefix(_ count: Int = 1) -> String {
                      return count>self.count ? self : String(self[index(self.startIndex, offsetBy: count)...])
                  }
               }
              

              【讨论】:

                【解决方案13】:

                Swift3

                extension String {
                    func chopPrefix(_ count: UInt = 1) -> String {
                        return substring(from: characters.index(startIndex, offsetBy: Int(count)))
                    }
                
                    func chopSuffix(_ count: UInt = 1) -> String {
                        return substring(to: characters.index(endIndex, offsetBy: -Int(count)))
                    }
                }
                
                class StringChopTests: XCTestCase {
                    func testPrefix() {
                        XCTAssertEqual("original".chopPrefix(0), "original")
                        XCTAssertEqual("Xfile".chopPrefix(), "file")
                        XCTAssertEqual("filename.jpg".chopPrefix(4), "name.jpg")
                    }
                
                    func testSuffix() {
                        XCTAssertEqual("original".chopSuffix(0), "original")
                        XCTAssertEqual("fileX".chopSuffix(), "file")
                        XCTAssertEqual("filename.jpg".chopSuffix(4), "filename")
                    }
                }
                

                【讨论】:

                • 我认为您应该添加一个输入为负值的测试
                猜你喜欢
                • 1970-01-01
                • 2011-03-14
                • 2020-12-05
                • 2017-05-19
                • 2022-01-06
                • 2011-05-22
                • 2010-10-05
                • 2016-12-03
                相关资源
                最近更新 更多