我有一个字符串集合,如下所示:
"0"
"90/100"
None
"1-5%/34B-1"
"-13/7"
我想将它们转换为整数(或None
),以便我从头开始选择数字并在第一个非数字字符处停止。因此,上述数据将变为:
0
90
None
1
None
我尝试做类似下面的代码,但遇到了多个问题,例如当int(new_n)
行只是空字符串时new_n
导致ValueError
。即使没有它,代码看起来也很糟糕:
def pick_right_numbers(old_n):
new_n = ''
numbers = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
if old_n is None:
return None
else:
for n in old_n:
if n in numbers:
new_n += n
else:
return int(new_n)
if new_n:
return int(new_n)
else:
return None
有人可以用这个把我推向正确的方向吗?
这是你要找的那种东西吗?
import re
data = ['0', '90/100', None, '1-5%/34B-1', '-13/7']
def pick_right_numbers(old_n):
if old_n is None:
return None
else:
digits = re.match("([0-9]*)",old_n).groups()[0]
if digits.isdigit():
return int(digits)
else:
return None
for string in data:
result = pick_right_numbers(string)
if result is not None:
print("Matched section is : {0:d}".format(result))
它使用re
(模式匹配)来检测字符串开头的数字块(匹配仅匹配字符串的开头,搜索将在字符串中的任何位置找到块)。它检查匹配项,确认匹配项是数字(否则最后一个数据元素匹配,但为空字符串),并将其转换为整数以返回。
的基本方法是:
input_list = ["0", "90/100", None, "1-5%/34B-1", "-13/7"]
char_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
output_list = []
for input_str in input_list:
if isinstance(input_str, str):
i = 0
for input_char in input_str:
if input_char in char_list:
i += 1
else:
break
else:
i = 0
if i:
output = int(input_str[0:i])
else:
output = None
output_list.append(output)
但是有很多变体。 如果这是一个您每天重复 10.000+ 次的功能,那么考虑一些性能分析将是明智的。
编辑:考虑Python 2与3中的字符串是什么可能是明智的(请参阅isinstance('aaa',basestring)和isinstance('aaa',str)之间的区别是什么?
edit2:了解 Bakuriu 的解决方案如何简化这一点 ->
from itertools import takewhile
input_list = ["0", "90/100", None, "1-5%/34B-1", "-13/7"]
output_list = []
for input_str in input_list:
text = ''.join(takewhile(str.isdigit, input_str or ""))
output_list.append(int(text) if text else None)
(所以我认为他应该补充说,这是诚实的最佳答案;)
>>> import re
>>> s = ["0", "90/100", None, "1-5%/34B-1", "-13/7"]
>>> [int(c) if c else None for c in (re.sub('([0-9]*).*', r'1', str(x)) for x in s)]
[0, 90, None, 1, None]
工作原理
我们有两个列表推导。 内部从列表s
的元素中删除除初始数字之外的所有内容:
>>> list(re.sub('([0-9]*).*', r'1', str(x)) for x in s)
['0', '90', '', '1', '']
外部列表推导会将这些字符串(如果为非空)转换为整数或以其他方式转换为None
:
>>> [int(c) if c else None for c in ('0', '90', '', '1', '')]
[0, 90, None, 1, None]
替代方法:使用takewhile
根据 Bakuriu 的评论,我们可以使用intertools.takewhile
代替re.sub
:
>>> from itertools import takewhile
>>> [int(c) if len(c) else None for c in (''.join(takewhile(str.isdigit, x or "")) for x in s)]
[0, 90, None, 1, None]
对原始代码的修改
或者,我们可以修改原始代码:
def pick_right_numbers(old_n):
if old_n is None:
return None
else:
new_n = ''
for n in old_n:
if not n.isdigit():
break
new_n += n
return int(new_n) if len(new_n) else None
此代码生成输出:
>>> [pick_right_numbers(x) for x in s]
[0, 90, None, 1, None]
有多种方法可以检查对象是否为数字。例如,请参阅此答案。
但是,您一次只需要检查一个字符,因此您的方法实际上很好。阵列将永久位于缓存中,因此将快速扫描。
请注意,你可以用更好的方式写它:
if n in "0123456789":
另一种可能性,可能是最快的,是检查范围,通过ASCII表示将它们视为数值(使用数字在该表示中是连续的,并且按照您期望的顺序):
zero = ord('0')
nine = ord('9')
for n in old_n:
nn = ord(n)
if (nn >= zero) and (nn <= nine):
当然,最优雅的方式是调用本地isdigit()
;您可以节省所有混乱并完全明确您的意图。请注意,它可能比您要求的要多 - 根据 Unicode ⑦
是一个数字。但是您不太可能遇到这种情况。另请注意,因此,它可能比您的实现慢。
请注意,您还需要在循环内检查new_n == ''
!不重复自己的最好方法是退出循环进入决赛,如果
def pick_right_numbers(old_n):
new_n = ''
if old_n is None:
return None
else:
for n in old_n:
if n.isdigit():
new_n += n
else:
break
if new_n:
return int(new_n)
else:
return None
当然,如果你需要速度,你可能不得不改变方法,因为你正在循环中增长一个向量。但是,如果这是对您有意义的逻辑,那么只有在这是程序的瓶颈时才使其复杂化。