6

我正在尝试在python中编写一个函数,它将确定字符串中的值类型;例如

如果字符串中为 1 或 0 或 True 或 False,则值为 BIT

如果字符串中为 0-9*,则值为 INT

如果字符串中为 0-9+.0-9+,则值为浮点数

如果字符串中的更多(文本等)值是文本

到目前为止,我喜欢

def dataType(string):

 odp=''
 patternBIT=re.compile('[01]')
 patternINT=re.compile('[0-9]+')
 patternFLOAT=re.compile('[0-9]+\.[0-9]+')
 patternTEXT=re.compile('[a-zA-Z0-9]+')
 if patternTEXT.match(string):
     odp= "text"
 if patternFLOAT.match(string):
     odp= "FLOAT"
 if patternINT.match(string):
     odp= "INT"
 if patternBIT.match(string):
     odp= "BIT"

 return odp 

但我不太擅长在 python 中使用正则表达式。你能告诉我,我做错了什么吗?例如,它不适用于 2010-00-10,它应该是 Text,但应该是 INT 或 20.90,它应该是 float 但应该是 int

4

4 回答 4

22

在你走得太远的正则表达式路线之前,你有没有考虑过使用ast.literal_eval

例子:

In [35]: ast.literal_eval('1')
Out[35]: 1

In [36]: type(ast.literal_eval('1'))
Out[36]: int

In [38]: type(ast.literal_eval('1.0'))
Out[38]: float

In [40]: type(ast.literal_eval('[1,2,3]'))
Out[40]: list

不妨用 Python 为你解析!

好的,这是一个更大的例子:

import ast, re
def dataType(str):
    str=str.strip()
    if len(str) == 0: return 'BLANK'
    try:
        t=ast.literal_eval(str)

    except ValueError:
        return 'TEXT'
    except SyntaxError:
        return 'TEXT'

    else:
        if type(t) in [int, long, float, bool]:
            if t in set((True,False)):
                return 'BIT'
            if type(t) is int or type(t) is long:
                return 'INT'
            if type(t) is float:
                return 'FLOAT'
        else:
            return 'TEXT' 



testSet=['   1  ', ' 0 ', 'True', 'False',   #should all be BIT
         '12', '34l', '-3','03',              #should all be INT
         '1.2', '-20.4', '1e66', '35.','-   .2','-.2e6',      #should all be FLOAT
         '10-1', 'def', '10,2', '[1,2]','35.9.6','35..','.']

for t in testSet:
    print "{:10}:{}".format(t,dataType(t))

输出:

   1      :BIT
 0        :BIT
True      :BIT
False     :BIT
12        :INT
34l       :INT
-3        :INT
03        :INT
1.2       :FLOAT
-20.4     :FLOAT
1e66      :FLOAT
35.       :FLOAT
-   .2    :FLOAT
-.2e6     :FLOAT
10-1      :TEXT
def       :TEXT
10,2      :TEXT
[1,2]     :TEXT
35.9.6    :TEXT
35..      :TEXT
.         :TEXT

如果你肯定必须有一个正则表达式解决方案,它会产生相同的结果,这里是:

def regDataType(str):
    str=str.strip()
    if len(str) == 0: return 'BLANK'

    if re.match(r'True$|^False$|^0$|^1$', str):
        return 'BIT'
    if re.match(r'([-+]\s*)?\d+[lL]?$', str): 
        return 'INT'
    if re.match(r'([-+]\s*)?[1-9][0-9]*\.?[0-9]*([Ee][+-]?[0-9]+)?$', str): 
        return 'FLOAT'
    if re.match(r'([-+]\s*)?[0-9]*\.?[0-9][0-9]*([Ee][+-]?[0-9]+)?$', str): 
        return 'FLOAT'

    return 'TEXT' 

但是,我不能推荐正则表达式而不是 ast 版本;只需让 Python 解释它认为这些数据类型是什么,而不是用正则表达式来解释它们......

于 2012-04-21T17:15:12.610 回答
5

你也可以使用 json。

import json
converted_val = json.loads('32.45')
type(converted_val)

输出

type <'float'>

编辑

但是,要回答您的问题:

re.match()返回部分匹配,从字符串的开头开始。由于您不断评估每个模式匹配,因此“2010-00-10”的序列如下所示:

if patternTEXT.match(str_obj): #don't use 'string' as a variable name.

它匹配,因此odp设置为“文本”

然后,您的脚本会:

if patternFLOAT.match(str_obj):

不匹配,odp仍然等于“文本”

if patternINT.match(str_obj):

部分匹配odp设置为“INT”

因为 match 返回部分匹配,if所以会评估多个语句,最后一个评估的语句确定在 中返回哪个字符串odp

您可以做以下几件事之一:

  1. 重新排列 if 语句的顺序,以便最后一个匹配的是正确的。

  2. 对其余语句使用ifand ,以便仅评估要匹配的第一个语句。elifif

  3. 检查以确保匹配对象匹配整个字符串:

    ...
    match = patternINT.match(str_obj)
    if match:
        if match.end() == match.endpos:
            #do stuff
    ...
    
于 2012-04-21T17:17:04.590 回答
2

您说您将这些用于输入:

  • 2010-00-10(是 int,不是文本)
  • 20.90(是整数,不是浮点数)

您的原始代码:

def dataType(string):

 odp=''
 patternBIT=re.compile('[01]')
 patternINT=re.compile('[0-9]+')
 patternFLOAT=re.compile('[0-9]+\.[0-9]+')
 patternTEXT=re.compile('[a-zA-Z0-9]+')
 if patternTEXT.match(string):
     odp= "text"
 if patternFLOAT.match(string):
     odp= "FLOAT"
 if patternINT.match(string):
     odp= "INT"
 if patternBIT.match(string):
     odp= "BIT"

 return odp 

问题

您的if语句将按顺序执行 - 即:

if patternTEXT.match(string):
    odp= "text"
if patternFLOAT.match(string):
    odp= "FLOAT"
if patternINT.match(string)
    odp= "INT"
if patternBIT.match(string):
    odp= "BIT"

“2010-00-10” 匹配您的文本模式,但随后它将尝试匹配您的浮点模式(失败,因为没有.),然后匹配int模式,因为它确实包含[0-9]+.

你应该使用:

if patternTEXT.match(string):
    odp = "text"
elif patternFLOAT.match(string):
    ...

尽管对于您的情况,您可能希望更具体到不太具体,因为如您所见,文本内容也可能是 int (反之亦然)。您还需要改进正则表达式,因为您的“文本”模式仅匹配字母数字输入,但不匹配特殊符号。

我会提供我自己的建议,尽管我更喜欢 AST 解决方案:

def get_type(string):

    if len(string) == 1 and string in ['0', '1']:
        return "BIT"

    # int has to come before float, because integers can be
    # floats.
    try:
        long(string)
        return "INT"
    except ValueError, ve:
        pass

    try:
        float(string)
        return "FLOAT"
    except ValueError, ve:
        pass

    return "TEXT"

运行示例:

In [27]: get_type("034")
Out[27]: 'INT'

In [28]: get_type("3-4")
Out[28]: 'TEXT'


In [29]: get_type("20.90")
Out[29]: 'FLOAT'

In [30]: get_type("u09pweur909ru20")
Out[30]: 'TEXT'
于 2012-04-21T17:48:03.887 回答
1

作为回复

例如,它不适用于 2010-00-10,它应该是 Text,但应该是 INT 或 20.90,它应该是 float 但应该是 int

>>> import re
>>> patternINT=re.compile('[0-9]+')
>>> print patternINT.match('2010-00-10')
<_sre.SRE_Match object at 0x7fa17bc69850>
>>> patternINT=re.compile('[0-9]+$')
>>> print patternINT.match('2010-00-10')
None
>>> print patternINT.match('2010')
<_sre.SRE_Match object at 0x7fa17bc69850>

不要忘记$限制字符串的结尾。

于 2012-04-21T18:13:04.813 回答