Std हो रही है :: Istream को LF, CR और CRLF को संभालने के लिए?


85

विशेष रूप से मुझे इसमें दिलचस्पी है istream& getline ( istream& is, string& str );। क्या हुड के नीचे सभी न्यूलाइन एन्कोडिंग को परिवर्तित करने के लिए इफस्ट्रीम कंस्ट्रक्टर को बताने का विकल्प है? मैं कॉल करने में सक्षम होना चाहता हूं getlineऔर इसे सभी लाइन एंडिंग को सुशोभित करना चाहता हूं ।

अपडेट : स्पष्ट करने के लिए, मैं कोड लिखना चाहता हूं जो लगभग कहीं भी संकलित करता है, और लगभग कहीं से भी इनपुट लेगा। उन दुर्लभ फ़ाइलों को शामिल किया गया है जिनमें '\ n' बिना '\ n' है। सॉफ्टवेयर के किसी भी उपयोगकर्ता के लिए असुविधा को कम करना।

इस समस्या को हल करना आसान है, लेकिन मैं अभी भी मानक तरीके से, सभी पाठ फ़ाइल स्वरूपों को लचीले ढंग से संभालने के लिए उत्सुक हूं।

getlineएक स्ट्रिंग में एक '\ n' तक, एक पूर्ण पंक्ति में पढ़ता है। '\ N' स्ट्रीम से खपत होती है, लेकिन गेटलाइन इसे स्ट्रिंग में शामिल नहीं करती है। यह अब तक ठीक है, लेकिन स्ट्रिंग में शामिल होने वाले '\ n' के ठीक पहले '\ r' हो सकता है।

कर रहे हैं लाइन अंत के तीन प्रकार पाठ फ़ाइलों में देखी गई: '\ n' यूनिक्स मशीनों, पर '\ r' पारंपरिक अंत है था (मुझे लगता है कि) वर्ष मैक ऑपरेटिंग सिस्टम पर इस्तेमाल किया, और Windows एक जोड़ी, '\ आर' का उपयोग करता है '\ n' द्वारा अनुसरण किया जा रहा है।

समस्या यह है कि getlineस्ट्रिंग के अंत में 'r' छोड़ देता है।

ifstream f("a_text_file_of_unknown_origin");
string line;
getline(f, line);
if(!f.fail()) { // a non-empty line was read
   // BUT, there might be an '\r' at the end now.
}

संपादित करें कि उनका कहना है के लिए नील के लिए धन्यवाद f.good()मैं चाहता था नहीं है। !f.fail()मुझे क्या चाहिए

मैं इसे स्वयं हटा सकता हूं (इस प्रश्न का संपादन देखें), जो कि विंडोज टेक्स्ट फाइलों के लिए आसान है। लेकिन मुझे चिंता है कि कोई फ़ाइल में केवल 'r' युक्त फ़ीड करेगा। उस स्थिति में, मुझे लगता है कि गेटलाइन पूरी फ़ाइल का उपभोग करेगी, यह सोचकर कि यह एक ही लाइन है!

.. और यह भी यूनिकोड :-) पर विचार नहीं कर रहा है

.. शायद बूस्ट के पास किसी भी पाठ-फ़ाइल प्रकार से एक समय में एक पंक्ति का उपभोग करने का एक अच्छा तरीका है?

संपादित करें मैं इस का उपयोग कर रहा हूँ, Windows फ़ाइलों को संभालने के लिए, लेकिन मैं अभी भी मैं नहीं करना चाहिए है! और यह 'केवल' फाइलों के लिए कांटा नहीं होगा।

if(!line.empty() && *line.rbegin() == '\r') {
    line.erase( line.length()-1, 1);
}

2
\ n का अर्थ है कि वर्तमान OS में जो भी प्रस्तुत किया गया है उसमें नई लाइन। पुस्तकालय उसका ख्याल रखता है। लेकिन इसके लिए काम करने के लिए, खिड़कियों में संकलित एक कार्यक्रम को खिड़कियों से पाठ फ़ाइलों को पढ़ना चाहिए, एक कार्यक्रम यूनिक्स में संकलित, यूनिक्स से पाठ फ़ाइलें आदि
जॉर्ज कस्तरीन

1
@George, भले ही मैं एक लिनक्स मशीन पर संकलन कर रहा हूं, कभी-कभी मैं मूल रूप से विंडोज मशीन से आए टेक्स्ट फाइलों का उपयोग कर रहा हूं। मैं अपना सॉफ़्टवेयर (नेटवर्क विश्लेषण के लिए एक छोटा उपकरण) जारी कर सकता हूं, और मैं उपयोगकर्ताओं को यह बताने में सक्षम होना चाहता हूं कि वे लगभग किसी भी समय (एएससीआईआई-जैसे) पाठ फ़ाइल में फ़ीड कर सकते हैं।
हारून मैकडैड


1
ध्यान दें कि यदि (f.good ()) वह नहीं करता है जो आपको लगता है कि यह करता है।

1
@JonathanMee: ऐसा लगता है कि हो सकता है यह । शायद।
ऑर्बिट

जवाबों:


111

जैसा कि नील ने कहा, "सी ++ रनटाइम को आपके विशेष प्लेटफॉर्म के लिए लाइन समाप्त होने वाले सम्मेलन के साथ सही ढंग से निपटना चाहिए।"

हालाँकि, लोग अलग-अलग प्लेटफ़ॉर्म के बीच टेक्स्ट फ़ाइलों को स्थानांतरित करते हैं, इसलिए यह पर्याप्त अच्छा नहीं है। यहां एक फ़ंक्शन है जो सभी तीन पंक्ति अंत ("\ r", "\ n" और "\ r \ n") को संभालता है:)

std::istream& safeGetline(std::istream& is, std::string& t)
{
    t.clear();

    // The characters in the stream are read one-by-one using a std::streambuf.
    // That is faster than reading them one-by-one using the std::istream.
    // Code that uses streambuf this way must be guarded by a sentry object.
    // The sentry object performs various tasks,
    // such as thread synchronization and updating the stream state.

    std::istream::sentry se(is, true);
    std::streambuf* sb = is.rdbuf();

    for(;;) {
        int c = sb->sbumpc();
        switch (c) {
        case '\n':
            return is;
        case '\r':
            if(sb->sgetc() == '\n')
                sb->sbumpc();
            return is;
        case std::streambuf::traits_type::eof():
            // Also handle the case when the last line has no line ending
            if(t.empty())
                is.setstate(std::ios::eofbit);
            return is;
        default:
            t += (char)c;
        }
    }
}

और यहाँ एक परीक्षण कार्यक्रम है:

int main()
{
    std::string path = ...  // insert path to test file here

    std::ifstream ifs(path.c_str());
    if(!ifs) {
        std::cout << "Failed to open the file." << std::endl;
        return EXIT_FAILURE;
    }

    int n = 0;
    std::string t;
    while(!safeGetline(ifs, t).eof())
        ++n;
    std::cout << "The file contains " << n << " lines." << std::endl;
    return EXIT_SUCCESS;
}

1
@Miek: मैंने Bo Persons सुझाव stackoverflow.com/questions/9188126/… के बाद कोड को अपडेट किया है और अपने परीक्षण चलाए हैं। सब कुछ अब उसी तरह काम करता है जैसा उसे करना चाहिए।
जोहान राडे

1
@ थोमस वेलर: संतरी के लिए कंस्ट्रक्टर और डिस्ट्रक्टर को निष्पादित किया जाता है। ये थ्रेड सिंक्रोनाइज़ेशन, वाइट स्पेस को स्किप करने और स्ट्रीम स्टेट को अपडेट करने जैसे काम करते हैं।
जोहान

1
ईओएफ मामले में, tईओफ़बिट सेट करने से पहले खाली होने की जाँच का उद्देश्य क्या है । अन्य पात्रों की परवाह किए बिना उस बिट को सेट नहीं किया जाना चाहिए?
Y29295

1
Yay295: ईओफ़ ध्वज को तब सेट किया जाना चाहिए, जब आप अंतिम पंक्ति के अंत तक नहीं पहुंचते, लेकिन जब आप अंतिम पंक्ति से आगे पढ़ने का प्रयास करते हैं। चेक यह सुनिश्चित करता है कि ऐसा तब होता है जब अंतिम लाइन में कोई ईओएल न हो। (चेक को हटाने का प्रयास करें, और फिर टेक्स्ट फ़ाइल पर परीक्षण कार्यक्रम चलाएं जहां अंतिम पंक्ति में कोई ईओएल नहीं है, और आप देखेंगे।)
जोहान राडे

3
यह एक खाली अंतिम पंक्ति को भी पढ़ता है, जो कि व्यवहार नहीं है, जो std::get_lineएक खाली अंतिम पंक्ति को अनदेखा करता है। मैंने std::get_lineव्यवहार का अनुकरण करने के लिए is.setstate(std::ios::eofbit); if (t.empty()) is.setstate(std::ios::badbit); return is;
ईओफ़

11

C ++ रनटाइम को आपके विशेष प्लेटफॉर्म के लिए एंडलाइन कन्वेंशन जो भी हो, सही तरीके से निपटना चाहिए। विशेष रूप से, यह कोड सभी प्लेटफार्मों पर काम करना चाहिए:

#include <string>
#include <iostream>
using namespace std;

int main() {
    string line;
    while( getline( cin, line ) ) {
        cout << line << endl;
    }
}

बेशक, यदि आप किसी अन्य प्लेटफ़ॉर्म से फ़ाइलों के साथ काम कर रहे हैं, तो सभी दांव बंद हैं।

जैसा कि दो सबसे सामान्य प्लेटफ़ॉर्म (लिनक्स और विंडोज) दोनों एक नई लाइन वर्ण के साथ लाइनों को समाप्त करते हैं, विंडोज के साथ एक गाड़ी वापसी के साथ पहले, आप lineउपरोक्त कोड में स्ट्रिंग के अंतिम चरित्र की जांच कर सकते हैं कि क्या यह है \rऔर यदि ऐसा है तो अपने एप्लिकेशन-विशिष्ट प्रसंस्करण करने से पहले इसे हटा दें।

उदाहरण के लिए, आप खुद को एक गेटलाइन स्टाइल फ़ंक्शन प्रदान कर सकते हैं जो कुछ इस तरह दिखता है (परीक्षण नहीं किया गया है, केवल अनुक्रमिक उपयोग के लिए अनुक्रमित, सामग्री आदि का उपयोग):

ostream & safegetline( ostream & os, string & line ) {
    string myline;
    if ( getline( os, myline ) ) {
       if ( myline.size() && myline[myline.size()-1] == '\r' ) {
           line = myline.substr( 0, myline.size() - 1 );
       }
       else {
           line = myline;
       }
    }
    return os;
}

9
सवाल यह है कि किसी अन्य प्लेटफ़ॉर्म से फ़ाइलों से कैसे निपटें।
को ऑर्बिट में हल्कापन दौड़

4
@ नहीं, यह जवाब अभी तक पर्याप्त नहीं है। अगर मैं सीआरएलएफ को संभालना चाहता हूं, तो मैं स्टैकऑवरफ्लो में नहीं आऊंगा। असली चुनौती उन फाइलों को संभालना है जिनमें केवल '\ r' है। वे आजकल बहुत दुर्लभ हैं, अब जब मैकओएस यूनिक्स के करीब चला गया है, लेकिन मैं यह नहीं मानना ​​चाहता कि वे कभी भी मेरे सॉफ़्टवेयर को खिलाए नहीं जाएंगे।
हारून मैकडैड

1
@ अच्छी तरह से, यदि आप कुछ भी करने में सक्षम होना चाहते हैं तो आपको इसे करने के लिए अपना कोड लिखना होगा।

4
मैंने शुरू से ही अपने प्रश्न में स्पष्ट कर दिया था कि यह आसान है कि मैं यह करने के लिए तैयार हूं और ऐसा करने में सक्षम हूं। मैंने इस बारे में पूछा क्योंकि यह ऐसा सामान्य प्रश्न लगता है, और विभिन्न प्रकार के पाठ-फ़ाइल स्वरूप हैं। मैंने माना / आशा है कि C ++ मानकों की समिति ने इसे बनाया था। यह मेरा प्रश्न था।
हारून मैकडैड

1
@ नहीं, मुझे लगता है कि एक और मुद्दा है जिसे मैं / हम भूल गए हैं। लेकिन पहले, मैं स्वीकार करता हूं कि समर्थित होने के लिए बहुत कम प्रारूपों की पहचान करना मेरे लिए व्यावहारिक है। इसलिए, मैं कोड चाहता हूं जो विंडोज और लिनक्स पर संकलित होगा और जो प्रारूप के साथ काम करेगा। आपके safegetlineसमाधान का एक महत्वपूर्ण हिस्सा है। लेकिन अगर यह प्रोग्राम विंडोज पर संकलित किया जा रहा है, तो क्या मुझे बाइनरी प्रारूप में फ़ाइल खोलने की आवश्यकता होगी? क्या विंडोज कंपाइलर (टेक्स्ट मोड में) '\ n' को '\ r' '\ n' की तरह व्यवहार करने की अनुमति देते हैं? ifstream f("f.txt", ios_base :: binary | ios_base::in );
हारून मैकडैड

8

क्या आप फ़ाइल को BINARY या TEXT मोड में पढ़ रहे हैं ? में पाठ मोड जोड़ी गाड़ी वापसी / लाइन फ़ीड, CRLF , के रूप में व्याख्या की है पाठ पंक्ति के अंत, या लाइन चरित्र के अंत है, लेकिन में बायनरी आप केवल लाने एक एक समय में बाइट, जिसका अर्थ है कि या तो चरित्र MUSTनजरअंदाज किया और बफर में एक और बाइट के रूप में लाने के लिए छोड़ दिया! कैरिज रिटर्न का मतलब है, टाइपराइटर में, कि टाइपराइटर कार, जहां प्रिंटिंग आर्म निहित है, कागज के दाहिने किनारे पर पहुंच गया है और बाएं किनारे पर वापस आ गया है। यह एक बहुत ही यांत्रिक मॉडल है, जो यांत्रिक टाइपराइटर का है। फिर लाइन फीड का मतलब है कि पेपर रोल को थोड़ा ऊपर घुमाया जाता है, ताकि कागज टाइपिंग की दूसरी लाइन शुरू करने की स्थिति में हो। जैसा कि मुझे याद है कि ASCII में कम अंकों में से एक का अर्थ है कि टाइपिंग के बिना सही एक वर्ण पर जाएं, मृत वर्ण, और निश्चित रूप से बैकस्पेस का अर्थ है: कार को एक वर्ण वापस ले जाएं। इस तरह से आप विशेष प्रभाव जोड़ सकते हैं, जैसे अंतर्निहित (प्रकार अंडरस्कोर), स्ट्राइकथ्रू (टाइप माइनस), लगभग अलग-अलग उच्चारण, रद्द करें (टाइप एक्स), एक विस्तारित कीबोर्ड की आवश्यकता के बिना, लाइन फीड में प्रवेश करने से पहले लाइन के साथ कार की स्थिति को समायोजित करके। तो आप बीच में एक कंप्यूटर के बिना टाइपराइटर को स्वचालित रूप से नियंत्रित करने के लिए बाइट आकार के ASCII वोल्टेज का उपयोग कर सकते हैं। जब स्वचालित टाइपराइटर पेश किया जाता है,AUTOMATIC का अर्थ है कि एक बार जब आप कागज के सबसे दूर के किनारे पर पहुँच जाते हैं, तो कार बाईं ओर वापस आ जाती है और लाइन फीड लागू हो जाता है, यानी रोल के ऊपर जाते ही कार अपने आप वापस आ जाती है! तो आपको दोनों नियंत्रण वर्णों की आवश्यकता नहीं है, केवल एक, \ n, नई पंक्ति, या पंक्ति फ़ीड।

इसका प्रोग्रामिंग से कोई लेना-देना नहीं है लेकिन ASCII अधिक पुराना है और HEY है! ऐसा लगता है कि कुछ लोग सोच नहीं रहे थे जब उन्होंने पाठ करना शुरू किया था! UNIX प्लेटफॉर्म एक इलेक्ट्रिकल ऑटोमैटिक टाइपकेमाइन मानता है; विंडोज मॉडल अधिक पूर्ण है और यांत्रिक मशीनों के नियंत्रण की अनुमति देता है, हालांकि कुछ नियंत्रण वर्ण कंप्यूटर में कम और कम उपयोगी हो जाते हैं, जैसे घंटी चरित्र, 0x07 अगर मुझे अच्छी तरह से याद है ... कुछ भूल गए ग्रंथों को मूल रूप से नियंत्रण वर्णों के साथ कैप्चर किया जाना चाहिए। विद्युत नियंत्रित टाइपराइटर के लिए और यह मॉडल को बनाए रखा ...

वास्तव में सही भिन्नता सिर्फ \ r, लाइन फीड, कैरिज रिटर्न के अनावश्यक होने, यानी स्वचालित, इसलिए शामिल होगी:

char c;
ifstream is;
is.open("",ios::binary);
...
is.getline(buffer, bufsize, '\r');

//ignore following \n or restore the buffer data
if ((c=is.get())!='\n') is.rdbuf()->sputbackc(c);
...

सभी प्रकार की फ़ाइलों को संभालने का सबसे सही तरीका होगा। नोट तथापि कि \ N में पाठ मोड वास्तव में बाइट जोड़ी 0x0d 0x0A है, लेकिन 0x0d है सिर्फ \ r: \ n \ r शामिल पाठ मोड लेकिन नहीं में बायनरी है, तो \ N और \ r \ n बराबर हैं ... या होना चाहिए। यह वास्तव में एक बहुत ही बुनियादी उद्योग भ्रम है, ठेठ उद्योग जड़ता, जैसा कि सम्मेलन सभी प्लेटफार्मों में CRLF की बात करना है, फिर विभिन्न द्विआधारी व्याख्याओं में आते हैं। सख्ती से, केवल 0x0d (गाड़ी वापसी) सहित फाइलें \ n (CRLF या लाइन फीड) होने के कारण, पाठ में विकृत हैंमोड (टाइपराइटर मशीन: बस कार और स्ट्राइकथ्रू सबकुछ वापस करें ...), और एक नॉन-लाइन ओरिएंटेड बाइनरी फॉर्मेट (या तो \ r या \ r \ n अर्थ लाइन ओरिएंटेड) हैं, ताकि आप टेक्स्ट के रूप में पढ़ने वाले न हों! कोड को कुछ उपयोगकर्ता संदेश के साथ विफल होना चाहिए। यह केवल ओएस पर निर्भर नहीं करता है, बल्कि सी लाइब्रेरी कार्यान्वयन पर भी भ्रम और संभावित बदलावों को जोड़ रहा है ... (विशेष रूप से पारदर्शी यूनिकोड अनुवाद परतों के लिए भ्रामक विविधताओं के लिए अभिव्यक्ति का एक और बिंदु जोड़ रहा है)।

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

संयोग से मुझे याद है कि मुझे कुछ \ r \ n टेक्स्ट फाइलें भी मिलीं ... जो कि अभी भी कुछ मुद्रित टेक्स्ट उपभोक्ताओं द्वारा आवश्यक डबल लाइन टेक्स्ट में तब्दील होती हैं।


"Ios :: बाइनरी" के लिए +1 - कभी-कभी, आप वास्तव में फ़ाइल को पढ़ना चाहते हैं क्योंकि यह है (उदाहरण के लिए चेकसम आदि की गणना के लिए) रनटाइम बदलने के बिना।
मथायस

2

इसका एक हल यह होगा कि पहले सभी लाइन एंडिंग को '\ n' में खोजें और बदलें जैसे कि Git डिफ़ॉल्ट रूप से करता है।


1

अपने स्वयं के कस्टम हैंडलर लिखने या बाहरी पुस्तकालय का उपयोग करने के अलावा, आप भाग्य से बाहर हैं। यह सुनिश्चित करने के लिए सबसे आसान काम line[line.length() - 1]है कि '\ r' नहीं है। लिनक्स पर, यह बहुत ही अच्छा है क्योंकि अधिकांश लाइनें '\ n' के साथ समाप्त हो जाएंगी, जिसका अर्थ है कि यदि आप एक लूप में हैं तो आपका समय थोड़ा कम हो जाएगा। विंडोज पर, यह भी शानदार है। हालाँकि, क्लासिक मैक फ़ाइलों के बारे में क्या जो '\ r' में समाप्त होती हैं? std :: getline लिनक्स या विंडोज पर उन फ़ाइलों के लिए काम नहीं करेगा क्योंकि '\ n' के साथ जांचने की आवश्यकता को समाप्त करते हुए '\ n' और '\ r' दोनों \ _ 'n' के साथ समाप्त हो जाते हैं। स्पष्ट रूप से ऐसा कार्य जो उन फाइलों के साथ काम करता है, वे अच्छी तरह से काम नहीं करेंगे। बेशक, फिर कई EBCDIC सिस्टम मौजूद हैं, कुछ ऐसा जो ज्यादातर पुस्तकालयों से निपटने की हिम्मत नहीं करेगा।

'\ R' के लिए जाँच करना शायद आपकी समस्या का सबसे अच्छा समाधान है। बाइनरी मोड में पढ़ने से आप तीनों सामान्य लाइन एंडिंग्स ('\ r', '\ r \ n' और '\ n') की जांच कर सकेंगे। यदि आप केवल लिनक्स और विंडोज के बारे में परवाह करते हैं, तो पुराने-स्टाइल वाले मैक लाइन एंडिंग अधिक समय तक नहीं होने चाहिए, केवल '\ n' के लिए जाँचें और पीछे चल रहे 'r' अक्षर को हटा दें।


0

यदि यह ज्ञात हो कि प्रत्येक पंक्ति में कितने आइटम / संख्याएँ हैं, तो कोई एक पंक्ति को उदाहरण के लिए 4 संख्याओं के साथ पढ़ सकता है

string num;
is >> num >> num >> num >> num;

यह अन्य लाइन एंडिंग के साथ भी काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.