Apakah ada cara untuk mengetahui apakah string mewakili integer (misalnya '3'
,, '-17'
tetapi tidak '3.14'
atau 'asfasfas'
) Tanpa menggunakan mekanisme coba / kecuali?
is_int('3.14') = False
is_int('-7') = True
Apakah ada cara untuk mengetahui apakah string mewakili integer (misalnya '3'
,, '-17'
tetapi tidak '3.14'
atau 'asfasfas'
) Tanpa menggunakan mekanisme coba / kecuali?
is_int('3.14') = False
is_int('-7') = True
Jawaban:
Jika Anda benar-benar kesal menggunakan try/except
s di semua tempat, harap tulis fungsi pembantu:
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False
Ini akan menjadi kode WAY lebih banyak untuk menutupi semua string yang oleh Python dianggap integer. Saya katakan hanya pythonic yang satu ini.
>>> print RepresentsInt(10.0)
True
>>> print RepresentsInt(10.06)
True
dengan bilangan bulat positif yang dapat Anda gunakan .isdigit
:
>>> '16'.isdigit()
True
itu tidak bekerja dengan bilangan bulat negatif. misalkan Anda dapat mencoba yang berikut:
>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True
itu tidak akan berfungsi dengan '16.0'
format, yang mirip dengan int
casting dalam pengertian ini.
edit :
def check_int(s):
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
u'²'.isdigit()
benar tetapi int(u'²')
meningkatkan ValueError. Gunakan u.isdecimal()
sebagai gantinya. str.isdigit()
bergantung pada lokal pada Python 2.
check_int('')
akan menaikkan pengecualian alih-alih kembaliFalse
Anda tahu, saya telah menemukan (dan saya telah menguji ini berulang-ulang) yang mencoba / kecuali tidak melakukan semua itu dengan baik, untuk alasan apa pun. Saya sering mencoba beberapa cara melakukan hal-hal, dan saya tidak berpikir saya pernah menemukan metode yang menggunakan try / kecuali untuk melakukan yang terbaik dari yang diuji, pada kenyataannya tampaknya bagi saya metode-metode itu biasanya keluar dekat dengan terburuk, kalau bukan yang terburuk. Tidak dalam setiap kasus, tetapi dalam banyak kasus. Saya tahu banyak orang mengatakan itu adalah cara "Pythonic", tapi itu satu area di mana saya berpisah dengan mereka. Bagi saya, itu tidak terlalu berkinerja atau sangat elegan, jadi, saya cenderung hanya menggunakannya untuk menjebak dan melaporkan kesalahan.
Saya akan mengeluh bahwa PHP, perl, ruby, C, dan bahkan shell freaking memiliki fungsi sederhana untuk menguji string untuk integer-hood, tetapi karena ketekunan dalam memverifikasi asumsi-asumsi itu membuat saya tersandung! Rupanya kekurangan ini adalah penyakit yang umum.
Berikut ini edit tulisan Bruno yang cepat dan kotor:
import sys, time, re
g_intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
testvals = [
# integers
0, 1, -1, 1.0, -1.0,
'0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '06',
# non-integers
'abc 123',
1.1, -1.1, '1.1', '-1.1', '+1.1',
'1.1.1', '1.1.0', '1.0.1', '1.0.0',
'1.0.', '1..0', '1..',
'0.0.', '0..0', '0..',
'one', object(), (1,2,3), [1,2,3], {'one':'two'},
# with spaces
' 0 ', ' 0.', ' .0','.01 '
]
def isInt_try(v):
try: i = int(v)
except: return False
return True
def isInt_str(v):
v = str(v).strip()
return v=='0' or (v if v.find('..') > -1 else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()
def isInt_re(v):
import re
if not hasattr(isInt_re, 'intRegex'):
isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
return isInt_re.intRegex.match(str(v).strip()) is not None
def isInt_re2(v):
return g_intRegex.match(str(v).strip()) is not None
def check_int(s):
s = str(s)
if s[0] in ('-', '+'):
return s[1:].isdigit()
return s.isdigit()
def timeFunc(func, times):
t1 = time.time()
for n in range(times):
for v in testvals:
r = func(v)
t2 = time.time()
return t2 - t1
def testFuncs(funcs):
for func in funcs:
sys.stdout.write( "\t%s\t|" % func.__name__)
print()
for v in testvals:
if type(v) == type(''):
sys.stdout.write("'%s'" % v)
else:
sys.stdout.write("%s" % str(v))
for func in funcs:
sys.stdout.write( "\t\t%s\t|" % func(v))
sys.stdout.write("\r\n")
if __name__ == '__main__':
print()
print("tests..")
testFuncs((isInt_try, isInt_str, isInt_re, isInt_re2, check_int))
print()
print("timings..")
print("isInt_try: %6.4f" % timeFunc(isInt_try, 10000))
print("isInt_str: %6.4f" % timeFunc(isInt_str, 10000))
print("isInt_re: %6.4f" % timeFunc(isInt_re, 10000))
print("isInt_re2: %6.4f" % timeFunc(isInt_re2, 10000))
print("check_int: %6.4f" % timeFunc(check_int, 10000))
Berikut adalah hasil perbandingan kinerja:
timings..
isInt_try: 0.6426
isInt_str: 0.7382
isInt_re: 1.1156
isInt_re2: 0.5344
check_int: 0.3452
Metode AC dapat memindai Sekali Melalui, dan dilakukan. Metode AC yang memindai string sekali melalui akan menjadi Hal yang Benar untuk dilakukan, saya pikir.
EDIT:
Saya telah memperbarui kode di atas untuk bekerja di Python 3.5, dan untuk memasukkan fungsi check_int dari jawaban yang paling banyak dipilih saat ini, dan untuk menggunakan regex paling populer saat ini yang dapat saya temukan untuk pengujian integer-hood. Regex ini menolak string seperti 'abc 123'. Saya telah menambahkan 'abc 123' sebagai nilai tes.
Sangat menarik bagi saya untuk mencatat, pada titik ini, bahwa TIDAK ada fungsi yang diuji, termasuk metode coba, fungsi check_int yang populer, dan regex paling populer untuk pengujian integer-hood, mengembalikan jawaban yang benar untuk semua nilai tes (baik, tergantung pada apa yang Anda pikirkan jawaban yang benar; lihat hasil tes di bawah).
Fungsi built-in int () secara diam-diam memotong bagian fraksional dari angka titik mengambang dan mengembalikan bagian bilangan bulat sebelum desimal, kecuali jika angka titik mengambang pertama kali dikonversi ke string.
Fungsi check_int () mengembalikan false untuk nilai-nilai seperti 0,0 dan 1.0 (yang secara teknis bilangan bulat) dan mengembalikan true untuk nilai-nilai seperti '06'.
Berikut adalah hasil tes (Python 3.5) saat ini:
isInt_try | isInt_str | isInt_re | isInt_re2 | check_int |
0 True | True | True | True | True |
1 True | True | True | True | True |
-1 True | True | True | True | True |
1.0 True | True | False | False | False |
-1.0 True | True | False | False | False |
'0' True | True | True | True | True |
'0.' False | True | False | False | False |
'0.0' False | True | False | False | False |
'1' True | True | True | True | True |
'-1' True | True | True | True | True |
'+1' True | True | True | True | True |
'1.0' False | True | False | False | False |
'-1.0' False | True | False | False | False |
'+1.0' False | True | False | False | False |
'06' True | True | False | False | True |
'abc 123' False | False | False | False | False |
1.1 True | False | False | False | False |
-1.1 True | False | False | False | False |
'1.1' False | False | False | False | False |
'-1.1' False | False | False | False | False |
'+1.1' False | False | False | False | False |
'1.1.1' False | False | False | False | False |
'1.1.0' False | False | False | False | False |
'1.0.1' False | False | False | False | False |
'1.0.0' False | False | False | False | False |
'1.0.' False | False | False | False | False |
'1..0' False | False | False | False | False |
'1..' False | False | False | False | False |
'0.0.' False | False | False | False | False |
'0..0' False | False | False | False | False |
'0..' False | False | False | False | False |
'one' False | False | False | False | False |
<obj..> False | False | False | False | False |
(1, 2, 3) False | False | False | False | False |
[1, 2, 3] False | False | False | False | False |
{'one': 'two'} False | False | False | False | False |
' 0 ' True | True | True | True | False |
' 0.' False | True | False | False | False |
' .0' False | False | False | False | False |
'.01 ' False | False | False | False | False |
Baru saja saya mencoba menambahkan fungsi ini:
def isInt_float(s):
try:
return float(str(s)).is_integer()
except:
return False
Berkinerja hampir sama baiknya dengan check_int (0.3486) dan mengembalikan nilai true seperti 1.0 dan 0.0 dan +1.0 dan 0. dan .0 dan seterusnya. Tetapi juga mengembalikan true untuk '06', jadi. Pilih racunmu, kurasa.
try
lebih efisien: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
str.isdigit()
harus melakukan trik.
Contoh:
str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False
EDIT : Seperti yang ditunjukkan @BuzzMoschetti, cara ini akan gagal untuk angka minus (mis., "-23" ). Jika input_num Anda kurang dari 0, gunakan re.sub (regex_search, regex_replace, content) sebelum menerapkan str.isdigit () . Sebagai contoh:
import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Gunakan ekspresi reguler:
import re
def RepresentsInt(s):
return re.match(r"[-+]?\d+$", s) is not None
Jika Anda harus menerima pecahan desimal juga:
def RepresentsInt(s):
return re.match(r"[-+]?\d+(\.0*)?$", s) is not None
Untuk peningkatan kinerja jika Anda sering melakukan ini, kompilasi ekspresi reguler hanya sekali menggunakan re.compile()
.
Solusi RegEx yang tepat akan menggabungkan ide-ide Greg Hewgill dan Nowell, tetapi tidak menggunakan variabel global. Anda dapat melakukannya dengan melampirkan atribut ke metode ini. Juga, saya tahu bahwa itu tidak disukai untuk memasukkan impor dalam suatu metode, tetapi yang saya maksudkan adalah efek "modul malas" seperti http://peak.telecommunity.com/DevCenter/Importing#lazy-imports
sunting: Teknik favorit saya sejauh ini adalah menggunakan metode eksklusif objek String.
#!/usr/bin/env python
# Uses exclusively methods of the String object
def isInteger(i):
i = str(i)
return i=='0' or (i if i.find('..') > -1 else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()
# Uses re module for regex
def isIntegre(i):
import re
if not hasattr(isIntegre, '_re'):
print("I compile only once. Remove this line when you are confident in that.")
isIntegre._re = re.compile(r"[-+]?\d+(\.0*)?$")
return isIntegre._re.match(str(i)) is not None
# When executed directly run Unit Tests
if __name__ == '__main__':
for obj in [
# integers
0, 1, -1, 1.0, -1.0,
'0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0',
# non-integers
1.1, -1.1, '1.1', '-1.1', '+1.1',
'1.1.1', '1.1.0', '1.0.1', '1.0.0',
'1.0.', '1..0', '1..',
'0.0.', '0..0', '0..',
'one', object(), (1,2,3), [1,2,3], {'one':'two'}
]:
# Notice the integre uses 're' (intended to be humorous)
integer = ('an integer' if isInteger(obj) else 'NOT an integer')
integre = ('an integre' if isIntegre(obj) else 'NOT an integre')
# Make strings look like strings in the output
if isinstance(obj, str):
obj = ("'%s'" % (obj,))
print("%30s is %14s is %14s" % (obj, integer, integre))
Dan untuk anggota kelas yang kurang berani, inilah hasilnya:
I compile only once. Remove this line when you are confident in that.
0 is an integer is an integre
1 is an integer is an integre
-1 is an integer is an integre
1.0 is an integer is an integre
-1.0 is an integer is an integre
'0' is an integer is an integre
'0.' is an integer is an integre
'0.0' is an integer is an integre
'1' is an integer is an integre
'-1' is an integer is an integre
'+1' is an integer is an integre
'1.0' is an integer is an integre
'-1.0' is an integer is an integre
'+1.0' is an integer is an integre
1.1 is NOT an integer is NOT an integre
-1.1 is NOT an integer is NOT an integre
'1.1' is NOT an integer is NOT an integre
'-1.1' is NOT an integer is NOT an integre
'+1.1' is NOT an integer is NOT an integre
'1.1.1' is NOT an integer is NOT an integre
'1.1.0' is NOT an integer is NOT an integre
'1.0.1' is NOT an integer is NOT an integre
'1.0.0' is NOT an integer is NOT an integre
'1.0.' is NOT an integer is NOT an integre
'1..0' is NOT an integer is NOT an integre
'1..' is NOT an integer is NOT an integre
'0.0.' is NOT an integer is NOT an integre
'0..0' is NOT an integer is NOT an integre
'0..' is NOT an integer is NOT an integre
'one' is NOT an integer is NOT an integre
<object object at 0x103b7d0a0> is NOT an integer is NOT an integre
(1, 2, 3) is NOT an integer is NOT an integre
[1, 2, 3] is NOT an integer is NOT an integre
{'one': 'two'} is NOT an integer is NOT an integre
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False
Jadi fungsi Anda adalah:
def is_int(val):
return val[1].isdigit() and val.lstrip("-+").isdigit()
Pendekatan Greg Hewgill kehilangan beberapa komponen: "^" yang memimpin hanya cocok dengan awal string, dan menyusun ulang sebelumnya. Tetapi pendekatan ini akan memungkinkan Anda untuk menghindari mencoba: kecuali:
import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
return INT_RE.match(str(s)) is not None
Saya akan tertarik mengapa Anda mencoba untuk menghindari mencoba: kecuali?
Saya harus melakukan ini sepanjang waktu, dan saya memiliki keengganan yang ringan tetapi diakui tidak rasional untuk menggunakan pola coba / kecuali. Saya menggunakan ini:
all([xi in '1234567890' for xi in x])
Itu tidak mengakomodasi angka negatif, jadi Anda bisa menghilangkan satu tanda minus (jika ada), dan kemudian memeriksa apakah hasilnya terdiri dari angka 0-9:
all([xi in '1234567890' for xi in x.replace('-', '', 1)])
Anda juga bisa meneruskan x ke str () jika Anda tidak yakin inputnya berupa string:
all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])
Setidaknya ada dua (tepi?) Kasus di mana ini berantakan:
type(1E2)
memberi <class 'float'>
sedangkan type(10^2)
memberi<class 'int'>
.Jadi itu tidak akan berfungsi untuk setiap input yang mungkin, tetapi jika Anda dapat mengecualikan notasi ilmiah, notasi eksponensial, dan string kosong, ini merupakan pemeriksaan satu-baris OK yang mengembalikan False
jika x bukan bilangan bulat danTrue
jika x adalah bilangan bulat.
Saya tidak tahu apakah ini pythonic, tetapi hanya satu baris, dan relatif jelas apa yang dilakukan oleh kode tersebut.
all(xi in '1234567890' for xi in x])
pola saya sepertinya lebih seperti meminta izin untuk berjalan melintasi halaman. Saya tidak senang menjadi penanya izin, tapi di sini kita.
kupikir
s.startswith('-') and s[1:].isdigit()
akan lebih baik untuk menulis ulang ke:
s.replace('-', '').isdigit()
karena s [1:] juga membuat string baru
Tetapi solusi yang jauh lebih baik adalah
s.lstrip('+-').isdigit()
replace
terjadi? Juga, ini akan salah menerima 5-2
, misalnya.
s='-'
Saya sangat menyukai pos Shavais, tetapi saya menambahkan satu lagi test case (& fungsi isdigit () bawaan):
def isInt_loop(v):
v = str(v).strip()
# swapping '0123456789' for '9876543210' makes nominal difference (might have because '1' is toward the beginning of the string)
numbers = '0123456789'
for i in v:
if i not in numbers:
return False
return True
def isInt_Digit(v):
v = str(v).strip()
return v.isdigit()
dan secara signifikan mengalahkan waktu sisanya:
timings..
isInt_try: 0.4628
isInt_str: 0.3556
isInt_re: 0.4889
isInt_re2: 0.2726
isInt_loop: 0.1842
isInt_Digit: 0.1577
menggunakan normal 2,7 python:
$ python --version
Python 2.7.10
Kedua dua test case yang saya tambahkan (isInt_loop dan isInt_digit) lulus test case yang sama persis (mereka berdua hanya menerima bilangan bulat yang tidak ditandatangani), tetapi saya pikir orang bisa lebih pandai dengan memodifikasi implementasi string (isInt_loop) yang bertentangan dengan isdigit built in () berfungsi, jadi saya memasukkannya, meskipun ada sedikit perbedaan dalam waktu eksekusi. (dan kedua metode ini mengalahkan semuanya, tetapi jangan menangani hal-hal tambahan: "./+/-")
Juga, saya menemukan itu menarik untuk dicatat bahwa regex (metode isInt_re2) mengalahkan perbandingan string dalam tes yang sama yang dilakukan oleh Shavais pada 2012 (saat ini 2018). Mungkin perpustakaan regex telah diperbaiki?
Ini mungkin cara yang paling mudah dan pythonic untuk mendekatinya menurut saya. Saya tidak melihat solusi ini dan pada dasarnya sama dengan regex, tetapi tanpa regex.
def is_int(test):
import string
return not (set(test) - set(string.digits))
set(input_string) == set(string.digits)
jika kita lewati '-+ '
di awal dan .0
, E-1
di akhir.
Berikut adalah fungsi yang mem-parsing tanpa memunculkan kesalahan. Ini menangani kasus yang jelas pengembalian None
kegagalan (menangani tanda-tanda 2000 '- / +' secara default di CPython!):
#!/usr/bin/env python
def get_int(number):
splits = number.split('.')
if len(splits) > 2:
# too many splits
return None
if len(splits) == 2 and splits[1]:
# handle decimal part recursively :-)
if get_int(splits[1]) != 0:
return None
int_part = splits[0].lstrip("+")
if int_part.startswith('-'):
# handle minus sign recursively :-)
return get_int(int_part[1:]) * -1
# successful 'and' returns last truth-y value (cast is always valid)
return int_part.isdigit() and int(int_part)
Beberapa tes:
tests = ["0", "0.0", "0.1", "1", "1.1", "1.0", "-1", "-1.1", "-1.0", "-0", "--0", "---3", '.3', '--3.', "+13", "+-1.00", "--+123", "-0.000"]
for t in tests:
print "get_int(%s) = %s" % (t, get_int(str(t)))
Hasil:
get_int(0) = 0
get_int(0.0) = 0
get_int(0.1) = None
get_int(1) = 1
get_int(1.1) = None
get_int(1.0) = 1
get_int(-1) = -1
get_int(-1.1) = None
get_int(-1.0) = -1
get_int(-0) = 0
get_int(--0) = 0
get_int(---3) = -3
get_int(.3) = None
get_int(--3.) = 3
get_int(+13) = 13
get_int(+-1.00) = -1
get_int(--+123) = 123
get_int(-0.000) = 0
Untuk kebutuhan Anda, Anda dapat menggunakan:
def int_predicate(number):
return get_int(number) is not None
Saya menyarankan yang berikut ini:
import ast
def is_int(s):
return isinstance(ast.literal_eval(s), int)
Dari dokumen :
Mengevaluasi simpul ekspresi atau string yang berisi tampilan Python literal atau wadah dengan aman. String atau simpul yang disediakan hanya dapat terdiri dari struktur literal Python berikut: string, byte, angka, tupel, daftar, dikte, set, boolean, dan Tidak ada.
Saya harus mencatat bahwa ini akan menimbulkan ValueError
pengecualian ketika dipanggil terhadap apa pun yang bukan merupakan Python literal. Karena pertanyaannya meminta solusi tanpa mencoba / kecuali, saya punya solusi tipe Kobayashi-Maru untuk itu:
from ast import literal_eval
from contextlib import suppress
def is_int(s):
with suppress(ValueError):
return isinstance(literal_eval(s), int)
return False
¯ \ _ (ツ) _ / ¯
Saya kira pertanyaannya terkait dengan kecepatan karena percobaan / kecuali memiliki penalti waktu:
Pertama, saya membuat daftar 200 string, 100 string gagal dan 100 string numerik.
from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)
np.core.defchararray.isnumeric juga dapat bekerja dengan string unicode np.core.defchararray.isnumeric(u'+12')
tetapi ia kembali dan array. Jadi, ini solusi yang bagus jika Anda harus melakukan ribuan konversi dan kehilangan data atau data non numerik.
import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop
def check_num(s):
try:
int(s)
return True
except:
return False
def check_list(l):
return [check_num(e) for e in l]
%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop
Tampaknya solusi numpy jauh lebih cepat.
Jika Anda hanya ingin menerima digit ascii rendah, berikut ini beberapa tes untuk melakukannya:
Python 3.7+: (u.isdecimal() and u.isascii())
Python <= 3.6: (u.isdecimal() and u == str(int(u)))
Jawaban lain menyarankan menggunakan .isdigit()
atau .isdecimal()
tetapi keduanya termasuk beberapa karakter kode-atas seperti '٢'
( u'\u0662'
):
u = u'\u0662' # '٢'
u.isdigit() # True
u.isdecimal() # True
u.isascii() # False (Python 3.7+ only)
u == str(int(u)) # False
int()
.
Uh .. Coba ini:
def int_check(a):
if int(a) == a:
return True
else:
return False
Ini berfungsi jika Anda tidak meletakkan string yang bukan angka.
Dan juga (saya lupa meletakkan nomor cek bagian.), Ada fungsi memeriksa apakah string adalah angka atau tidak. Ini str.isdigit (). Ini sebuah contoh:
a = 2
a.isdigit()
Jika Anda memanggil a.isdigit (), itu akan mengembalikan True.
2
diberikan a
.