iOS开发中如何提取并修改字符串前两个单词(适配俄语变格)
问题描述
我正在开发一款适配俄语变格语法的iOS应用,场景如下:
- 应用包含FirstVC和SecondVC两个视图控制器,各自带有UITableView;
- 用户点击FirstVC的单元格时,单元格文本会传递到SecondVC的表格表头;
- SecondVC的表头文本需符合俄语变格规则,目前已实现提取并修改字符串首个单词的逻辑,现在需要扩展功能,支持修改字符串前两个单词的情况,请求基于现有逻辑的实现方法。
现有SecondVC代码
import UIKit class SecondVC: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var names: [Cell2Model] = [] var textForChanging: String = "" var switchedFirstWord: String { switch textForChanging.firstWord { case "Соревнование": return "соревнования" //translates as "competition" default: return "none" } } var restoOfTheName: String { var string = textForChanging string.enumerateSubstrings(in: string.startIndex..., options: .byWords) { (_, _, enclosingRange, stop) in string.removeSubrange(enclosingRange) stop = true } return string } var adjustedEventNameText: String { return switchedFirstWord + " " + restoOfTheName } override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self // names will be taken from UITextFields on the fourth view controller names = [ (Cell2Model(name: "Participant 1")), (Cell2Model(name: "Participant 2")), (Cell2Model(name: "Participant 3")) ] let tableHeader = UIView() tableView.tableHeaderView = tableHeader let labelOne = UILabel(frame: tableHeader.bounds) labelOne.text = "СПИСОК" labelOne.font = UIFont(name:"Arial-BoldMT", size: 17.0) labelOne.numberOfLines = 0 labelOne.lineBreakMode = .byWordWrapping labelOne.textAlignment = .center labelOne.translatesAutoresizingMaskIntoConstraints = false let labelTwo = UILabel(frame: tableHeader.bounds) labelTwo.translatesAutoresizingMaskIntoConstraints = false labelTwo.text = "участников \(adjustedEventNameText)" labelTwo.font = UIFont(name:"Arial-BoldMT", size: 17.0) labelTwo.numberOfLines = 0 labelTwo.lineBreakMode = .byWordWrapping labelTwo.textAlignment = .center labelTwo.translatesAutoresizingMaskIntoConstraints = false tableHeader.addSubview(labelOne) tableHeader.addSubview(labelTwo) let g = tableHeader.layoutMarginsGuide NSLayoutConstraint.activate([ labelOne.topAnchor.constraint(equalTo: g.topAnchor, constant: 11.0), labelOne.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0), labelOne.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0), labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 0.0), labelTwo.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0), labelTwo.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0), ]) let c = labelTwo.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -11.0) c.priority = .required - 1 c.isActive = true } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let tableHeader = tableView.tableHeaderView { let fitSize: CGSize = CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude) let sz: CGSize = tableHeader.systemLayoutSizeFitting(fitSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow) let height: CGFloat = sz.height var headerFrame = tableHeader.frame if height != headerFrame.size.height { headerFrame.size.height = height tableHeader.frame = headerFrame tableView.tableHeaderView = tableHeader } } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return names.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "secondCell", for: indexPath) as! SecondCell let Name = names[indexPath.row] cell.label.text = Name.name return cell } } extension StringProtocol { var byWords: [SubSequence] { components(separated: .byWords) } func components(separated options: String.EnumerationOptions)-> [SubSequence] { var components: [SubSequence] = [] enumerateSubstrings(in: startIndex..., options: options) { _, range, _, _ in components.append(self[range]) } return components } var firstWord: SubSequence? { var word: SubSequence? enumerateSubstrings(in: startIndex..., options: .byWords) { _, range, _, stop in word = self[range] stop = true } return word } }
现有FirstVC代码
import UIKit class FirstVC: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! var names: [Cell1Model] = [] override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self // names will be taken from UITextFields on the third view controller names = [ Cell1Model(name: "Соревнование по легкой атлетике"), // translates as "athletics competition (or "competition on athletics" to put it into the Russian words order) Cell1Model(name: "Благотворительное соревнование по легкой атлетике") // translates as "athletics charity competition" (or "charity competition on athletics" in the Russian words order) ] } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) if let vc = storyboard?.instantiateViewController(withIdentifier: "SecondVC") as? SecondVC { vc.textForChanging = names[indexPath.row].name! self.navigationController?.pushViewController(vc, animated: true) } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return names.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Cell let Name = names[indexPath.row] cell.label.text = Name.name return cell }
解决方案
基于现有代码逻辑,通过以下步骤扩展支持前两个单词的变格处理:
1. 扩展StringProtocol,新增多单词操作方法
在现有StringProtocol扩展中添加获取前N个单词、移除前N个单词的方法,兼容原有逻辑:
extension StringProtocol { var byWords: [SubSequence] { components(separated: .byWords) } func components(separated options: String.EnumerationOptions)-> [SubSequence] { var components: [SubSequence] = [] enumerateSubstrings(in: startIndex..., options: options) { _, range, _, _ in components.append(self[range]) } return components } var firstWord: SubSequence? { var word: SubSequence? enumerateSubstrings(in: startIndex..., options: .byWords) { _, range, _, stop in word = self[range] stop = true } return word } // 获取前n个单词 func firstWords(_ count: Int) -> [SubSequence] { var words: [SubSequence] = [] enumerateSubstrings(in: startIndex..., options: .byWords) { _, range, _, stop in words.append(self[range]) if words.count == count { stop = true } } return words } // 移除前n个单词后的剩余字符串 func removingFirstWords(_ count: Int) -> String { var string = String(self) var removedCount = 0 string.enumerateSubstrings(in: string.startIndex..., options: .byWords) { _, enclosingRange, _, stop in string.removeSubrange(enclosingRange) removedCount += 1 if removedCount == count { stop = true } } return string.trimmingCharacters(in: .whitespaces) } }
2. 修改SecondVC,添加双单词变格逻辑
拆分变格逻辑到独立方法,根据输入文本判断需要变格1个还是2个单词:
class SecondVC: UIViewController, UITableViewDelegate, UITableViewDataSource { // ... 原有代码保持不变 ... // 处理单个单词的变格 private func inflectSingleWord(_ word: SubSequence) -> String { switch word { case "Соревнование": return "соревнования" default: return String(word) } } // 处理两个单词的变格(示例:"Благотворительное соревнование") private func inflectTwoWords(_ words: [SubSequence]) -> String { guard words.count >= 2 else { return inflectSingleWord(words.first!) } let firstWord = words[0] let secondWord = words[1] // 根据俄语变格规则添加对应处理 switch (firstWord, secondWord) { case ("Благотворительное", "Соревнование"): return "благотворительного соревнования" // 可添加更多双单词变格规则 default: return "\(inflectSingleWord(firstWord)) \(inflectSingleWord(secondWord))" } } var adjustedEventNameText: String { let allWords = textForChanging.byWords switch allWords.count { case 1: return inflectSingleWord(allWords[0]) case >=2: let firstTwo = allWords.prefix(2) // 判断是否需要双单词变格,可根据实际规则调整条件 let needsDoubleInflection = firstTwo.contains("Благотворительное") && firstTwo.contains("Соревнование") if needsDoubleInflection { let inflectedTwo = inflectTwoWords(Array(firstTwo)) let rest = textForChanging.removingFirstWords(2) return rest.isEmpty ? inflectedTwo : "\(inflectedTwo) \(rest)" } else { // 仅变格第一个单词,兼容原有逻辑 let inflectedFirst = inflectSingleWord(allWords[0]) let rest = textForChanging.removingFirstWords(1) return rest.isEmpty ? inflectedFirst : "\(inflectedFirst) \(rest)" } default: return textForChanging } } // ... 原有代码保持不变 ... }
逻辑说明
- 通过新增的
firstWords(_:)和removingFirstWords(_:)方法,简化多单词的提取与剩余文本处理; - 拆分变格逻辑到独立方法,便于后续扩展更多俄语变格规则;
- 在
adjustedEventNameText中自动判断文本类型,优先处理双单词变格场景,同时保留原有单单词变格的兼容性。
内容的提问来源于stack exchange,提问作者CalebGates
相关产品推荐
相关产品推荐

