如何从指定字符串提取子串并将Bob、Mike分别赋值给Player1、Player2?
解决Swift中提取字符串内玩家名字的问题
嘿,我完全明白你想从"Match Between Bob and Mike"里把Bob和Mike分别存入Player1、Player2变量的需求了。你之前的代码方向有点走偏——不仅找的"Between: "和原字符串里的"Between "(没有冒号)不匹配,而且你取的是这个索引之前的内容,这当然拿不到后面的玩家名字啦。咱们换几种更直接靠谱的方式来解决:
方法一:正则表达式(最灵活的方案)
如果你的字符串格式可能有轻微变化(比如大小写、多余空格),正则表达式是最稳妥的选择,能精准匹配捕获目标内容:
let match = "Match Between Bob and Mike" // 正则模式:匹配固定前缀,然后捕获两个玩家名字 let pattern = "Match Between (\\w+) and (\\w+)" if let regex = try? NSRegularExpression(pattern: pattern), let matchResult = regex.firstMatch(in: match, range: NSRange(match.startIndex..., in: match)) { // 提取第一个捕获组(对应Bob) let player1Range = Range(matchResult.range(at: 1), in: match)! let player1 = String(match[player1Range]) // 提取第二个捕获组(对应Mike) let player2Range = Range(matchResult.range(at: 2), in: match)! let player2 = String(match[player2Range]) print("Player1: \(player1), Player2: \(player2)") // 输出:Player1: Bob, Player2: Mike }
这里的(\w+)会匹配由字母、数字组成的名字,如果你需要支持带空格的名字(比如"Bob Smith"),可以把模式改成"Match Between ([^and]+) and (.+)"来适配。
方法二:字符串分割(最直观的方案)
如果你的字符串格式完全固定(必须是"Match Between X and Y"的结构),直接分割字符串会更简单易懂:
let match = "Match Between Bob and Mike" // 第一步:按"Between "分割,取后面的部分 guard let afterBetween = match.components(separatedBy: "Between ").last else { print("格式不匹配") return } // 第二步:按" and "分割成两个名字 let players = afterBetween.components(separatedBy: " and ") if players.count == 2 { let player1 = players[0] let player2 = players[1] print("Player1: \(player1), Player2: \(player2)") } else { print("格式不匹配") }
这种方法代码量少,逻辑清晰,适合格式完全可控的场景。
方法三:基于你写的扩展调整逻辑(手动索引操作)
如果你坚持想用自己写的StringProtocol扩展,那需要调整逻辑,拿到"Between "结束后的内容,再定位" and "的位置来截取名字:
let match = "Match Between Bob and Mike" // 先找到"Between "的结束索引 if let betweenEndIndex = match.endIndex(of: "Between ") { let substringAfterBetween = match[betweenEndIndex...] // 找到" and "在后续子串中的起始索引 if let andStartIndex = substringAfterBetween.index(of: " and ") { // 计算"and"在原字符串中的绝对位置 let absoluteAndStart = match.index(betweenEndIndex, offsetBy: match.distance(from: substringAfterBetween.startIndex, to: andStartIndex)) // 截取Player1:从Between结束到and开始的位置 let player1 = String(match[betweenEndIndex..<absoluteAndStart]) // 截取Player2:从and结束的位置到字符串末尾 let absoluteAndEnd = match.index(absoluteAndStart, offsetBy: " and ".count) let player2 = String(match[absoluteAndEnd...]) print("Player1: \(player1), Player2: \(player2)") } } // 你的StringProtocol扩展保留不变 extension StringProtocol { func index<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? { range(of: string, options: options)?.lowerBound } func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? { range(of: string, options: options)?.upperBound } func indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index] { ranges(of: string, options: options).map(\.lowerBound) } func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] { var result: [Range<Index>] = [] var startIndex = self.startIndex while startIndex < endIndex, let range = self[startIndex...] .range(of: string, options: options) { result.append(range) startIndex = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex } return result } }
不过这种方法相对繁琐,除非你有特殊需求要手动操作字符串索引,否则更推荐前两种方案。
内容的提问来源于stack exchange,提问作者Anish
相关产品推荐
相关产品推荐

