2012-08-07 9 views
25

में चर के लिए स्ट्रिंग (जैसे प्रारूप(), लेकिन विपरीत में) कनवर्ट करें, या अनफॉर्मेट करें, मेरे पास Version 1.4.0\n और Version 1.15.6\n फ़ॉर्म के तार हैं, और मैं उनसे तीन संख्याओं को निकालने का एक आसान तरीका चाहता हूं। मुझे पता है कि मैं प्रारूप विधि के साथ एक स्ट्रिंग में चर डाल सकते हैं; (कैसे format() करने के लिए Use Python format string in reverse for parsingपाइथन

एक सामान्य जवाब: मैं मूल रूप से ऐसा करने के लिए पीछे की ओर, इस तरह हैं: किसी और

# So I know I can do this: 
x, y, z = 1, 4, 0 
print 'Version {0}.{1}.{2}\n'.format(x,y,z) 
# Output is 'Version 1.4.0\n' 

# But I'd like to be able to reverse it: 

mystr='Version 1.15.6\n' 
a, b, c = mystr.unformat('Version {0}.{1}.{2}\n') 

# And have the result that a, b, c = 1, 15, 6 

किसी मैंने पाया वही सवाल पूछा, लेकिन जबाब अपने विशेष मामले के लिए विशिष्ट था विपरीत में) बहुत अच्छा होगा! हालांकि मेरे विशिष्ट मामले का उत्तर बहुत उपयोगी होगा।

+3

मुझे नीचे कुछ जवाब दिखाई देते हैं जो आपकी समस्या के लिए सीधे हैं। लेकिन एक बेहतर समाधान नियमित अभिव्यक्ति imho का उपयोग करना होगा। –

+0

यह [स्कैनफ़] के लिए एक अच्छा उपयोग प्रतीत होता है (http://code.activestate.com/recipes/502213-simple-scanf-implementation/) सी-शैली – Gaius

उत्तर

0

दरअसल पाइथन नियमित अभिव्यक्ति लाइब्रेरी पहले से ही सामान्य कार्यक्षमता प्रदान करती है जिसे आप पूछ रहे हैं। तुम बस थोड़ा

>>> import re 
>>> from operator import itemgetter 
>>> mystr='Version 1.15.6\n' 
>>> m = re.match('Version (?P<_0>.+)\.(?P<_1>.+)\.(?P<_2>.+)', mystr) 
>>> map(itemgetter(1), sorted(m.groupdict().items())) 
['1', '15', '6'] 

आप देख सकते हैं, तो आप से {0} (+? पी < _0>।) से (संयुक्त राष्ट्र) स्वरूप तार बदलने के लिए पैटर्न की वाक्य रचना बदलना होगा। आपको दशमलव के साथ भी आवश्यकता हो सकती है (? पी < _0> \ d +)। इसके अलावा, आपको रेगेक्स विशेष पात्रों के रूप में व्याख्या करने से रोकने के लिए कुछ पात्रों से बचना होगा। लेकिन इसे टर्म में फिर से स्वचालित किया जा सकता है उदा।

>>> re.sub(r'\\{(\d+)\\}', r'(?P<_\1>.+)', re.escape('Version {0}.{1}.{2}')) 
'Version\\ (?P<_0>.+)\\.(?P<_1>.+)\\.(?P<_2>.+)' 
3

यह

a, b, c = (int(i) for i in mystr.split()[1].split('.')) 

आप a, b और c

>>> a 
1 
>>> b 
15 
>>> c 
6 

निर्भर करता है के लिए int मूल्यों दे देंगे कैसे नियमित या अनियमित, यानी, संगत, अपना नंबर/संस्करण प्रारूपों हो जाएगा पर, आप नियमित अभिव्यक्ति के उपयोग पर विचार करना चाहेंगे, भले ही वे इस प्रारूप में रहें, अगर मैं इसे सरल समाधान का पक्ष लेगा आपके लिए काम करता है

+1

+1 जनरेटर अभिव्यक्ति का उपयोग करें, '[] ' । –

+1

@ अश्विनी चौधरी हां, आप सही हैं .. मैंने पहले सूची समझ के बारे में सीखा, इसलिए जहां मैं शुरुआत में जाता हूं, लेकिन आप सही हैं, सूची रखने की कोई आवश्यकता नहीं है - धन्यवाद, मैंने जवाब अपडेट किया। – Levon

+1

इस मामले में जनरेटर का उपयोग करने से कोई मतलब नहीं है, सूची समझ नौकरी ठीक करेगी। – Willian

8
>>> import re 
>>> re.findall('(\d+)\.(\d+)\.(\d+)', 'Version 1.15.6\n') 
[('1', '15', '6')] 
+0

ओह, मेरा मतलब था 'x, y, z = [int (num) परिणामस्वरूप re.findall (' (\ d +) \। (\ D +) \। (\ D +) ',' संस्करण 1.15.6 \ n ') परिणाम में संख्या के लिए] ' –

+0

ए, बी, सी = re.findall (' (\ d +) \। (\ d +) \। (\ d +) ',' संस्करण 1.15.6 \ n ') [ 0] – Willian

+0

यह एक अच्छा परिष्करण है लेकिन यह अभी भी परिणामों को पूर्णांक में परिवर्तित नहीं करता है। मैं अपना उदाहरण संशोधित करता हूं: re.findall ('(\ d +) \। (\ D +) \। (\ D +)', 'संस्करण 1.15.6 \ n में num के लिए' x, y, z = [int (num) ') [0]] ' –

2

कुछ समय पहले मैंने नीचे दिया गया कोड बनाया है जो प्रारूप के विपरीत है लेकिन मुझे आवश्यक मामलों तक ही सीमित है।

और, मैं यह कोशिश कभी नहीं है, लेकिन मुझे लगता है कि यह भी parse library

मेरे कोड का उद्देश्य है:

import string 
import re 

_def_re = '.+' 
_int_re = '[0-9]+' 
_float_re = '[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?' 

_spec_char = '[\^$.|?*+()' 

def format_parse(text, pattern): 
    """ 
    Scan `text` using the string.format-type `pattern` 

    If `text` is not a string but iterable return a list of parsed elements 

    All format-like pattern cannot be process: 
     - variable name cannot repeat (even unspecified ones s.t. '{}_{0}') 
     - alignment is not taken into account 
     - only the following variable types are recognized: 
      'd' look for and returns an integer 
      'f' look for and returns a float 

    Examples:: 

     res = format_parse('the depth is -42.13', 'the {name} is {value:f}') 
     print res 
     print type(res['value']) 
     # {'name': 'depth', 'value': -42.13} 
     # <type 'float'> 

     print 'the {name} is {value:f}'.format(**res) 
     # 'the depth is -42.130000' 

     # Ex2: without given variable name and and invalid item (2nd) 
     versions = ['Version 1.4.0', 'Version 3,1,6', 'Version 0.1.0'] 
     v = format_parse(versions, 'Version {:d}.{:d}.{:d}') 
     # v=[{0: 1, 1: 4, 2: 0}, None, {0: 0, 1: 1, 2: 0}] 

    """ 
    # convert pattern to suitable regular expression & variable name 
    v_int = 0 # available integer variable name for unnamed variable 
    cur_g = 0 # indices of current regexp group name 
    n_map = {} # map variable name (keys) to regexp group name (values) 
    v_cvt = {} # (optional) type conversion function attached to variable name 
    rpattern = '^' # stores to regexp pattern related to format pattern   

    for txt,vname, spec, conv in string.Formatter().parse(pattern): 
     # process variable name 
     if len(vname)==0: 
      vname = v_int 
      v_int += 1 
     if vname not in n_map: 
      gname = '_'+str(cur_g) 
      n_map[vname] = gname 
      cur_g += 1     
     else:  
      gname = n_map[vname] 

     # process type of required variables 
     if 'd' in spec: vtype = _int_re; v_cvt[vname] = int 
     elif 'f' in spec: vtype = _float_re; v_cvt[vname] = float 
     else:    vtype = _def_re; 

     # check for regexp special characters in txt (add '\' before) 
     txt = ''.join(map(lambda c: '\\'+c if c in _spec_char else c, txt)) 

     rpattern += txt + '(?P<'+gname+'>' + vtype +')' 

    rpattern += '$' 

    # replace dictionary key from regexp group-name to the variable-name 
    def map_result(match): 
     if match is None: return None 
     match = match.groupdict() 
     match = dict((vname, match[gname]) for vname,gname in n_map.iteritems()) 
     for vname, value in match.iteritems(): 
      if vname in v_cvt: 
       match[vname] = v_cvt[vname](value) 
     return match 

    # parse pattern 
    if isinstance(text,basestring): 
     match = re.search(rpattern, text) 
     match = map_result(match) 
    else: 
     comp = re.compile(rpattern) 
     match = map(comp.search, text) 
     match = map(map_result, match) 

    return match 
अपने मामले के लिए

, यहाँ एक प्रयोग उदाहरण है:

versions = ['Version 1.4.0', 'Version 3.1.6', 'Version 0.1.0'] 
v = format_parse(versions, 'Version {:d}.{:d}.{:d}') 
# v=[{0: 1, 1: 4, 2: 0}, {0: 3, 1: 1, 2: 6}, {0: 0, 1: 1, 2: 0}] 

# to get the versions as a list of integer list, you can use: 
v = [[vi[i] for i in range(3)] for vi in filter(None,v)] 

अनपेक्षित संस्करणों को हटाने के लिए filter(None,v) पर ध्यान दें (जो कोई भी नहीं लौटाता है)। यहां यह जरूरी नहीं है।

4

बस Uche's answer पर निर्माण करने के लिए, मैं kwargs के साथ एक पैटर्न के माध्यम से एक स्ट्रिंग को रिवर्स करने का एक तरीका ढूंढ रहा था।

def string_to_dict(string, pattern): 
    regex = re.sub(r'{(.+?)}', r'(?P<_\1>.+)', pattern) 
    values = list(re.search(regex, string).groups()) 
    keys = re.findall(r'{(.+?)}', pattern) 
    _dict = dict(zip(keys, values)) 
    return _dict 

कौन सा अनुसार काम करता है:: तो मैं निम्नलिखित समारोह एक साथ रखा

>>> p = 'hello, my name is {name} and I am a {age} year old {what}' 

>>> s = p.format(name='dan', age=33, what='developer') 
>>> s 
'hello, my name is dan and I am a 33 year old developer' 
>>> string_to_dict(s, p) 
{'age': '33', 'name': 'dan', 'what': 'developer'} 

>>> s = p.format(name='cody', age=18, what='quarterback') 
>>> s 
'hello, my name is cody and I am a 18 year old quarterback' 
>>> string_to_dict(s, p) 
{'age': '18', 'name': 'cody', 'what': 'quarterback'} 
2

संपादित करें: इसके अलावा parse के बारे में और parmatter थोड़ा और अधिक जानकारी के लिए this answer देखते हैं।

pip install parse 

इस तरह इस्तेमाल किया जा सकता:

pypi पैकेज parse इस उद्देश्य अच्छी तरह से कार्य करता है

>>> import parse 
>>> result=parse.parse('Version {0}.{1}.{2}\n', 'Version 1.15.6\n') 
<Result ('1', '15', '6') {}> 
>>> values=list(result) 
>>> print(values) 
['1', '15', '6'] 

ध्यान दें कि the docs sayparse पैकेज बिल्कुल डिफ़ॉल्ट रूप से format specification mini-language का अनुकरण नहीं करता है; यह re द्वारा निर्दिष्ट कुछ प्रकार-संकेतकों का भी उपयोग करता है। विशेष नोट यह है कि s का अर्थ डिफ़ॉल्ट रूप से "व्हाइटस्पेस" है, str के बजाय। यह आसानी से str को s के लिए डिफ़ॉल्ट प्रकार बदलने (extra_types का प्रयोग करके) द्वारा प्रारूप विनिर्देश के अनुरूप होना करने के लिए संशोधित किया जा सकता है:

result = parse.parse(format_str, string, extra_types=dict(s=str)) 

यहाँ निर्मित का उपयोग कर वर्ग string.Formatter के एक संशोधन के लिए एक वैचारिक विचार है parse पैकेज unformat क्षमता जोड़ने के लिए है कि मैं अपने आप को इस्तेमाल किया है:

import parse 
from string import Formatter 
class Unformatter(Formatter): 
    '''A parsable formatter.''' 
    def unformat(self, format, string, extra_types=dict(s=str), evaluate_result=True): 
     return parse.parse(format, string, extra_types, evaluate_result) 
    unformat.__doc__ = parse.Parser.parse.__doc__ 

महत्वपूर्ण: विधि नाम parse पहले से ही Formatter वर्ग द्वारा उपयोग में है, तो मैंको चुना है विवाद से बचने के लिए।

अद्यतन: आप इसे इस तरह उपयोग कर सकते हैं- string.Formatter कक्षा के समान ही।

प्रारूपण ('{:d} {:d}'.format(1, 2) के समान):

>>> formatter = Unformatter() 
>>> s = formatter.format('{:d} {:d}', 1, 2) 
>>> s 
'1 2' 

Unformatting:

>>> result = formatter.unformat('{:d} {:d}', s) 
>>> result 
<Result (1, 2) {}> 
>>> tuple(result) 
(1, 2) 

इस रूप में ऊपर दिखाए गए बहुत सीमित उपयोग की निश्चित रूप से है। हालांकि, मैंने एक पीपीआई पैकेज (parmatter - मूल रूप से अपने स्वयं के उपयोग के लिए एक प्रोजेक्ट रखा है लेकिन शायद दूसरों को यह उपयोगी लगेगा) जो इस विचार को और अधिक उपयोगी काम करने के तरीके के बारे में कुछ विचारों की पड़ताल करता है। पैकेज उपरोक्त parse पैकेज पर भारी निर्भर करता है।

+0

मैं इस कक्षा का अंत में वर्णन कैसे करूं? नियमित स्ट्रिंग विधियों के लिए एकदम सही जोड़ की तरह दिखता है। – Harsh

+0

@ हर्ष कुछ मार्गदर्शन के लिए मेरे अद्यतन उत्तर देखें। –

+0

धन्यवाद। यह अब स्पष्ट है। – Harsh