【问题标题】:Replacing keywords in strings with sequence of symbols用符号序列替换字符串中的关键字
【发布时间】:2020-03-14 03:28:55
【问题描述】:

对于一个练习,我必须创建一个简单的脏话过滤器来了解课程。

过滤器使用一系列攻击性关键字和替换模板进行初始化。任何这些单词的每次出现都应替换为从模板生成的字符串。如果字长比模板短,则应使用从头开始的子字符串,对于较长的字号,应根据需要多次重复模板。

以下是我到目前为止的结果,并带有一个示例。

class ProfanityFilter:

    def __init__(self, keywords, template):
        self.__keywords = sorted(keywords, key=len, reverse=True)
        self.__template = template

    def filter(self, msg):

        def __replace_letters__(old_word, replace_str):
            replaced_word = ""
            old_index = 0
            replace_index = 0
            while old_index <= len(old_word):
                if replace_index == len(replace_str):
                    replace_index = 0
                else:
                    replaced_word += replace_str[replace_index]
                    replace_index += 1

                old_index += 1

            return replaced_word

        for keyword in self.__keywords:
            idx = 0
            while idx < len(msg):
                index_l = msg.lower().find(keyword.lower(), idx)
                if index_l == -1:
                    break
                msg = msg[:index_l] + __replace_letters__(keyword, self.__template) + msg[index_l + len(keyword):]
                idx = index_l + len(keyword)

        return msg


f = ProfanityFilter(["duck", "shot", "batch", "mastard"], "?#$")
offensive_msg = "this mastard shot my duck"
clean_msg = f.filter(offensive_msg)
print(clean_msg)  # should be: "this ?#$?#$? ?#$? my ?#$?"

示例应打印:

this ?#$?#$? ?#$? my ?#$?

但它会打印:

this ?#$?#$ ?#$? my ?#$?

由于某种原因,它用 6 个符号而不是 7 个(每个字母一个)替换了“mastard”一词。它适用于其他关键字,为什么不适用于这个?


此外,如果您发现其他任何问题,请随时告诉我。请记住,我是初学者,我的“工具箱”非常小。

【问题讨论】:

    标签: python string class replace


    【解决方案1】:

    您的问题在于索引逻辑。你有两个错误

    1. 每次到达替换字符串的末尾时,您跳过脏话中的一个字母:

          while old_index <= len(old_word):
              if replace_index == len(replace_str):
                  replace_index = 0
                  # You don't replace a letter; you just reset the new index, but ...
              else:
                  replaced_word += replace_str[replace_index]
                  replace_index += 1
      
              old_index += 1     # ... but you still advance the old index.
      

    您没有注意到这一点的原因是您有一个 second 错误:您从 0 len(old_word) 运行 old_index,这是一个 更多个字符比你开始。对于规范的四字母单词(或 5 或 6 个字符的单词),这两个错误相互抵消。您没有看到这一点,因为您没有进行足够的测试。例如,使用:

    f = ProfanityFilter(["StackOverflow", "PC"], "?#$")
    offensive_msg = "StackOverflow on PC rulez!"
    clean_msg = f.filter(offensive_msg)
    

    输出:

    ?#$?#$?#$?# on ?#$ rulez!
    

    输入的单词是13个和2个字母;替换为 11 和 3。

    修复这两个错误:使old_index 保持在界限内,并仅在您进行替换时增加它。

            while old_index < len(old_word):
                if replace_index == len(replace_str):
                    replace_index = 0
                else:
                    replaced_word += replace_str[replace_index]
                    replace_index += 1
                    old_index += 1
    

    未来的改进:

    • 将其重构为 for 循环。
    • 不要重置你的replace_index;事实上,摆脱它。只需使用old_index % len(replace_str)

    【讨论】:

      【解决方案2】:

      我会使用正则表达式来代替,因为 re.sub() 有一个方便的 API 用于动态替换:

      import re
      
      
      class ProfanityFilter:
      
          def __init__(self, keywords, template):
              # Build a regular expression that will match all of the profane words
              self.keyword_re = re.compile("|".join(re.escape(keyword) for keyword in keywords), re.I)
              self.template = template
      
          def _generate_replacement(self, word):
              l = len(word)
              # Figure out how many times to repeat the template
              repeats = (l // len(self.template)) + 1
              # Since we may end up with a string longer than the original,
              # slice to the correct length.
              return (self.template * repeats)[:l]
      
      
          def filter(self, msg):
              # Replace all occurrences of the regular expression with
              # a dynamically computed replacement value.
              return self.keyword_re.sub(
                  lambda m: self._generate_replacement(m.group(0)),
                  msg,
              )
      
      f = ProfanityFilter(["duck", "shot", "batch", "mastard"], "?#$")
      offensive_msg = "this mastard shot my duck"
      print(f.filter(offensive_msg))
      

      【讨论】:

        【解决方案3】:

        无法制作单线,但这是一个糟糕的实现方式。 不要像 VoNWooDSoN 那样做

        def replace(msg, keywords=["duck", "shot", "batch", "mastard"], template="?#$"):                                                                                                                                        
            for keyword in keywords * len(msg)):                                                                                                                                                   
                msg = (template*len(keyword))[:len(keyword)].join([msg[:msg.find(keyword)], msg[msg.find(keyword)+len(keyword):]]) if msg.find(keyword) > 0 else msg                                                            
            return msg                                                                                                                                                                                                          
        
        offensive_msg = "this mastard shot my duck"                                                                                                                                                                             
        clean_msg = replace(offensive_msg)                                                                                                                                                                                      
        
        print(clean_msg)  # should be: "this ?#$?#$? ?#$? my ?#$?"                                                                                                                                                              
        print(clean_msg=="this ?#$?#$? ?#$? my ?#$?")
        

        编辑 所以,我猜 3.8 有赋值表达式......所以,但这将是唯一的衬里(可能)。

        print ((lambda msg: [msg := (("?#$"*len(keyword))[:len(keyword)].join([msg[:msg.find(keyword)], msg[msg.find(keyword)+len(keyword):]]) if msg.find(keyword) > 0 else msg) for keyword in ["duck", "shot", "batch", "mastard"]])("this mastard shot my duck")[-1])
        

        【讨论】:

          猜你喜欢
          • 2011-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多