String 如何实现在键入要搜索的字符串时在另一个字符串中查找一个字符串的所有位置?
我希望在我的应用程序中具有以下功能: 当我在表格视图中同时在NSTextView窗口中键入每个酶的DNA序列(字符串)(每个代表小字符串)时,用户立即看到每个酶对应的已找到位点(字符串)的数量(0或任意数字) 我有一个函数,可用于查找字符串中字符串的所有可能位置(返回NSRanges数组)。在我的例子中,这将在DNA序列(字符串)中找到每个酶对应的所有可能位点(字符串) 因此,还有一个问题是如何实现此功能:在键入字符串时,查找此字符串中的所有位点(以NSRanges数组的形式),并在表中相应地为每个酶输入找到的位点的数字 换句话说,返回酶位点位置的NSRanges数组的函数应自动启动 更新 我是cocoa的新手,在得到了R Menke的建议后(我在下面的代码中加入了他的代码行),我可能有更愚蠢的问题。我有一个控制器类作为NSWindowController的子类。我无法将R Menke中的代码放入此类(请参见下面的错误)。而且,在我的控制器类中,我有我的NSTextView,用户将在其中键入文本@IBOutlet,我应该使用它吗?我应该制作另一个控制器文件吗?下面是代码和错误String 如何实现在键入要搜索的字符串时在另一个字符串中查找一个字符串的所有位置?,string,macos,swift,cocoa,String,Macos,Swift,Cocoa,我希望在我的应用程序中具有以下功能: 当我在表格视图中同时在NSTextView窗口中键入每个酶的DNA序列(字符串)(每个代表小字符串)时,用户立即看到每个酶对应的已找到位点(字符串)的数量(0或任意数字) 我有一个函数,可用于查找字符串中字符串的所有可能位置(返回NSRanges数组)。在我的例子中,这将在DNA序列(字符串)中找到每个酶对应的所有可能位点(字符串) 因此,还有一个问题是如何实现此功能:在键入字符串时,查找此字符串中的所有位点(以NSRanges数组的形式),并在表中相应地为
import Cocoa
//Error. Multiple inheritance from classes 'NSWindowController' and 'NSViewController'
class AllUnderControl: NSWindowController, NSViewController,NSTextViewDelegate
{
override var windowNibName: String?
{
return "AllUnderControl"
}
override func windowDidLoad() {
super.windowDidLoad()
}
//Error. Instance member 'inputDnaFromUser' cannot be used on type 'AllUnderControl'
var textView = inputDnaFromUser(frame: CGRectZero)
//Error. Method does not override any method from its superclass
override func viewDidLoad() {
textView.delegate = self
}
func textDidChange(notification: NSNotification) {
// trigger your function
}
@IBOutlet var inputDnaFromUser: NSTextView!
更新2
在阅读了两个控制器:NSWindowController和NSViewController的说明后,我做了以下更改。触发功能正确吗
import Cocoa
class AllUnderControl: NSWindowController, NSTextViewDelegate
{
override var windowNibName: String?
{
return "AllUnderControl"
}
override func windowDidLoad() {
super.windowDidLoad()
inputDnaFromUser.delegate = self
}
func textDidChange(notification: NSNotification) {
// trigger your function
}
@IBOutlet var inputDnaFromUser: NSTextView! = NSTextView(frame: CGRectZero)
如果您询问:“当有人在NSTextView中键入时,如何触发函数?” 实现
NSTextView委托
将NSTextView
的委托设置为self
,并触发textDidChange
内的函数。NSTextViewDelegate
具有一组将由用户交互触发的功能。因此,当相应的操作发生时,它们内部的代码将被执行
我建议为此使用NSViewController,而不是NSWindowController。NSWindowController用于管理NSViewController。NSViewControllers是按钮和文本字段之类的更好的地方
如果您询问:“如何在另一个字符串中找到一个字符串的所有匹配项?” 这将返回一个范围数组。该数组的计数显然是出现的次数 另一个选项是使用@Russel在回答中所述的
EnumerateSubstringsRange
。我总是喜欢写我自己的循环
let string = "The sky is blue today, super blue"
let searchString = "blue"
var ranges: [NSRange] = []
var copyString = NSMutableString(string: string)
while copyString.containsString(searchString) {
ranges.append(copyString.rangeOfString(searchString))
guard let lastRange = ranges.last else {
break
}
var replaceString = ""
for _ in 0..<lastRange.length { replaceString += "$" } // unnalowed character
copyString.replaceCharactersInRange(lastRange, withString: replaceString)
}
您将希望在后台队列中执行此操作。但这很快就会变得棘手。一种酶在某一时刻可能是一种,而在下一时刻可能会发生变化。因此,您需要对键入的每个字符执行所有工作 一种可能的解决方案是在键入字符时取消每次正在进行的搜索。如果在键入下一个字符之前完成,则会得到结果
这是我写的一篇文章,它实现了这个逻辑。除了使用<代码> UICOROR < /代码>一切都是纯粹的基础。将其更改为Cocoa非常容易。您需要实现
UISearchResultsUpdateing
协议来实现这一点。它使用了一个UISearchController
(在iOS 8中引入),它必须以编程方式添加,而不是通过故事板添加,但别担心,它非常简单
使用updateSearchResultsForSearchController
委托方法处理搜索,该方法在搜索栏文本更改时调用。我试着让它非常自我记录,但如果你有任何问题,请告诉我。取决于你想搜索多少酶,这可能会很快变得效率低下,因为你必须搜索每个酶的子串
干杯,
拉塞尔
你能告诉我你目前的尝试是什么样子的吗?还有什么是无效的?问题是我不知道它应该是什么样子。当我有dna序列并单击按钮执行操作时,我可以找到dna序列中任何位点(酶)的所有位置,但我不知道如何在键入dna序列期间执行此搜索以获取NSRange数组。如何存储所有可能的子字符串(酶)?这是您创建的预定义数组吗?inputDnaFromUser是一个IBOutlet,所以我假设它是在XIB中连接的。分配视图没有意义。inputDnaFromUser的代理也可以在XIB中连接。OP似乎在为Cocoa应用程序寻找答案,因为OP指的是
NSTextView
我正在为OSX编写,无论如何,谢谢!啊,说得好。我自己也是一个iOS用户,所以我甚至没有想到可可应用的一面。如果没有其他内容,您可以为酶字典提取一些有用的逻辑,并在酶数组中进行搜索。我希望这里有一些对你有用的东西!谢谢这是非常有用的!我是可可新手,抱歉问了这么愚蠢的问题。我迷路了。我有一个控制器类作为NSWindowController的子类。我不能把你的代码放在这个类上面。而且,在我的控制器类中,我有我的NSTextView,用户将在其中键入文本@IBOutlet,我应该使用它吗?我应该创建另一个控制器文件吗?在循环中执行RangeOfsString:options:range:不是更快吗?如果RangeOfsString:options:range:崩溃,则说明您做错了什么。当循环返回位置NSNotFound时,就结束循环。@Willeke我做错了什么。更详细地看了看。我没有正确设置搜索范围。并没有减少搜索长度,只是每次通过后的位置。谢谢!
let string = "The sky is blue today, super blue"
let searchString = "blue"
var ranges: [NSRange] = []
var copyString = NSMutableString(string: string)
while copyString.containsString(searchString) {
ranges.append(copyString.rangeOfString(searchString))
guard let lastRange = ranges.last else {
break
}
var replaceString = ""
for _ in 0..<lastRange.length { replaceString += "$" } // unnalowed character
copyString.replaceCharactersInRange(lastRange, withString: replaceString)
}
let string : NSString = "The sky is blue today, super blue"
let searchString = "blue"
var ranges: [NSRange] = []
var searchRange : NSRange = NSRange(location: 0, length: string.length)
var lastFoundRange : NSRange = string.rangeOfString(searchString, options: NSStringCompareOptions.LiteralSearch, range: searchRange)
while lastFoundRange.location != NSNotFound {
ranges.append(lastFoundRange)
let searchRangeLocation = lastFoundRange.location + lastFoundRange.length
let searchRangeLength = string.length - searchRangeLocation
searchRange = NSRange(location: searchRangeLocation, length: searchRangeLength)
lastFoundRange = string.rangeOfString(searchString, options: NSStringCompareOptions.LiteralSearch, range: searchRange)
}
class YourTableViewController: UITableViewController, UISearchBarDelegate, UISearchResultsUpdating {
// Array of searchable enzymes
var enzymes: [String] = ["...", "...", "..."]
// Dictionary of enzymes mapping to an array of NSRange
var enzymeSites: [String : [NSRange]] = [String : [NSRange]]()
// Search controller
var enzymeSearchController = UISearchController()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.enzymeSearchController = UISearchController(searchResultsController: nil)
self.enzymeSearchController.dimsBackgroundDuringPresentation = true
// This is used for dynamic search results updating while the user types
// Requires UISearchResultsUpdating delegate
self.enzymeSearchController.searchResultsUpdater = self
// Configure the search controller's search bar
self.enzymeSearchController.searchBar.placeholder = "Enter DNA sequence"
self.enzymeSearchController.searchBar.sizeToFit()
self.enzymeSearchController.searchBar.delegate = self
self.definesPresentationContext = true
// Set the search controller to the header of the table
self.tableView.tableHeaderView = self.enzymeSearchController.searchBar
}
// MARK: - Search Logic
func searchEnzymeSites(searchString: String) {
// Search through all of the enzymes
for enzyme in enzymes {
// See logic from here: https://stackoverflow.com/questions/27040924/nsrange-from-swift-range
let nsEnzyme = searchString as NSString
let enzymeRange = NSMakeRange(0, nsEnzyme.length)
nsEnzyme.enumerateSubstringsInRange(enzymeRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
if (substring == enzyme) {
// Update the enzymeSites dictionary by appending to the range array
enzymeSites[enzyme]?.append(substringRange)
}
})
}
}
// MARK: - Search Bar Delegate Methods
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
// Force search if user pushes button
let searchString: String = searchBar.text.lowercaseString
if (searchString != "") {
searchEnzymeSites(searchString)
}
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
// Clear any search criteria
searchBar.text = ""
// Force reload of table data from normal data source
}
// MARK: - UISearchResultsUpdating Methods
// This function is used along with UISearchResultsUpdating for dynamic search results processing
// Called anytime the search bar text is changed
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchString: String = searchController.searchBar.text.lowercaseString
if (searchString != "") {
searchEnzymeSites(searchString)
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.enzymeSearchController.active) {
return self.enzymeSites.count
} else {
// return whatever your normal data source is
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("userCell") as! UserCell
if (self.enzymeSearchController.active && self.enzymeSites.count > indexPath.row) {
// bind data to the enzymeSites cell
} else {
// bind data from your normal data source
}
return cell
}
// MARK: - UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if (self.enzymeSearchController.active && self.searchUsers.count > 0) {
// Segue or whatever you want
} else {
// normal data source selection
}
}
}