2013-01-23 71 views
1

我們有大量包含可能是整數的子字符串的字符串,例如。驗證Python字符串中的整數

mystring = "123 345 456 567 678 789" 

和需要驗證:

一個。每個子字符串實際上是一個整數,例如。在mystring = "123 345 456 567 abc 789"達到'abc'時失敗

b。每個整數在範圍內0 < = i < = 10000例如。 mystring = "123 -345 456 567 678 789"當它達到 '-345'

一種解決方案是將失敗:

mylist= [int(i) for i in mystring.split() if isinstance(int(i), int) and (0 <= int(i) <= 10000)] 

的問題是:

我。在列表理解中,對於每個i,int(i)是否得到一次或多次評估?

ii。有沒有其他方法可以更快(因爲字符串的體積很大,每個字符串可能包含數百到數千的整數)?

+0

** a。每個整數是一個整數** ?? –

+0

給名爲'mystring'的變量分配一個列表聽起來像一個相當不好的主意? – geoffspear

+0

a。他顯然意味着子串。 'mystring'絕對應該是該作業左側的'mylist'。 – jgritty

回答

5

我想我可能會使用類似:

try: 
    if not all((0 <= int(i) <= 10000) for i in mystring.split()): 
     raise ValueError("arg!") 
except ValueError: 
    print "Oops, didn't pass" 

這具有短路,如果事情不能轉化爲int,或者如果它不能在正確的範圍不同的優勢。

這是一個愚蠢的測試:

def test_str(mystring): 
    try: 
     return all((0 <= int(i) <= 10000) for i in mystring.split()) 
    except ValueError: 
     return False 

print test_str("123 345 456 567 abc 789") 
print test_str("123 345 456 567 -300 789") 
print test_str("123 345 456 567 300 789") 
+0

啊,這很好,因爲它在短路時出現故障,並且不使用for-loop。不知道'全部'操作符 - 也將有助於其他驗證。最好。 –

1

int(i)被多次評估。此外,isinstance(int(i), int)是無用的,因爲int()將引發非整數輸入的異常,而不是靜默地返回非int。

將代碼編寫爲老式循環沒有任何問題。它爲您提供了錯誤處理方面最大的靈活性。如果您擔心效率問題,請記住列表理解只不過是這種循環的語法糖。

intlist = [] 
for part in mystring.split(): 
    try: 
     val = int(part) 
    except ValueError: 
     continue # or report the error 
    if val < 0 or val > 10000: 
     continue # or report the error 
    intlist.append(val) 
0

如果有非數字的字符串您的解決方案不起作用:

ValueError: invalid literal for int() with base 10: 'abc'

我會做這樣的事情:

mystring = "123 345 456 -123 567 abc 678 789" 

mylist = [] 
for i in mystring.split(): 
    try: 
     ii = int(i) 
    except ValueError: 
     print "{} is bad".format(i) 
    if 0 <= ii <= 10000: 
     mylist.append(ii) 
    else: 
     print "{} is out of range".format(i) 
print mylist 

回答您的問題:

i。

i。是的,不止一次。

ii。是的,已經提供了幾個例子。

我的輸出是這樣的:

-123 is out of range

abc is bad

[123, 345, 456, 567, 567, 678, 789]

0

你也可以使用正則表達式:

import re 
mystring = "123 345 456 567 abc 789 -300 ndas" 

re_integer = r'(-??\d+)' 
re_space_or_eof = r'(\ |$)' #using space or eof so we don't match floats 

#match all integers 
matches = re.finditer(re_integer + re_space_or_eof, mystring) 

#extract the str, convert to int for all matches 
int_matches = [int(num.groups()[0]) for num in matches] 

#filter based on criteria 
in_range = [rnum for rnum in int_matches if 0 <= rnum <=10000] 

>>> in_range 
[123, 345, 456, 567, 789] 
0

看來我錯過了辯論的熱度,但這裏的另一個 - 可能更快 - 做法:

>>> f = lambda(s): set(s) <= set('') and not filter(lambda n: int(n) > 10000, s.split()) 

測試:

>>> s1 = '123 345 456 567 678 789' 
>>> s2 = '123 345 456 567 678 789 100001' 
>>> s3 = '123 345 456 567 678 789 -3' 
>>> s4 = '123 345 456 567 678 789 ba' 
>>> f(s1) 
True 
>>> f(s2) 
False 
>>> f(s3) 
False 
>>> f(s4) 
False 

我沒有時間,但我懷疑它可能比其他建議的解決方案更快,因爲集合比較已經關注x < 0測試和不可分析的字符串,如abc。由於兩個測試(集合比較和數值範圍)在邏輯上連接,第一個失敗將阻止第二個失敗。

HTH!