问题:如何将一个字符串儿中多余的空格给删掉?
需要注意的是,我们只要求删除多余的空格,但是并不要求把所有的空格都删掉。保留必要的空格是保持原有语义所必须的,如果把句子中所有的空格都删除,那么就会造成多个单词连成一坨而无法辨别。
问题的解决思路还是比较简单的,可以用空格或者换行把字符串儿分割成独立的单词或标点符号,然后将所有的空格都过滤掉,最后再用一个空格把所有的独立的单词或者标点符号重新连接成一个字符串儿就可以了:
// 解决方案一
func challenge(input: String) -> String {let components = input.components(separatedBy: .whitespacesAndNewlines)return components.filter { !$0.isEmpty }.joined(separator: " ")
}let str = "Grasp all, lose all." // 中间有好多好多空格
challenge(input: str)
filter(_: )
函数和joined(separator: )
方法在前面的笔记《从一个字符串儿中移除重复的字符》中已经讲过了,这里就不再重复了。稍微解释一下字符串儿的分割方法components(separatedBy: )
,它是NSString里面的方法:
我们注意到,像这样的方法共有有两个,而且方法名是一毛一样的,只是参数不同,这个在Swift中叫做方法重载。在Objective-C中是没有方法重载的概念的,因为Objective-C中方法调用的本质是发送消息,如果方法名相同,那么系统在发送消息时,就不知道该把消息发送给谁。接下来再看一下参数CharacterSet类型,跟String一样,它也是一个结构体,里面很多属性,其中有一个whitespacesAndNewlines
属性,它表示空格和换行,在上面的代码中,我们就用它来分割独立的单词或者标点符号。另外,有一个细节需要注意,就是最后joined(separator: )
方法,我们给它传递包含一个空格的双引号,如果漏掉了空格,仅仅只是传递了双引号,最后返回的字符串儿就会变成一大坨。为了便于理解,我还是把filter(_: )
函数完整的写出来:
func challenge(input: String) -> String {let components = input.components(separatedBy: .whitespacesAndNewlines)return components.filter({ (str) -> Bool inreturn !str.isEmpty // 返回非空字符串儿}).joined(separator: " ")
}
其实,上面的代码除了写成独立的方法之外,如果你喜欢,还可以写成String的扩展:
extension String {func challenge() -> String {return self.components(separatedBy: .whitespacesAndNewlines).filter { !$0.isEmpty }.joined(separator: " ")}
}let str = "Grasp all, lose all." // 中间有好多好多空格
str.challenge()
除了对长字符串儿进行“分割-过滤-再拼接”之外,还可以使用for ... in
循环。其思路是,先定义一个isSpace
变量,默认其为false
,用于标识字符是否为空格,再定义一个空的可变字符串儿returnValue
用来拼接遍历之后的字符、标点符号和空格。在遍历过程中对字符进行判断,利用continue
将重复出现的空格给过滤掉:
// 解决方案二
func challenge(input: String) -> String {// 用于标识空格,默认为falsevar isSpace = false// 创建空的字符串儿,用于后面的字符串儿拼接var returnValue = "" // 注意,双引号中间没有任何空格// 遍历输入的字符串儿for letter in input.characters {// 如果输入了空格if letter == " " { // 注意,双引号中间有一个空格// 如果isSpace为true,则跳过本次循环体中尚未执行的语句,接着进行终止条件的判断if isSpace { continue } // 如果连续多个空格,则一直continue,从而保证多个空格只添加一次// 修改isSpace的结果为trueisSpace = true} else {// 修改isSpace为falseisSpace = false}// 将字符letter拼接到returnValue后面returnValue.append(letter)}// 返回拼接好了字符串儿return returnValue
}let str = "Grasp all, lose all." // 中间有好多好多空格
challenge(input: str)
上面代码的核心知识是continue
。当出现多个连续的空格时,则利用continue
的特性跳过本次循环,不执行returnValue.append(letter)
语句,从而保证多个连续的空格只会被添加一次。
我们都知道,在Swift中,String和Foundation的NSString具有良好的兼容性,所以就在这里介绍三个NSString的字符串儿替换方法:
今天我们要用的主要是replacingOccurrences(of: , with: , options: , range: )
方法。该方法有四个参数,其中target表示要被替换的子字符串,replacement表示用来替换原来子字符串儿的目标字符串,options表示选项标记,range表示替换的范围。在参数options中有一个正则表达式选项,可以大大减少我们的工作量:
// 解决方案三
func challenge(input: String) -> String {return input.replacingOccurrences(of: " +", with: " ", options: .regularExpression, range: nil)
}let str = "Grasp all, lose all." // 中间有好多好多空格
challenge(input: str)
在上面的代码中,参数of
后面的“空格+”用来表示匹配一个或者多个空格。这样一来,所有连续的空格都将会被替换为一个空格。我们看到,利用正则表达式,一句代码就可以搞定前面需要一大坨代码才能搞定的功能。