我有一组字符串,如:
"0"
"90/100"
None
"1-5%/34B-1"
"-13/7"
我想将它们转换为整数(或None
),以便我从头开始选择数字并停在第一个非数字字符处。因此上述数据将成为:
0
90
None
1
None
我尝试过类似下面的代码,但遇到了多个问题,例如当ValueError
只是空字符串时,int(new_n)
行导致new_n
。即使没有这个,代码看起来很可怕:
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
有人可以用这个推动我朝着正确的方向前进吗?
答案 0 :(得分:0)
>>> 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]
答案 1 :(得分:0)
执行此操作的基本方法是:
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 vs 3中的字符串可能是明智的(参见What is the difference between isinstance('aaa', basestring) and 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)
(所以我认为他应该将其作为诚实的最佳答案;)
答案 2 :(得分:0)
这是你要找的东西吗?
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
(模式匹配)来检测字符串开头的数字块(匹配只匹配字符串的开头,搜索会在字符串中的任何位置找到一个块)。
它检查匹配,确认匹配是数字(否则最后一个数据元素匹配,但是是空字符串)并将其转换为整数以返回。
答案 3 :(得分:0)
有多种方法可以检查对象是否为数字。例如,请参阅this answer。
但是你只需要一次检查一个字符,所以你的方法实际上很好。该阵列将永久保存在缓存中,因此可以快速扫描。
请注意,您可以以更好的方式编写它:
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
当然,如果你需要速度,你可能不得不改变方法,因为你在循环中增长一个向量。但是,如果这是对你有意义的逻辑,只有在这是程序的瓶颈时才会使它复杂化。