【问题标题】:Why isn't the last full stop removed from a stringArray using .map为什么不使用 .map 从 stringArray 中删除最后一个句号
【发布时间】:2019-01-31 19:07:07
【问题描述】:

我正在使用 Swift。 我正在尝试将句子转换为字符串数组。我使用 map 将句号和逗号与单词分开,如下所示:

extension String  {


func convertSentenceToArray()-> [String] {
var sentence = String(self)

sentence.index(of: ".").map { 
   sentence.remove( at: $0)
   sentence.insert(".", at: $0)
   sentence.insert(" ", at: $0)
   }
sentence.index(of: ",").map { 
  sentence.remove( at: $0)
  sentence.insert(",", at: $0)
  sentence.insert(" ", at: $0) 
   }
 return sentence.components(separatedBy: " ")
 }
}

let  thisSentenceString = "I am trying to create an array from a sentence. But I don't understand, Why isn't the last fullstop removed, from the last word."

let thisSentenceArray = thisSentenceString.convertSentenceToArray()

print(thisSentenceArray)

结果:

["I", "am", "trying", "to", "create", "an", "array", "from", "a", "sentence", ".", "But ", "I", "don\'t", "understand", ",", "Why", "isn\'t", "the", "last", "fullstop", "removed,", " from", "the", "last", "word."]

所有句号和逗号都按照我的预期处理,除了最后一个。

我不明白为什么最后一个句号仍然存在。虽然我可以找到解决此问题的方法,但我想了解我所采取的方法有什么问题。

【问题讨论】:

  • 您的代码(最多)处理 one 句号和 one 逗号。
  • “removed”后没有去掉逗号
  • 提示:index(of:) 正在返回一个可选项,因此您使用的是 Optional.map不是 Array.map

标签: arrays swift


【解决方案1】:

首先解释一下你的代码是做什么的:

sentence
   .index(of: ".") // find the first index of the dot character
   .map {  // Optional.map, if the index exists, do the following
      sentence.remove( at: $0) // remove dot
      sentence.insert(".", at: $0) // insert dot again
      sentence.insert(" ", at: $0) // insert space
   }

或重写:

if let firstDotIndex = sentence.index(of: ".") {
    sentence.insert(" ", at: firstDotIndex)
}

这意味着只有第一个点字符被找到并替换。

要正确执行此算法,您需要:

// helper method checking punctuation to avoid code duplication
let isPunctuation: (Character) -> Bool = {
    return [".", ","].contains($0)
}

// initial range, we want to check the entire string
var range = sentence.startIndex...

// iterate while some punctuation exists
while let punctuationIndex = sentence[range].index(where: isPunctuation) {
    // insert the separator
    sentence.insert(" ", at: punctuationIndex)
    // search next punctuation only from the last replacement
    range = sentence.index(after: punctuationIndex)...
}

不过其实已经有String替换的方法了:

sentence = sentence.replacingOccurrences(of: ".", with: " .")

或者更简单,用正则表达式一次性覆盖所有标点符号:

return self
    .replacingOccurrences(of: "[,.]", with: " $0", options: .regularExpression)
    .components(separatedBy: " ")

【讨论】:

  • 非常好的解决方案!!谢谢
【解决方案2】:

这与您所要求的略有不同,但根据您这样做的原因,您可以考虑使用 NaturalLanguage 框架。例如

import NaturalLanguage

let text = "I am trying to create an array from a sentence. But I don't understand, Why isn't the last fullstop removed, from the last word."

var words: [String] = []

let tagger = NLTagger(tagSchemes: [.lexicalClass])
tagger.string = text
let options: NLTagger.Options = [.omitWhitespace, .joinContractions]
tagger.enumerateTags(in: text.startIndex..<text.endIndex, unit: .word, scheme: .lexicalClass, options: options) { tag, range in
    if let tag = tag {
        words.append(String(text[range]))
    }
    return true
}
print(words)

["I", "am", "trying", "to", "create", "an", "array", "from", "a", "sentence", ".", "But ", "I", "don\'t", "understand", ",", "Why", "isn\'t", "the", "last", "fullstop", "removed", ", ", "from", "the", "last", "word", "."]

有趣的是tag属性会告诉你词性,什么是句子终止符等等,例如:

tagger.enumerateTags(in: text.startIndex..<text.endIndex, unit: .word, scheme: .lexicalClass, options: options) { tag, range in
    if let tag = tag {
        print(text[range], tag.rawValue)
    }
    return true
}

制作:

我代词
是动词
尝试动词
到粒子
创建动词
一个限定符
数组 名词
来自介词
限定词
句子名词
.句子终结者
但连词
我代词
不要动词
理解动词
, 标点符号
为什么是代词
不是动词
决定者
最后一个形容词
句号 名词
删除动词
, 标点符号
来自介词
决定者
最后一个形容词
名词名词
.句子终结者

或者,也许您并不真正关心标点符号,而只是想将其分解为句子并将句子分解为单词:

var sentences: [[String]] = []

let sentenceTokenizer = NLTokenizer(unit: .sentence)
sentenceTokenizer.string = text

sentenceTokenizer.enumerateTokens(in: text.startIndex ..< text.endIndex) { range, _ in
    let sentence = String(text[range])
    let wordTokenizer = NLTokenizer(unit: .word)
    wordTokenizer.string = sentence

    let words = wordTokenizer.tokens(for: sentence.startIndex ..< sentence.endIndex)
        .map { String(sentence[$0]) }

    sentences.append(words)
    return true
}
print(sentences)

[
["I", "am", "trying", "to", "create", "an", "array", "from", "a", "sentence"],
[“但是”、“我”、“不”、“理解”、“为什么”、“不是”、“该”、“最后”、“句号”、“已删除”、“从” , "the", "last", "word"]
]

NLTaggerNLTokenizer 之间有很多选项。根据您真正要解决的问题,这些可能比自己操作字符串更好。


正如 Sultan 所说,您显然可以只插入空格和 split 字符串,但我可能会建议添加其他标点符号并包括 + 以匹配连续标点符号的多个或一个字符(特别是省略号) ,...),例如

let words = text.replacingOccurrences(of: "[,.:;!?]+", with: " $0", options: .regularExpression)
    .split(separator: " ")

【讨论】:

  • Rob - 这真是太棒了,我可以看到我将不得不学习自然语言框架!再次感谢
【解决方案3】:

也许你想要这样:

 func convertSentenceToArray()-> [String] {
    var sentence = String(self)
 sentence =    sentence.replacingOccurrences(of: ".", with: " .")
 sentence =    sentence.replacingOccurrences(of: ",", with: " ,")
    return sentence.components(separatedBy: " ")
}

【讨论】:

  • 很好的解决方案,但还是有点太复杂了self.replacingOccurrences(of: "([,.])", with: " $1", options: .regularExpression)。现在不需要使用var sentence 副本。
  • 非常感谢 E.Coms 解决方案对我有用,我想我一定是太努力使用 .map 了!!!再次感谢所有回复的人
  • @sapjjr 谢谢。当你教我如何使用 optional.map 来使代码变得更简洁时:stackoverflow.com/questions/54441006/…
【解决方案4】:

这是一种更传统和通用的方法:

func separateString(string: String) -> [String]{
    let stringArray = Array(string.unicodeScalars)
    var stringsArray: [String] = []

    let letterSet = CharacterSet.letters
    let punctuationSet = CharacterSet.punctuationCharacters

    var newWord = ""
    var newPunctioationChar = ""

    for char in stringArray {
        if letterSet.contains(char) {
            newWord.unicodeScalars.append(char)

        } else if punctuationSet.contains(char) {
            newPunctioationChar.unicodeScalars.append(char)

            stringsArray.append(contentsOf: [newWord, newPunctioationChar])

            newWord = ""
            newPunctioationChar = ""
        }
    }

     return stringsArray
}

【讨论】:

    猜你喜欢
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    相关资源
    最近更新 更多