2009-11-10 12 views
36

में पढ़ने पर # कॉमेंट लाइनों को अनदेखा कैसे करें पायथन में, मैंने केवल एक पंक्ति फ़ाइल को एक टेक्स्ट फ़ाइल पढ़ी है और मैं जानना चाहता हूं कि हैश # के साथ टिप्पणियों को अनदेखा करने के लिए कोड कैसे करें रेखा।पायथन: फ़ाइल

मुझे लगता है कि यह कुछ इस तरह होना चाहिए:

for 
    if line !contain # 
     then ...process line 
    else end for loop 

लेकिन मैं अजगर करने के लिए नया हूँ और मैं वाक्य रचना

उत्तर

46

नहीं जानते कि तुम startswith()

जैसे

उपयोग कर सकते हैं
for line in open("file"): 
    li=line.strip() 
    if not li.startswith("#"): 
     print line.rstrip() 
+0

हां। नोटिस के लिए धन्यवाद। – ghostdog74

+4

... अग्रणी व्हाइटस्पेस को अनदेखा करते समय: 'if not line.strip()। Startwith ("#") ' – exhuma

+10

आपके कोड में' खुले फ़ाइल" के लिए लाइन है: 'जो एक खुली फ़ाइल हैंडल छोड़ देता है। आपको 'ओपन ("फाइल") का रिटर्न वैल्यू रखना चाहिए और जब आप पूरा कर लें तो स्पष्ट रूप से' क्लोज़() 'पर कॉल करें, या' with' स्टेटमेंट 'का उपयोग करें (http://docs.python.org/ देखें पुस्तकालय/stdtypes.html # file.close)। –

38

मुझे सलाह है कि आपदेखते समय पूरी लाइन को अनदेखा न करेंचरित्र; बस बाकी रेखा को अनदेखा करें। विभाजन स्ट्रिंग, विभाजन स्ट्रिंग, और विभाजन स्ट्रिंग के बाद सब कुछ पहले सब कुछ: आप partition नामक एक स्ट्रिंग विधि समारोह के साथ आसानी से ऐसा कर सकते हैं:

with open("filename") as f: 
    for line in f: 
     line = line.partition('#')[0] 
     line = line.rstrip() 
     # ... do something with line ... 

partition एक टपल देता है। तो, [0] के साथ अनुक्रमण करके हम विभाजन स्ट्रिंग से पहले भाग लेते हैं।

संपादित करें: आप, यहाँ कि partition() नहीं है अजगर का एक संस्करण का उपयोग कर रहे हैं, तो आप इस्तेमाल कर सकते हैं कोड है:

with open("filename") as f: 
    for line in f: 
     line = line.split('#', 1)[0] 
     line = line.rstrip() 
     # ... do something with line ... 

यह एक '#' चरित्र पर स्ट्रिंग विभाजन है, तो सब कुछ रहता है विभाजन से पहले। 1 तर्क एक विभाजन के बाद .split() विधि स्टॉप बनाता है; चूंकि हम 0 वें सबस्ट्रिंग को पकड़ रहे हैं ([0] के साथ अनुक्रमणित करके) आपको 1 तर्क के बिना एक ही जवाब मिल जाएगा, लेकिन यह थोड़ा तेज़ हो सकता है। (मेरे मूल कोड से सरलीकृत @gnr से एक टिप्पणी के लिए धन्यवाद। मेरा मूल कोड कोई अच्छा कारण नहीं था; धन्यवाद, @gnr।)

आप अपने partition() का अपना संस्करण भी लिख सकते हैं। यहाँ एक part() कहा जाता है:

def part(s, s_part): 
    i0 = s.find(s_part) 
    i1 = i0 + len(s_part) 
    return (s[:i0], s[i0:i1], s[i1:]) 

@dalle ने कहा कि '#' एक स्ट्रिंग के अंदर दिखाई दे सकता है। इस मामले को सही तरीके से संभालना इतना आसान नहीं है, इसलिए मैंने इसे अनदेखा किया, लेकिन मुझे कुछ कहना चाहिए था।

यदि आपकी इनपुट फ़ाइल में उद्धृत तारों के लिए पर्याप्त नियम हैं, तो यह मुश्किल नहीं है। यदि आप किसी भी कानूनी पायथन उद्धृत स्ट्रिंग को स्वीकार करते हैं तो यह कठिन होगा, क्योंकि एकल-उद्धृत, डबल-उद्धृत, मल्टीलाइन उद्धरण हैं जो बैकस्लैश के अंत में लाइन से बचते हैं, ट्रिपल उद्धृत तार (या तो सिंगल या डबल कोट्स का उपयोग करके), और कच्चे तार भी! एक जटिल राज्य मशीन होगी जो सही ढंग से सभी को संभालने का एकमात्र संभावित तरीका है।

लेकिन अगर हम खुद को केवल एक साधारण उद्धृत स्ट्रिंग तक सीमित करते हैं, तो हम इसे एक साधारण राज्य मशीन से संभाल सकते हैं। हम स्ट्रिंग के अंदर बैकस्लैश-उद्धृत डबल कोट भी अनुमति दे सकते हैं।

c_backslash = '\\' 
c_dquote = '"' 
c_comment = '#' 


def chop_comment(line): 
    # a little state machine with two state varaibles: 
    in_quote = False # whether we are in a quoted string right now 
    backslash_escape = False # true if we just saw a backslash 

    for i, ch in enumerate(line): 
     if not in_quote and ch == c_comment: 
      # not in a quote, saw a '#', it's a comment. Chop it and return! 
      return line[:i] 
     elif backslash_escape: 
      # we must have just seen a backslash; reset that flag and continue 
      backslash_escape = False 
     elif in_quote and ch == c_backslash: 
      # we are in a quote and we see a backslash; escape next char 
      backslash_escape = True 
     elif ch == c_dquote: 
      in_quote = not in_quote 

    return line 

मैं वास्तव में इस एक सवाल में चिह्नित "शुरुआत" में जटिल हो नहीं करना चाहता था, लेकिन इस राज्य मशीन यथोचित आसान है, और मुझे आशा है कि यह दिलचस्प होगा।एक फ़िल्टर अभिव्यक्ति की

+1

सच है, लेकिन फिर आपको सहीता के लिए बाहर निकलने पर शायद # उद्धृत करने की भी आवश्यकता है। – dalle

+1

ओपी के लिए लेने के लिए एक छोटा सा नोट यह है कि विभाजन पुराने संस्करण में उपलब्ध नहीं है। – ghostdog74

+0

ओह हेक, यह सही है: 'विभाजन() 'केवल पायथन 2.5 और नए में है। मैं अपना जवाब संपादित करूंगा और एक और समाधान जोड़ूंगा। – steveha

3

एक अधिक कॉम्पैक्ट संस्करण भी इस तरह दिख सकता:

for line in (l for l in open(filename) if not l.startswith('#')): 
    # do something with line 

(l for ...) "जनरेटर अभिव्यक्ति" जो एक रैपिंग इटरेटर कि फ़ाइल से सभी अनावश्यक लाइनों को फ़िल्टर कर देगा के रूप में यहाँ काम करता है, जबकि पुनरावृत्ति कहा जाता है इस पर। स्क्वायर ब्रैकेट्स [l for ... ] में एक ही चीज़ के साथ भ्रमित न करें जो एक "सूची समझ" है जो पहले फ़ाइल से सभी पंक्तियों को स्मृति में पढ़ेगी और केवल तभी इसे फिर से शुरू कर देगा।

lines = open(filename) 
lines = (l for l in lines if ...) 
# more filters and mappings you might want 
for line in lines: 
    # do something with line 

सभी फिल्टर एक चरण में मक्खी पर निष्पादित किया जाएगा:

कभी-कभी आप यह कम एक liney और अधिक पठनीय है करने के लिए चाहते हो सकता है।

5

यह कम से कम रूप है:

for line in open(filename): 
    if line.startswith('#'): 
    continue 
    # PROCESS LINE HERE 

एक स्ट्रिंग पर startswith() विधि TRUE देता स्ट्रिंग आप स्ट्रिंग आपके द्वारा व्यतीत साथ शुरू होता है पर इसे कहते

हालांकि इस में ठीक है। शैल स्क्रिप्ट जैसे कुछ परिस्थितियों में, इसमें दो समस्याएं हैं। सबसे पहले, यह निर्दिष्ट नहीं करता है कि फ़ाइल को कैसे खोलें। फ़ाइल खोलने के लिए डिफ़ॉल्ट मोड 'r' है, जिसका अर्थ है 'बाइनरी मोड में फ़ाइल को पढ़ें'। चूंकि आप एक टेक्स्ट फ़ाइल की उम्मीद कर रहे हैं, इसलिए इसे 'rt' के साथ खोलना बेहतर है। यद्यपि यह भेद यूनिक्स जैसे ऑपरेटिंग सिस्टम पर अप्रासंगिक है, यह विंडोज (और प्री-ओएस एक्स मैक पर) पर महत्वपूर्ण है।

दूसरी समस्या खुली फ़ाइल हैंडल है। open() फ़ंक्शन एक फ़ाइल ऑब्जेक्ट देता है, और जब आप उनके साथ काम करते हैं तो फ़ाइलों को बंद करने के लिए यह अच्छा अभ्यास माना जाता है। ऐसा करने के लिए, ऑब्जेक्ट पर close() विधि को कॉल करें। अब, पाइथन शायद आपके लिए यह कर देगा, अंततः; पाइथन ऑब्जेक्ट्स में संदर्भ-गिना जाता है, और जब किसी ऑब्जेक्ट की संदर्भ संख्या शून्य हो जाती है तो यह मुक्त हो जाती है, और किसी ऑब्जेक्ट को मुक्त करने के बाद कुछ बिंदु पर पाइथन अपने विनाशक को कॉल करेगा (__del__ नामक एक विशेष विधि)। ध्यान दें कि मैंने कहा है कि शायद: पायथन की वास्तव में उन वस्तुओं पर विनाशक को बुलाए जाने की बुरी आदत नहीं है जिनकी संदर्भ संख्या प्रोग्राम समाप्त होने से कुछ ही समय पहले शून्य हो जाती है। मुझे लगता है कि यह जल्दी में है!

शैल स्क्रिप्ट जैसे अल्पकालिक कार्यक्रमों और विशेष रूप से फ़ाइल ऑब्जेक्ट्स के लिए, इससे कोई फर्क नहीं पड़ता। प्रोग्राम समाप्त होने पर आपका ऑपरेटिंग सिस्टम स्वचालित रूप से खोले गए किसी भी फ़ाइल हैंडल को साफ़ कर देगा। लेकिन अगर आपने फ़ाइल खोला है, तो सामग्री को पढ़ें, फिर पहले फ़ाइल संभाल को स्पष्ट रूप से बंद किए बिना एक लंबी गणना शुरू की, पाइथन आपके गणना के दौरान फ़ाइल हैंडल को खोलने की संभावना है। और यह बुरा अभ्यास है।

इस संस्करण में अजगर के किसी भी 2.x संस्करण में काम करेंगे, और सुधारों दोनों समस्याओं मैं ऊपर चर्चा:

f = open(file, 'rt') 
for line in f: 
    if line.startswith('#'): 
    continue 
    # PROCESS LINE HERE 
f.close() 

इस अजगर के पुराने संस्करणों के लिए सबसे अच्छा सामान्य रूप है।

स्टीव द्वारा सुझाए गए अनुसार, "साथ" कथन का उपयोग करके अब सर्वोत्तम अभ्यास माना जाता है। यदि आप 2 का उपयोग कर रहे हैं।6 या इससे ऊपर आपको इसे इस तरह लिखना चाहिए:

with open(filename, 'rt') as f: 
    for line in f: 
    if line.startswith('#'): 
     continue 
    # PROCESS LINE HERE 

"साथ" कथन आपके लिए फ़ाइल हैंडल साफ़ कर देगा।

आपके प्रश्न में आपने कहा "लाइनें जो # से शुरू होती हैं", इसलिए मैंने आपको यहां दिखाया है। यदि आप वैकल्पिक व्हाइटस्पेस और से एक '#' से शुरू होने वाली लाइनों को फ़िल्टर करना चाहते हैं, तो आपको '#' की तलाश करने से पहले व्हाइटस्पेस को पट्टी करना चाहिए। उस मामले में, आप इस बदलना चाहिए:

if line.startswith('#'): 
इस के लिए

:

if line.lstrip().startswith('#'): 

अजगर में, तार अपरिवर्तनीय हैं, तो यह line का मूल्य नहीं बदलता है। lstrip() विधि स्ट्रिंग की एक प्रति अपने सभी प्रमुख व्हाइटस्पेस को हटा देती है।

+0

"पाइथन की वास्तव में उन वस्तुओं पर विनाशक को बुलाए जाने की बुरी आदत नहीं है, जिनकी संदर्भ संख्या प्रोग्राम समाप्त होने से कुछ ही समय पहले शून्य हो जाती है।" क्या आपके पास इस दावे का सबूत है? – gotgenes

+0

"यह गारंटी नहीं है कि __del __() विधियों को ऑब्जेक्ट्स के लिए बुलाया जाता है जो दुभाषिया से बाहर निकलने पर मौजूद होते हैं।" __del __(): पर अनुच्छेद की अंतिम वाक्य http://docs.python.org/reference/datamodel.html#object.__del__ यह 2.6 के लिए प्रलेखन है; यह 3 के लिए सच है।1. मुझे लगता है कि मैंने जो लिखा वह पूरी तरह सटीक नहीं था। सटीक तथ्यों को मैं उस बिंदु के लिए प्रासंगिक हूं जो मैं बना रहा था। सुनिश्चित नहीं है कि मेरे जवाब को सही करने के लिए संपादन योग्य है या नहीं। –

-1

मैं

for line in lines: 
    if '#' not in line: 
     #do something 

यह उपयोग करने के लिए हालांकि इस सवाल का जवाब जो rpartition शामिल मेरी वोट दें है, पूरी लाइन पर ध्यान नहीं देगा के रूप में यह

5

मैं हाल ही में पाया है # से पहले से कोई जानकारी शामिल कर सकते हैं करते हैं कि जनरेटर फ़ंक्शन इस का एक अच्छा काम करता है। मैं समान कार्य का उपयोग किया है टिप्पणी लाइनों, रिक्त लाइनों, आदि को छोड़

मैं परिभाषित मेरी समारोह के रूप में

def skip_comments(file): 
    for line in file: 
     if not line.strip().startswith('#'): 
      yield line 

इस तरह से, मैं सिर्फ

f = open('testfile') 
for line in skip_comments(f): 
    print line 

क्या कर सकते हैं यह भर में पुन: प्रयोज्य मेरा पूरा कोड, और मैं कोई अतिरिक्त हैंडलिंग/लॉगिंग/आदि जोड़ सकता हूं। जिसकी मुझे आवश्यकता है।

6

मैं इस देर से आ रहा हूं, लेकिन शैल शैली (या पायथन शैली) को संभालने की समस्या # टिप्पणियां एक बहुत आम है।

मैं लगभग हर बार जब मैं एक टेक्स्ट फ़ाइल पढ़ता हूं तो कुछ कोड का उपयोग कर रहा हूं।
समस्या यह है कि यह उद्धृत या बच निकले टिप्पणियों को ठीक से संभाल नहीं करता। लेकिन यह सरल मामलों के लिए काम करता है और आसान है।

import shlex 
for line in instream: 
    lex = shlex.shlex(line) 
    lex.whitespace = '' # if you want to strip newlines, use '\n' 
    line = ''.join(list(lex)) 
    if not line: 
     continue 
    # process decommented line 

यह shlex दृष्टिकोण न केवल उद्धरण संभालती है और ठीक से निकल जाता है, यह शांत कार्यक्षमता का एक बहुत (क्षमता की तरह कहते हैं अन्य फ़ाइलों स्रोत के लिए:

for line in whatever: 
    line = line.split('#',1)[0].strip() 
    if not line: 
     continue 
    # process line 

एक और अधिक मजबूत समाधान shlex उपयोग करने के लिए है यदि आप चाहते हैं तो फाइलें)। मैंने बड़ी फ़ाइलों पर गति के लिए इसका परीक्षण नहीं किया है, लेकिन यह छोटी चीजों के लिए पर्याप्त है।

आम मामला है जब आप भी फ़ील्ड में सभी इनपुट लाइन (सफेद स्थान पर) बंटवारे रहे हैं भी सरल है:

import shlex 
for line in instream: 
    fields = shlex.split(line, comments=True) 
    if not fields: 
     continue 
    # process list of fields 
+0

यह एक गुच्छा अधिक upvotes के लायक है! यहां तक ​​कि 'श्लेक्स' के बिना समाधान स्वीकार किए गए उत्तर से अधिक व्यापक है (जो कि सरल उपयोग के मामलों के लिए ठीक है, लेकिन परेशानी यह है कि यदि आप उपयोगकर्ताओं को बताते हैं कि "आप वहां टिप्पणियां डाल सकते हैं, लेकिन केवल तभी जब वे लाइन शुरू करते हैं", तो आप ' उस प्रतिबंध को भूलने वाले पहले व्यक्ति होंगे)। – dlukes

2

मुझे पता है कि यह एक पुराने धागा है, लेकिन यह एक जनरेटर समारोह है कि मैं अपने उद्देश्यों के लिए उपयोग करें।यह टिप्पणियों को स्ट्रिप्स करता है इससे कोई फर्क नहीं पड़ता कि वे लाइन में दिखाई देते हैं, साथ ही साथ अग्रणी/पिछला सफेद स्थान और रिक्त रेखाएं अलग-अलग होते हैं। निम्नलिखित स्रोत पाठ:

# Comment line 1 
# Comment line 2 

# host01 # This host commented out. 
host02 # This host not commented out. 
host03 
    host04 # Oops! Included leading whitespace in error! 

निकलेगा:

host02 
host03 
host04 

यहाँ कोड दर्ज है, जो एक डेमो में शामिल हैं:

def strip_comments(item, *, token='#'): 
    """Generator. Strips comments and whitespace from input lines. 

    This generator strips comments, leading/trailing whitespace, and 
    blank lines from its input. 

    Arguments: 
     item (obj): Object to strip comments from. 
     token (str, optional): Comment delimiter. Defaults to ``#``. 

    Yields: 
     str: Next non-blank line from ``item`` with comments and 
      leading/trailing whitespace removed. 

    """ 

    for line in item: 
     s = line.split(token, 1)[0].strip() 
     if s != '': 
      yield s 


if __name__ == '__main__': 
    HOSTS = ['# Comment line 1', 
      '# Comment line 2', 
      '', 
      '# host01 # This host commented out.', 
      'host02 # This host not commented out.', 
      'host03', 
      ' host04 # Oops! Included leading whitespace in error!',] 

    hosts = strip_comments(HOSTS) 
    for host in hosts: 
     print('\'%s\'' % host) 

सामान्य उपयोग के मामले से टिप्पणी पट्टी हो जाएगा एक फ़ाइल (यानी, एक होस्ट फ़ाइल, जैसा ऊपर मेरे उदाहरण में है)। नई लाइनों और टिप्पणियों को छोड़

if __name__ == '__main__': 
    with open('hosts.txt', 'r') as f: 
     hosts = strip_comments(f) 

    for host in hosts: 
     print('\'%s\'' % host) 
1

उपयोग regex re.compile("^(?:\s+)*#|(?:\s+)"): यदि यह मामला है, तो इसके बाद के संस्करण कोड 'के अंत करने के लिए संशोधित किया जाएगा।