【问题标题】:Python, a smarter way of string to integer conversionPython,一种更智能的字符串到整数转换方式
【发布时间】:2011-01-30 19:03:54
【问题描述】:

我已经编写了这段代码来将格式为 "0(532) 222 22 22" 的字符串转换为整数,例如 05322222222。

class Phone():
    def __init__(self,input):
        self.phone = input
    def __str__(self):
        return self.phone
    #convert to integer.
    def to_int(self):
        return int((self.phone).replace(" ","").replace("(","").replace(")",""))

test = Phone("0(532) 222 22 22")
print test.to_int()

用 3 种替换方法来解决这个问题感觉很笨拙。我很好奇是否有更好的解决方案?

【问题讨论】:

  • 为什么?电话号码不是整数 - 它是一串数字,有时是其他字符。将其转换为 int 没有任何用处,并且会丢失前导零等信息。不要这样做。
  • 如果电话号码长度始终保持不变,切断前导零并不重要。

标签: python string integer type-conversion


【解决方案1】:
p = "0(532) 222 22 22"
print ''.join([x for x in p if x.isdigit()])

请注意,如果您想将前导零转换为 int(就像您在标题中建议的那样),您将“丢失”前导零。如果你想这样做,只需将上述内容包装在int() 调用中。不过,电话号码作为字符串确实更有意义(在我看来)。

【讨论】:

  • 随意放弃方括号。
  • 方括号仅在较新版本的 Python 中是可选的。
  • @gabe 如果“较新”是指“2.4 或更高”,现在已经 5 岁了?
  • 我的很多系统都停留在 2.3 上,但我怀疑只有我一个人。
【解决方案2】:

在 Python 2.6 或 2.7 中,
(self.phone).translate(None,' ()')
将从电话​​字符串中删除任何空格或 ()。详情请见Python 2.6 doc on str.translate

在 Python 3.x 中,str.translate() 采用映射(而不是如上所示的两个字符串)。因此,对应的 sn-p 类似于以下内容,使用 str.maketrans() 生成映射。
'(self.phone).translate(str.maketrans('','', '()-/ '))
详情请见Python 3.1 doc on str.translate

【讨论】:

  • 也很好,糟透了 debian 使用 2.5 来稳定 :(
  • @Hellnar:我在 CentOS 5 上经常被限制为 2.4。我实际上比我更喜欢 ChristopheD 的答案。
【解决方案3】:

只使用正则表达式怎么样?

例子:

>>> import re
>>> num = '0(532) 222 22 22'
>>> re.sub('[\D]', '', num) # Match all non-digits ([\D]), replace them with empty string, where found in the `num` variable.
'05322222222'

ChristopheD 提出的建议可以正常工作,但效率不高。

以下是使用 dis 模块演示这一点的测试程序(有关更多详细信息,请参阅模块 here 上的 Doug Hellman 的 PyMOTW)。

TEST_PHONE_NUM = '0(532) 222 22 22'

def replace_method():
    print (TEST_PHONE_NUM).replace(" ","").replace("(","").replace(")","")

def list_comp_is_digit_method():
    print ''.join([x for x in TEST_PHONE_NUM if x.isdigit()])

def translate_method():
    print (TEST_PHONE_NUM).translate(None,' ()')

import re
def regex_method():
    print re.sub('[\D]', '', TEST_PHONE_NUM)

if __name__ == '__main__':
    from dis import dis

    print 'replace_method:'
    dis(replace_method)
    print
    print

    print 'list_comp_is_digit_method:'
    dis(list_comp_is_digit_method)

    print
    print

    print 'translate_method:'
    dis(translate_method)

    print
    print
    print "regex_method:"
    dis(phone_digit_strip_regex)
    print

输出:

replace_method:
  5       0 LOAD_GLOBAL              0 (TEST_PHONE_NUM)
          3 LOAD_ATTR                1 (replace)
          6 LOAD_CONST               1 (' ')
          9 LOAD_CONST               2 ('')
         12 CALL_FUNCTION            2
         15 LOAD_ATTR                1 (replace)
         18 LOAD_CONST               3 ('(')
         21 LOAD_CONST               2 ('')
         24 CALL_FUNCTION            2
         27 LOAD_ATTR                1 (replace)
         30 LOAD_CONST               4 (')')
         33 LOAD_CONST               2 ('')
         36 CALL_FUNCTION            2
         39 PRINT_ITEM          
         40 PRINT_NEWLINE       
         41 LOAD_CONST               0 (None)
         44 RETURN_VALUE   

phone_digit_strip_list_comp:
  3           0 LOAD_CONST               1 ('0(532) 222 22 22')
              3 STORE_FAST               0 (phone)

  4           6 LOAD_CONST               2 ('')
              9 LOAD_ATTR                0 (join)
             12 BUILD_LIST               0
             15 DUP_TOP             
             16 STORE_FAST               1 (_[1])
             19 LOAD_GLOBAL              1 (test_phone_num)
             22 GET_ITER            
             23 FOR_ITER                30 (to 56)
             26 STORE_FAST               2 (x)
             29 LOAD_FAST                2 (x)
             32 LOAD_ATTR                2 (isdigit)
             35 CALL_FUNCTION            0
             38 JUMP_IF_FALSE           11 (to 52)
             41 POP_TOP             
             42 LOAD_FAST                1 (_[1])
             45 LOAD_FAST                2 (x)
             48 LIST_APPEND         
             49 JUMP_ABSOLUTE           23
             52 POP_TOP             
             53 JUMP_ABSOLUTE           23
             56 DELETE_FAST              1 (_[1])
             59 CALL_FUNCTION            1
             62 PRINT_ITEM          
             63 PRINT_NEWLINE       
             64 LOAD_CONST               0 (None)
             67 RETURN_VALUE   

translate_method:
  11           0 LOAD_GLOBAL              0 (TEST_PHONE_NUM)
               3 LOAD_ATTR                1 (translate)
               6 LOAD_CONST               0 (None)
               9 LOAD_CONST               1 (' ()')
              12 CALL_FUNCTION            2
              15 PRINT_ITEM          
              16 PRINT_NEWLINE       
              17 LOAD_CONST               0 (None)
              20 RETURN_VALUE      

phone_digit_strip_regex:
  8       0 LOAD_CONST               1 ('0(532) 222 22 22')
          3 STORE_FAST               0 (phone)

  9       6 LOAD_GLOBAL              0 (re)
          9 LOAD_ATTR                1 (sub)
         12 LOAD_CONST               2 ('[\\D]')
         15 LOAD_CONST               3 ('')
         18 LOAD_GLOBAL              2 (test_phone_num)
         21 CALL_FUNCTION            3
         24 PRINT_ITEM          
         25 PRINT_NEWLINE       
         26 LOAD_CONST               0 (None)
         29 RETURN_VALUE        

translate 方法将是最有效的,尽管它依赖于 py2.6+。正则表达式的效率稍低,但更兼容(我认为这是您的要求)。原来的替换方法每次替换都会增加 6 条额外的指令,而其他所有指令都保持不变。

附带说明,将您的电话号码存储为字符串以处理前导零,并在需要时使用电话格式化程序。相信我,它以前咬过我。

【讨论】:

  • 那里不需要字符类,使用+ 量词会稍微高效一些。我的问题是:“自从dis.dis 的输出显示效率?”
  • 是的,你是对的。 dis.dis 只会深入了解编译器正在做什么,而不是实际的执行速度。我将更新测试以使用 timeit 模块。尽管如此,您是否同意 re 是以跨版本兼容的方式做到这一点的最佳方式?
【解决方案4】:

SilentGhost:dis.dis确实展示了潜在的概念/执行复杂性。毕竟,OP抱怨原来的替换链太“笨拙”,而不是太“慢”。

我建议不要在非不可避免的情况下使用正则表达式;他们只是增加了概念上的开销和速度损失。在这里使用translate() 恕我直言,这是一个错误的工具,而且在概念上没有原始替换链那么简单和通用。

所以你说 tamaytoes,我说 tomahtoes:原始解决方案在清晰度和通用性方面非常好。它一点也不笨拙。为了使其更密集和更参数化,请考虑将其更改为

phone_nr_translations = [ 
    ( ' ', '', ), 
    ( '(', '', ), 
    ( ')', '', ), ]

def sanitize_phone_nr( phone_nr ):
  R = phone_nr
  for probe, replacement in phone_nr_translations:
    R = R.replace( probe, replacement )
  return R

当然,在这个特殊的应用程序中,您真正想要做的只是消除任何不需要的字符,因此您可以简化:

probes = ' ()'

def sanitize_phone_nr( phone_nr ):
  R = phone_nr
  for probe in probes:
    R = R.replace( probe, '' )
  return R

回想起来,我不太清楚为什么要将电话号码转换为整数——这只是错误的数据类型。这可以通过以下事实来证明:至少在移动网络中,+# 甚至更多是拨号字符串中的有效字符(拨号,字符串——看到了吗?)。

但除此之外,清理用户输入电话号码以获得标准化和安全的表示是一个非常非常有效的问题 - 只是我觉得你的方法太具体了。为什么不将消毒方法重写为非常通用的东西而不变得更复杂呢?毕竟,您如何确保您的用户不会在该 Web 表单字段中输入其他异常字符?

所以你真正想要的不是dis - 允许特定字符(在 unicode 5.1 中定义了大约十万个代码点,那么如何赶上这些?),而是 允许那些在拨号字符串中被认为是合法的字符。你可以用正则表达式来做到这一点......

from re import compile as _new_regex
illegal_phone_nr_chrs_re = _new_regex( r"[^0-9#+]" )

def sanitize_phone_nr( phone_nr ):
  return illegal_phone_nr_chrs_re.sub( '', phone_nr )

...或带有一组:

legal_phone_nr_chrs = set( '0123456789#+' )

def sanitize_phone_nr( phone_nr ):
    return ''.join( 
        chr for chr in phone_nr 
            if chr in legal_phone_nr_chrs )

最后一节可以写在一行上。此解决方案的缺点是您从 Python 中迭代输入字符,而不使用 str.replace() 甚至正则表达式提供的潜在更快的 C 遍历。然而,在任何情况下,性能都取决于预期的使用模式(我相信你首先会截断你的手机 nrs,对吗?所以要处理的将是许多小字符串,而不是几个大字符串)。

请注意这里的几点:我力求清晰,这就是为什么我尽量避免过度使用缩写。 chr 代表characternr 代表numberR 代表返回值(更可能是,呃,在标准库中使用的retval)在我的风格书中。编程是关于让事情被理解和完成,而不是程序员编写接近 gzip 空间效率的代码。现在看,最后一个解决方案在...

legal_phone_nr_chrs = set( '0123456789#+' )
def sanitize_phone_nr( phone_nr ): return ''.join( chr for chr in phone_nr if chr in legal_phone_nr_chrs )

...如果需要,两行代码,而 OP 的代码...

class Phone():
    def __init__  ( self, input ): self.phone = self._sanitize( input )
    def __str__   ( self        ): return self.phone
    def _sanitize ( self, input ): return input.replace( ' ', '' ).replace( '(', '' ).replace( ')', '' )

...几乎不能压缩到四行以下。看看严格的 OOP 解决方案给您带来了哪些额外的负担?我相信大多数时候它可以被排除在外。

【讨论】:

    猜你喜欢
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多