2010-11-26 15 views
13

मैं ऐसा ही कुछ होता है एक पाठ फ़ाइल के माध्यम से पार्स करने के लिए की जरूरत का उदाहरण बनाकर:एक पाठ फ़ाइल के माध्यम से पार्स, नई वस्तु C++

1|Song Title|Release date||"ignore me"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0 

जो गीत संख्या है, रिलीज की तारीख के बाद, एक के बाद वेबसाइट जिसे मुझे अनदेखा करने की आवश्यकता है, और उसके बाद 0 और 1 की एक श्रृंखला है जो शैलियों के वेक्टर का प्रतिनिधित्व कर सकती है।

मुझे इस डेटा को अलग करने के लिए एक तरीका चाहिए, और उस वेबसाइट को अनदेखा करें, जबकि एक ही समय में एक सॉन्ग ऑब्जेक्ट का एक नया उदाहरण बना रहा है जिसमें एक है: (int songNumber, string songTitle, vector * genres, string रिलीजडेट)

धन्यवाद!

उत्तर

4
  • , एक वर्ग Song कि प्रपत्र की आवश्यकता में डेटा रखता परिभाषित के रूप में आप
  • ऊपर कहा गया है Song::operator>>(const istream&); लागू एक इनपुट स्ट्रीम
  • से ऊपर डेटा पार्स करने से वर्ग को भरने के लिए कर फ़ाइल लाइन पढ़ प्रत्येक लाइन के लिए string::getline
  • का उपयोग करके stringstream में कनवर्ट करें और फिर Song के उदाहरण में फ़ील्ड भरने के लिए अपने operator>> का उपयोग करें।

स्ट्रिंगस्ट्रीम को '|' के साथ टोकननाइज़ करना सरल है। एक विभाजक के रूप में चरित्र, जो काम का बड़ा हिस्सा होगा।

int main() 
{ 
    std::string token; 
    std::string line("1|Song Title|Release date||\"ignore me\"|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0"); 

    std::istringstream iss(line); 
    while (getline(iss, token, '|')) 
    { 
     std::cout << token << std::endl; 
    } 
    return 0; 
} 

कोड here से उठाया गया कोड।

+0

पाया जा सकता है मैं कैसे जानते हो कि 1 और उदाहरण के लिए गीत शीर्षक एक-दूसरे से अलग हो गए हैं? – Edward

+0

@Edward - प्रत्येक इनपुट लाइन को टोकन में पार्स करने के तरीके पर संपादित करें, जिनमें से प्रत्येक 'std :: string' है। यदि आपका इनपुट अच्छी तरह से गठित होने के लिए जाना जाता है तो आप टोकन को सीधे गीत संख्या 'int' और शैलियों के अपने' वेक्टर 'में रखने के लिए संशोधित कर सकते हैं। –

3

आप आमतौर पर वस्तु के प्रकार के लिए operator>> अधिक भार के द्वारा इस करते हैं:

struct song_data { 
    std::string number; 
    std::string title; 
    std::string release_date; 
    // ... 
}; 

std::istream &operator>>(std::istream &is, song_data &s_d) {   
    std::getline(is, s_d.number, '|'); 
    std::getline(is, s_d.title, '|'); 
    std::getline(is, s_d.release_date, '|'); 
    std::string ignore; 
    std::getline(is, ignore, '|'); 
    // ... 
    return is; 
} 

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

संपादित करें: मैं शायद std::vector<bool> genres; जोड़कर और उस वेक्टर में 0 और 1 को पढ़कर शैलियों को संभालेगा। मैं तो एक गणन को निर्दिष्ट क्या शैली वेक्टर में किसी विशिष्ट स्थिति से दर्शाया जाता है जोड़ना होगा, तो (उदाहरण के लिए) का परीक्षण एक विशेष गीत के रूप में "देश" वर्गीकृत किया गया है कि क्या कुछ ऐसा दिखाई देगा:

enum { jazz, country, hiphop, classic_rock, progressive_rock, metal /*, ... */}; 

if (songs[i].genres[country]) 

if (songs[i].genres[hiphop]) 
    process_hiphop(songs[i]); 

बेशक , सटीक शैलियों और उनका आदेश कुछ ऐसा नहीं है जिसे मैं नहीं जानता, इसलिए मैंने कुछ संभावनाएं बनाई हैं - आपको (स्पष्ट रूप से) फ़ाइल प्रारूप के लिए परिभाषित शैलियों (और क्रम) का उपयोग करना होगा।

जहां तक ​​सैकड़ों गाने चलते हैं, सामान्य तरीका (ऊपर उल्लिखित) कुछ ऐसा होगा: std::vector<song_data> songs;। इसके बाद के संस्करण की तरह एक धारा निकासी का उपयोग करना, आप तो डेटा फ़ाइल से वेक्टर को कॉपी कर सकते हैं:

std::copy(std::istream_iterator<song_data>(infile), 
      std::istream_iterator<song_data>(), 
      std::back_inserter(songs)); 

आप मुख्य रूप से नाम से गाने को देखने के लिए की संभावना हो तो (एक उदाहरण के लिए), आप उपयोग करना पसंद कर सकते हैं std::map<std::string, song_data> songs।इससे तरह कुछ करने के लिए कर देगा:

songs["new song"].release_date = Today; 
+0

हां, मुझे लगता है कि मुक्त 'ऑपरेटर >>' सदस्य फ़ंक्शन –

+0

का उपयोग करने से बेहतर शर्त है, अभी मेरे पास 4 पैरा के साथ एक गीत ऑब्जेक्ट है जिसमें शीर्षक, शीर्षक, रिलीज़ डेटा और शैलियों का वेक्टर है। ऐसा करने से मैं गीत ऑब्जेक्ट का एक नया उदाहरण कैसे बना सकता हूं, और क्या कोई अच्छा तरीका है कि मैं 1 और 0 के शैलियों को संभाल सकता हूं? क्या यह ठीक होगा जैसे कि इस तरह की सैकड़ों लाइनें थीं या इसे संपादित करना होगा? – Edward

+0

@Edward - यदि आपकी झलक पर आखिरी चीज है, तो शैली की झंडे की सूची आप तब तक पढ़ सकते हैं जब तक आप उस लाइन पर डेटा से बाहर नहीं निकलते, जब आप 'push_back' का उपयोग करते हुए शैलियों के वेक्टर में शामिल होते हैं। लेकिन यह मुझे स्पष्ट नहीं है कि 0 और 1 एस की सूची में कौन सी संरचना निहित है - क्या एक ही शैली के लिए हमेशा एक ही शैली में ध्वज है? आपके डेटा के आकार के साथ कोई समस्या नहीं है बशर्ते यह आपके कंप्यूटर में स्मृति से बाहर न हो - फ़ाइल इनपुट के लिए सही ढंग से निर्मित लूप और शैली पार्सिंग के लिए ठीक काम करेगा। –

16

C++ String Toolkit Library (StrTk) आपकी समस्या के लिए निम्न समाधान हो गया है:

#include <string> 
#include <deque> 
#include "strtk.hpp" 

struct song_type 
{ 
    unsinged int id; 
    std::string release_date; 
    std::string url; 
    char genre[8]; 
}; 

strtk_parse_begin(song_type) 
strtk_parse_type(id) 
strtk_parse_type(release_date) 
strtk_parse_type(url) 
strtk_parse_type(genre[0]) 
strtk_parse_type(genre[1]) 
strtk_parse_type(genre[2]) 
strtk_parse_type(genre[3]) 
strtk_parse_type(genre[4]) 
strtk_parse_type(genre[5]) 
strtk_parse_type(genre[6]) 
strtk_parse_type(genre[7]) 
strtk_parse_end() 

int main() 
{ 
    std::deque<song_type> song_list; 

    strtk::for_each_line("songs.txt", 
         [&song_list](const std::string& line) 
         { 
          song_type s; 
          if (strtk::parse(line,"|",s)) 
           song_list.push_back(s); 
         }); 

    return 0; 
} 

अधिक उदाहरण Here

संबंधित मुद्दे