【问题标题】:Removing non numeric characters from a string从字符串中删除非数字字符
【发布时间】:2013-06-24 14:06:57
【问题描述】:

我的任务是从文本文件或字符串中删除所有非数字字符,包括空格,然后在旧字符旁边打印新结果,例如:

之前:

sd67637 8

之后:

676378

由于我是初学者,我不知道从哪里开始这项任务。请帮忙

【问题讨论】:

标签: python python-3.x python-3.3


【解决方案1】:

我不会为此使用 RegEx。速度慢了很多!

我们只使用一个简单的for 循环。

TLDR;

此功能将快速完成工作...

def filter_non_digits(string: str) -> str:
    result = ''
    for char in string:
        if char in '1234567890':
            result += char
    return result 

解释

让我们创建一个非常基本的基准来测试已提出的几种不同方法。我将测试三种方法...

  1. For 循环方法(我的想法)。
  2. 来自Jon Clements' answer的列表理解方法。
  3. 来自Moradnejad's answer 的RegEx 方法。
# filters.py

import re

# For loop method
def filter_non_digits_for(string: str) -> str:
    result = ''
    for char in string:
        if char in '1234567890':
            result += char
    return result 


# Comprehension method
def filter_non_digits_comp(s: str) -> str:
    return ''.join(ch for ch in s if ch.isdigit())


# RegEx method
def filter_non_digits_re(string: str) -> str:
    return re.sub('[^\d]','', string)

现在我们已经实现了每种删除数字的方法,让我们对每一种方法进行基准测试。

这里是一些非常基本和基本的基准代码。但是,它会起到作用,并让我们很好地比较每种方法的执行情况。

# tests.py

import time, platform
from filters import filter_non_digits_re,
                    filter_non_digits_comp,
                    filter_non_digits_for


def benchmark_func(func):
    start = time.time()
    # the "_" in the number just makes it more readable
    for i in range(100_000):
        func('afes098u98sfe')
    end = time.time()
    return (end-start)/100_000


def bench_all():
    print(f'# System ({platform.system()} {platform.machine()})')
    print(f'# Python {platform.python_version()}\n')

    tests = [
        filter_non_digits_re,
        filter_non_digits_comp,
        filter_non_digits_for,
    ]

    for t in tests:
        duration = benchmark_func(t)
        ns = round(duration * 1_000_000_000)
        print(f'{t.__name__.ljust(30)} {str(ns).rjust(6)} ns/op')


if __name__ == "__main__":
    bench_all()

这是基准代码的输出。

# System (Windows AMD64)
# Python 3.9.8

filter_non_digits_re             2920 ns/op
filter_non_digits_comp           1280 ns/op
filter_non_digits_for             660 ns/op

如您所见,filter_non_digits_for() 函数比使用 RegEx 快四倍以上,大约是理解方法的两倍。有时简单是最好的。

【讨论】:

    【解决方案2】:

    添加到 @MoradneJad 中。您可以使用以下代码提取整数值、浮点数甚至有符号值。

    a = re.findall(r"[-+]?\d*\.\d+|\d+", "Over th44e same pe14.1riod of time, p-0.8rices also rose by 82.8p")

    然后您可以使用map 有效地将列表项转换为数字数据类型。

    print(list(map(float, a)))

    [44.0, 14.1, -0.8, 82.8]

    【讨论】:

      【解决方案3】:

      提取整数

      示例:sd67637 8 ==> 676378

      import re
      def extract_int(x):
          return re.sub('[^\d]','', x)
      

      提取单个浮点数/整数(可能的小数分隔符)

      示例:sd7512.sd23 ==> 7512.23

      import re
      def extract_single_float(x):
          return re.sub('[^\d|\.]','', x)
      

      提取多个浮点数/浮点数

      示例:123.2 xs12.28 4 ==> [123.2, 12.28, 4]

      import re
      def extract_floats(x):
          return re.findall("\d+\.\d+", x)
      

      【讨论】:

        【解决方案4】:

        您可以使用string.ascii_letters 来识别您的非数字:

        from string import *
        
        a = 'sd67637 8'
        a = a.replace(' ', '')
        
        for i in ascii_letters:
            a = a.replace(i, '')
        

        如果您想替换冒号,请使用引号 " 而不是冒号 '

        【讨论】:

        • 冒号呢?
        • @jtlz2 然后你使用a = a.replace("'", ""),注意引号内的冒号
        • ' 不是冒号,而是单引号。 : 是一个冒号。这个答案只替换 [a-z] (忽略大小写)。最后,如果只使用 ascii_letters,为什么要从字符串中导入 *?
        【解决方案5】:

        为此有一个builtin

        string.translate(s, table[, deletechars])

        从 s 中删除所有字符 在 deletechars 中(如果存在),然后翻译 使用 table 的字符,它必须是 256 个字符的字符串 每个字符值的翻译,由它的序数索引。如果 table 为 None,则只执行字符删除步骤。

        >>> import string
        >>> non_numeric_chars = ''.join(set(string.printable) - set(string.digits))
        >>> non_numeric_chars = string.printable[10:]  # more effective method. (choose one)
        'sd67637 8'.translate(None, non_numeric_chars)
        '676378'
        

        或者你可以在没有导入的情况下做到这一点(但没有理由这样做):

        >>> chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
        >>> 'sd67637 8'.translate(None, chars)
        '676378'
        

        【讨论】:

        • 这应该是最佳答案。
        • 不是真的&gt;&gt;&gt; 's.,d67637 8'.translate(None, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ') 产生'.,676378'
        • @DarthKotik OP 没有提及特殊字符或编码。无论如何,string.translate 可以通过正确的输入解决所有这些问题。就像每个问题一样,它应该一次解决一个步骤。在敏捷开发中,不需要过早的优化。问题很简单,答案很简单。如果您想了解细节,我们将在这里一整天。
        • 不兼容 Python 3。非常过时的答案。
        • @InbarRose 请更新python 3的答案(stackoverflow.com/a/41708804/828885
        【解决方案6】:

        最简单的方法是使用正则表达式

        import re
        a = 'lkdfhisoe78347834 (())&/&745  '
        result = re.sub('[^0-9]','', a)
        
        print result
        >>> '78347834745'
        

        【讨论】:

        • 有什么办法可以保留小数?
        • 为什么不[^\d]+
        • @Mark 这应该可以工作re.findall(r"[-+]?\d*\.\d+|\d+", "Over th44e same pe14.1riod of time, p-0.8rices also rose by 82.8p")。这也应该提取浮点数和有符号值。
        • 您可以通过将小数作为'[^0-9.]'添加到正则表达式来包含小数
        【解决方案7】:

        逐个字符循环遍历你的字符串,并且只包含数字:

        new_string = ''.join(ch for ch in your_string if ch.isdigit())
        

        或者在你的字符串上使用正则表达式(如果在某个时候你想分别处理不连续的组)...

        import re
        s = 'sd67637 8' 
        new_string = ''.join(re.findall(r'\d+', s))
        # 676378
        

        那就print他们出去吧:

        print(old_string, '=', new_string)
        

        【讨论】:

        • 这样更好,因为它不仅仅适用于 ascii
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-30
        • 2011-10-19
        相关资源
        最近更新 更多