在字符串中查找引号内的字符

3

我正在尝试提取一个字符串中用引号括起来的部分,例如在"Rouge One" is an awesome movie中,我想要提取Rouge One。

到目前为止,我只能做到这一步,但无法确定下一步该怎么做:我创建了文本的一个副本,以便我可以删除第一个引号,从而获取第二个引号的索引。

if text.contains("\"") {
    guard let firstQuoteMarkIndex = text.range(of: "\"") else {return}
    var textCopy = text
    let textWithoutFirstQuoteMark = textCopy.replacingCharacters(in: firstQuoteMarkIndex, with: "")
    let secondQuoteMarkIndex = textCopy.range(of: "\"")
    let stringBetweenQuotes = text.substring(with: Range(start: firstQuoteMarkIndex, end: secondQuoteMarkIndex))
}
4个回答

5
这个任务不需要创建副本或替换子字符串。以下是一种可能的方法:
  • 使用text.range(of: "\"")查找第一个引号。
  • 使用text.range(of: "\"", range:...)查找第二个引号,即在步骤1中找到的范围之后的第一个引号。
  • 提取两个范围之间的子字符串。

示例:

let text = "  \"Rouge One\" is an awesome movie"

if let r1 = text.range(of: "\""),
    let r2 = text.range(of: "\"", range: r1.upperBound..<text.endIndex) {

    let stringBetweenQuotes = text.substring(with: r1.upperBound..<r2.lowerBound)
    print(stringBetweenQuotes) // "Rouge One"
}

另一个选项是使用“正向回顾”和“正向预查”的模式进行正则表达式搜索:
if let range = text.range(of: "(?<=\\\").*?(?=\\\")", options: .regularExpression) {
    let stringBetweenQuotes = text.substring(with: range)
    print(stringBetweenQuotes)
}

前瞻/后顾的使用很好。但是,对于像"a"b"这样的字符串呢?两个都应该匹配吗? - jtbandes
@jtbandes:那段代码将提取第一个和第二个引号之间的字符串(这是我理解问题的方式)。如果想要所有带引号的字符串,则您的方法更好。 - Martin R
在执行 r2.lowerBound 时,您忘记了对 r2 进行解包。正确的写法应该是 r2!.lowerBound。虽然我不明白为什么不能使用 r2?.lowerBound - mfaani
@Honey:不是。r1r2都被赋予了可选绑定。如果第二个范围是nil,那么if块将不会被执行。 - Martin R
哦,我错过了逗号:D - mfaani
如何在包含多个引号单词的句子中使用它? - nr5

2
var rouge = "\"Rouge One\" is an awesome movie"

var separated = rouge.components(separatedBy: "\"") // ["", "Rouge One", " is an awesome movie"]

separated.dropFirst().first

所有的回答都很好,但这个最轻量级,满足了我的需求。谢谢! - GarySabo
@GarySabo 一般来说,请注意此答案返回一个可选项,并且不会检查您是否有两个分隔符。这是一个简单但轻量级的答案。最好使用类似于简单的 if separated.count >2 {rest of the code} 的东西进行检查。或者只需像其他答案一样做就可以了:D - mfaani

1

另一种选择是使用正则表达式来查找引号对:

let pattern = try! NSRegularExpression(pattern: "\\\"([^\"]+)\\\"")

// Small helper methods making it easier to work with enumerateMatches(in:...)
extension String {
    subscript(utf16Range range: Range<Int>) -> String? {
        get {
            let start = utf16.index(utf16.startIndex, offsetBy: range.lowerBound)
            let end = utf16.index(utf16.startIndex, offsetBy: range.upperBound)
            return String(utf16[start..<end])
        }
    }

    var fullUTF16Range: NSRange {
        return NSRange(location: 0, length: utf16.count)
    }
}

// Loop through *all* quoted substrings in the original string.
let str = "\"Rogue One\" is an awesome movie"
pattern.enumerateMatches(in: str, range: str.fullUTF16Range) { (result, flags, stop) in
    // rangeAt(1) is the range representing the characters in the 1st
    // capture group of the regular expression: ([^"]+)
    if let result = result, let range = result.rangeAt(1).toRange() {
        print("This was in quotes: \(str[utf16Range: range] ?? "<bad range>")")
    }
}

1
我会使用 .components(separatedBy:) 进行分割。
let stringArray = text.components(separatedBy: "\"")

检查stringArray计数是否大于2(至少有2个引号)。

检查stringArray计数是否为奇数,即count%2 == 1。

  • 如果是奇数,则所有偶数索引都在2个引号之间,并且它们是您想要的。
  • 如果是偶数,则所有偶数索引-1都在2个引号之间(最后一个没有结束引号)。

这还将允许您捕获多组带引号的字符串,例如:"Rogue One"是一部"Star Wars"电影。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接