जावा में एक सुविधाजनक विभाजन विधि है:
String str = "The quick brown fox";
String[] results = str.split(" ");
क्या C ++ में ऐसा करने का एक आसान तरीका है?
जावा में एक सुविधाजनक विभाजन विधि है:
String str = "The quick brown fox";
String[] results = str.split(" ");
क्या C ++ में ऐसा करने का एक आसान तरीका है?
जवाबों:
C ++ मानक पुस्तकालय एल्गोरिदम कंक्रीट कंटेनर के बजाय पुनरावृत्तियों के आसपास बहुत सार्वभौमिक रूप से आधारित हैं। दुर्भाग्य से यह split
C ++ मानक पुस्तकालय में जावा जैसा फ़ंक्शन प्रदान करना कठिन बनाता है , हालांकि कोई भी तर्क नहीं देता है कि यह सुविधाजनक होगा। लेकिन इसका रिटर्न टाइप क्या होगा? std::vector<std::basic_string<…>>
? हो सकता है, लेकिन तब हम प्रदर्शन करने के लिए मजबूर होते हैं (संभवतः अनावश्यक और महंगा) आवंटन।
इसके बजाय, C ++ मनमाने ढंग से जटिल सीमांकक के आधार पर तारों को विभाजित करने के तरीकों की एक बहुतायत प्रदान करता है, लेकिन उनमें से कोई भी अन्य भाषाओं की तरह अच्छी तरह से समझाया नहीं गया है। कई तरीके पूरे ब्लॉग पोस्ट को भरते हैं ।
इसके सरलतम पर, आप std::string::find
हिट होने तक इसका उपयोग कर सकते हैं std::string::npos
, और उपयोग की गई सामग्री को निकाल सकते हैं std::string::substr
।
व्हॉट्सएप पर बंटवारे के लिए एक अधिक द्रव (और मुहावरेदार, लेकिन मूल) संस्करण का उपयोग करेगा std::istringstream
:
auto iss = std::istringstream{"The quick brown fox"};
auto str = std::string{};
while (iss >> str) {
process(str);
}
std::istream_iterator
एस का उपयोग करते हुए , स्ट्रिंग स्ट्रीम की सामग्री को इसकी पुनरावृत्ति श्रेणी के निर्माणकर्ता का उपयोग करके वेक्टर में भी कॉपी किया जा सकता है।
कई पुस्तकालय (जैसे Boost.Tokenizer ) विशिष्ट टोकन प्रदान करते हैं।
अधिक उन्नत विभाजन के लिए नियमित अभिव्यक्ति की आवश्यकता होती है। C ++ std::regex_token_iterator
इस उद्देश्य के लिए विशेष रूप से प्रदान करता है :
auto const str = "The quick brown fox"s;
auto const re = std::regex{R"(\s+)"};
auto const vec = std::vector<std::string>(
std::sregex_token_iterator{begin(str), end(str), re, -1},
std::sregex_token_iterator{}
);
बूस्ट tokenizer वर्ग काफी सरल बात की इस तरह बना सकते हैं:
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char> > tokens(text, sep);
BOOST_FOREACH (const string& t, tokens) {
cout << t << "." << endl;
}
}
C ++ 11 के लिए अपडेट किया गया:
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer<char_separator<char>> tokens(text, sep);
for (const auto& t : tokens) {
cout << t << "." << endl;
}
}
char_separator
कंस्ट्रक्टर के तीसरे मापदंडों से सावधान रहें ( drop_empty_tokens
डिफ़ॉल्ट, वैकल्पिक है keep_empty_tokens
)।
.h
सी हेडर के लिए)
यहाँ एक वास्तविक सरल है:
#include <vector>
#include <string>
using namespace std;
vector<string> split(const char *str, char c = ' ')
{
vector<string> result;
do
{
const char *begin = str;
while(*str != c && *str)
str++;
result.push_back(string(begin, str));
} while (0 != *str++);
return result;
}
स्ट्रटोक का उपयोग करें। मेरी राय में, टोकन के आसपास एक वर्ग बनाने की आवश्यकता नहीं है जब तक कि स्ट्रटोक आपको वह प्रदान नहीं करता है जिसकी आपको आवश्यकता है। यह नहीं हो सकता है, लेकिन सी और सी ++ में विभिन्न पार्सिंग कोड लिखने के 15+ वर्षों में, मैंने हमेशा स्ट्रैटोक का उपयोग किया है। यहाँ एक उदाहरण है
char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
printf ("Token: %s\n", p);
p = strtok(NULL, " ");
}
कुछ कैविएट (जो आपकी आवश्यकताओं के अनुरूप नहीं हो सकते हैं)। प्रक्रिया में स्ट्रिंग को "नष्ट" किया जाता है, जिसका अर्थ है कि ईओएस वर्णों को सीमांकक स्थानों में इनलाइन रखा गया है। सही उपयोग से आपको स्ट्रिंग का नॉन-कास्ट संस्करण बनाने की आवश्यकता हो सकती है। आप सीमांकक मध्य पार्स की सूची भी बदल सकते हैं।
मेरी अपनी राय में, उपरोक्त कोड इसके लिए एक अलग वर्ग लिखने की तुलना में कहीं अधिक सरल और आसान है। मेरे लिए, यह उन कार्यों में से एक है जो भाषा प्रदान करती है और इसे अच्छी तरह से और सफाई से करती है। यह बस एक "सी आधारित" समाधान है। यह उपयुक्त है, यह आसान है, और आपको बहुत सारे अतिरिक्त कोड लिखने की आवश्यकता नहीं है :-)
एक और त्वरित तरीका उपयोग करना है getline
। कुछ इस तरह:
stringstream ss("bla bla");
string s;
while (getline(ss, s, ' ')) {
cout << s << endl;
}
यदि आप चाहें, तो आप एक साधारण split()
तरीके से रिटर्निंग कर सकते हैं vector<string>
, जो वास्तव में उपयोगी है।
आप इसे सीधे रूप से करने के लिए धाराओं, पुनरावृत्तियों और प्रतिलिपि एल्गोरिथ्म का उपयोग कर सकते हैं।
#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>
int main()
{
std::string str = "The quick brown fox";
// construct a stream from the string
std::stringstream strstr(str);
// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);
// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);
}
std
इस तरह से यह बताना चाहता हूं कि मुझे पता है कि मेरी वस्तु कहां से आई है, यह केवल शैली की बात है।
कोई अपराध लोगों, लेकिन इस तरह एक साधारण सी समस्या के लिए, आप कर रहे हैं चीजें जिस तरह से भी जटिल। बूस्ट का उपयोग करने के कई कारण हैं । लेकिन कुछ इस सरल के लिए, यह 20 # स्लेज के साथ एक मक्खी मारने की तरह है।
void
split( vector<string> & theStringVector, /* Altered/returned value */
const string & theString,
const string & theDelimiter)
{
UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.
size_t start = 0, end = 0;
while ( end != string::npos)
{
end = theString.find( theDelimiter, start);
// If at end, use length=maxLength. Else use length=end-start.
theStringVector.push_back( theString.substr( start,
(end == string::npos) ? string::npos : end - start));
// If at end, use start=maxSize. Else use start=end+delimiter.
start = ( ( end > (string::npos - theDelimiter.size()) )
? string::npos : end + theDelimiter.size());
}
}
उदाहरण के लिए (डौग के मामले के लिए),
#define SHOW(I,X) cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl
int
main()
{
vector<string> v;
split( v, "A:PEP:909:Inventory Item", ":" );
for (unsigned int i = 0; i < v.size(); i++)
SHOW( i, v[i] );
}
और हाँ, हम विभाजित हो सकते हैं () एक में गुजरने के बजाय एक नया वेक्टर लौटाते हैं। यह लपेटने और अधिभार के लिए तुच्छ है। लेकिन जो मैं कर रहा हूं, उसके आधार पर, मैं अक्सर पहले से मौजूद वस्तुओं को फिर से उपयोग करने के बजाय बेहतर बनाता हूं कि वे हमेशा नए बनाएं। (जब तक मैं बीच में वेक्टर खाली करने के लिए मत भूलना!)
संदर्भ: http://www.cplusplus.com/reference/string/string/ ।
(मैं मूल रूप से डग के सवाल का जवाब लिख रहा था: सी ++ स्ट्रिंग्स मॉडिफ़ाइंग एंड एक्सट्रैक्टिंग पर आधारित सेपरेटर (बंद) । लेकिन चूंकि मार्टिन ने उस प्रश्न को यहां एक पॉइंटर के साथ बंद कर दिया ... मैं सिर्फ अपना कोड सामान्य करूंगा।)
std::string
क्लास में स्प्लिट () फ़ंक्शन क्यों शामिल नहीं है ?
start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());
और जबकि लूप होनी चाहिए while (start != string::npos)
। इसके अलावा, मैं यह सुनिश्चित करने के लिए कि यह सदिश में डालने से पहले खाली नहीं हूं, सबस्ट्रिंग की जांच करता हूं।
regex_token_iterator
एस का उपयोग कर एक समाधान :
#include <iostream>
#include <regex>
#include <string>
using namespace std;
int main()
{
string str("The quick brown fox");
regex reg("\\s+");
sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
sregex_token_iterator end;
vector<string> vec(iter, end);
for (auto a : vec)
{
cout << a << endl;
}
}
बूस्ट का एक मजबूत विभाजन कार्य है: बढ़ावा :: एल्गोरिथ्म :: विभाजन ।
नमूना कार्यक्रम:
#include <vector>
#include <boost/algorithm/string.hpp>
int main() {
auto s = "a,b, c ,,e,f,";
std::vector<std::string> fields;
boost::split(fields, s, boost::is_any_of(","));
for (const auto& field : fields)
std::cout << "\"" << field << "\"\n";
return 0;
}
आउटपुट:
"a"
"b"
" c "
""
"e"
"f"
""
मुझे पता है कि आपने C ++ समाधान के लिए कहा था, लेकिन आप इसे उपयोगी मान सकते हैं:
क्यूटी
#include <QString>
...
QString str = "The quick brown fox";
QStringList results = str.split(" ");
इस उदाहरण में बूस्ट पर लाभ यह है कि यह आपके पोस्ट के कोड के लिए एक मैपिंग के लिए प्रत्यक्ष है।
Qt प्रलेखन में अधिक देखें
यहाँ एक नमूना टोकेनाइज़र वर्ग है जो कि आप जो चाहते हैं वह कर सकते हैं
//Header file
class Tokenizer
{
public:
static const std::string DELIMITERS;
Tokenizer(const std::string& str);
Tokenizer(const std::string& str, const std::string& delimiters);
bool NextToken();
bool NextToken(const std::string& delimiters);
const std::string GetToken() const;
void Reset();
protected:
size_t m_offset;
const std::string m_string;
std::string m_token;
std::string m_delimiters;
};
//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");
Tokenizer::Tokenizer(const std::string& s) :
m_string(s),
m_offset(0),
m_delimiters(DELIMITERS) {}
Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
m_string(s),
m_offset(0),
m_delimiters(delimiters) {}
bool Tokenizer::NextToken()
{
return NextToken(m_delimiters);
}
bool Tokenizer::NextToken(const std::string& delimiters)
{
size_t i = m_string.find_first_not_of(delimiters, m_offset);
if (std::string::npos == i)
{
m_offset = m_string.length();
return false;
}
size_t j = m_string.find_first_of(delimiters, i);
if (std::string::npos == j)
{
m_token = m_string.substr(i);
m_offset = m_string.length();
return true;
}
m_token = m_string.substr(i, j - i);
m_offset = j;
return true;
}
उदाहरण:
std::vector <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
v.push_back(s.GetToken());
}
यह एक सरल एसटीएल-ओनली सॉल्यूशन (~ 5 लाइन्स!) का उपयोग करना है std::find
और std::find_first_not_of
जो कि सीमांकक के पुनरावृत्तियों (उदाहरण के लिए रिक्त स्थान या अवधि) को संभालता है, साथ ही सीमांकक को अग्रणी और अनुगामी बनाता है:
#include <string>
#include <vector>
void tokenize(std::string str, std::vector<string> &token_v){
size_t start = str.find_first_not_of(DELIMITER), end=start;
while (start != std::string::npos){
// Find next occurence of delimiter
end = str.find(DELIMITER, start);
// Push back the token found into vector
token_v.push_back(str.substr(start, end-start));
// Skip all occurences of the delimiter to find new start
start = str.find_first_not_of(DELIMITER, end);
}
}
इसे लाइव करके देखें !
pystring एक छोटा पुस्तकालय है जो विभाजन विधि सहित पायथन के स्ट्रिंग कार्यों का एक गुच्छा लागू करता है:
#include <string>
#include <vector>
#include "pystring.h"
std::vector<std::string> chunks;
pystring::split("this string", chunks);
// also can specify a separator
pystring::split("this-string", chunks, "-");
मैंने इसी प्रश्न के लिए यह उत्तर पोस्ट किया है।
पहिया को सुदृढ़ मत करो। मैंने कई लाइब्रेरियों का उपयोग किया है और सबसे तेज और सबसे लचीला मैं आया है: C ++ स्ट्रिंग टूलकिट लाइब्रेरी ।
यहां इसका उपयोग करने का एक उदाहरण दिया गया है कि मैंने स्टैकओवरफ्लो पर और कहां पोस्ट किया है।
#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>
const char *whitespace = " \t\r\n\f";
const char *whitespace_and_punctuation = " \t\r\n\f;,=";
int main()
{
{ // normal parsing of a string into a vector of strings
std::string s("Somewhere down the road");
std::vector<std::string> result;
if( strtk::parse( s, whitespace, result ) )
{
for(size_t i = 0; i < result.size(); ++i )
std::cout << result[i] << std::endl;
}
}
{ // parsing a string into a vector of floats with other separators
// besides spaces
std::string s("3.0, 3.14; 4.0");
std::vector<float> values;
if( strtk::parse( s, whitespace_and_punctuation, values ) )
{
for(size_t i = 0; i < values.size(); ++i )
std::cout << values[i] << std::endl;
}
}
{ // parsing a string into specific variables
std::string s("angle = 45; radius = 9.9");
std::string w1, w2;
float v1, v2;
if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
{
std::cout << "word " << w1 << ", value " << v1 << std::endl;
std::cout << "word " << w2 << ", value " << v2 << std::endl;
}
}
return 0;
}
इस उदाहरण को देखें। यह आपकी मदद कर सकता है ..
#include <iostream>
#include <sstream>
using namespace std;
int main ()
{
string tmps;
istringstream is ("the dellimiter is the space");
while (is.good ()) {
is >> tmps;
cout << tmps << "\n";
}
return 0;
}
while ( is >> tmps ) { std::cout << tmps << "\n"; }
MFC / ATL में एक बहुत अच्छा टोकन है। MSDN से:
CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;
resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
printf("Resulting token: %s\n", resToken);
resToken= str.Tokenize("% #",curPos);
};
Output
Resulting Token: First
Resulting Token: Second
Resulting Token: Third
यदि आप C का उपयोग करने के इच्छुक हैं, तो आप strtok फ़ंक्शन का उपयोग कर सकते हैं । इसका उपयोग करते समय आपको बहु-थ्रेडिंग मुद्दों पर ध्यान देना चाहिए।
साधारण सामान के लिए मैं बस निम्नलिखित का उपयोग करता हूं:
unsigned TokenizeString(const std::string& i_source,
const std::string& i_seperators,
bool i_discard_empty_tokens,
std::vector<std::string>& o_tokens)
{
unsigned prev_pos = 0;
unsigned pos = 0;
unsigned number_of_tokens = 0;
o_tokens.clear();
pos = i_source.find_first_of(i_seperators, pos);
while (pos != std::string::npos)
{
std::string token = i_source.substr(prev_pos, pos - prev_pos);
if (!i_discard_empty_tokens || token != "")
{
o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
number_of_tokens++;
}
pos++;
prev_pos = pos;
pos = i_source.find_first_of(i_seperators, pos);
}
if (prev_pos < i_source.length())
{
o_tokens.push_back(i_source.substr(prev_pos));
number_of_tokens++;
}
return number_of_tokens;
}
कायरतापूर्ण अस्वीकरण: मैं वास्तविक समय डाटा प्रोसेसिंग सॉफ्टवेयर लिखता हूं जहां डेटा बाइनरी फाइलों, सॉकेट्स या कुछ एपीआई कॉल (आई / ओ कार्ड, कैमरा) के माध्यम से आता है। मैं इस फ़ंक्शन का उपयोग स्टार्टअप पर बाहरी कॉन्फ़िगरेशन फ़ाइलों को पढ़ने की तुलना में कुछ अधिक जटिल या समय-महत्वपूर्ण के लिए नहीं करता हूं।
आप बस एक नियमित अभिव्यक्ति पुस्तकालय का उपयोग कर सकते हैं और नियमित अभिव्यक्ति का उपयोग करके हल कर सकते हैं।
अभिव्यक्ति का उपयोग करें (\ w +) और चर (1 या $ 1 नियमित चर के पुस्तकालय कार्यान्वयन के आधार पर)।
बहुत से जटिल सुझाव यहां दिए गए हैं। इस सरल std :: स्ट्रिंग समाधान का प्रयास करें:
using namespace std;
string someText = ...
string::size_type tokenOff = 0, sepOff = tokenOff;
while (sepOff != string::npos)
{
sepOff = someText.find(' ', sepOff);
string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff;
string token = someText.substr(tokenOff, tokenLen);
if (!token.empty())
/* do something with token */;
tokenOff = sepOff;
}
एडम पियर्स का जवाब एक हाथ से घूमने वाला टोकन प्रदान करता है const char*
। यह iterators के साथ करने के लिए थोड़ा और अधिक समस्याग्रस्त है क्योंकि एक string
's अंत ittering वृद्धि अपरिभाषित है । यह कहा, string str{ "The quick brown fox" }
हम निश्चित रूप से यह पूरा कर सकते हैं:
auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };
while (start != cend(str)) {
const auto finish = find(++start, cend(str), ' ');
tokens.push_back(string(start, finish));
start = finish;
}
यदि आप मानक कार्यक्षमता का उपयोग करके जटिलता को देख रहे हैं, जैसा कि फ्रायड का सुझाव है strtok
कि एक सरल विकल्प है:
vector<string> tokens;
for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i);
यदि आपके पास C ++ 17 तक पहुंच नहीं है, तो आपको data(str)
इस उदाहरण में विकल्प की आवश्यकता होगी : http://ideone.com/8kAGoa
हालांकि उदाहरण में प्रदर्शित नहीं किया गया है, strtok
प्रत्येक टोकन के लिए एक ही सीमांकक का उपयोग करने की आवश्यकता नहीं है। इस लाभ के साथ हालांकि, कई कमियां हैं:
strtok
कई पर नहीं किया जा सकता strings
एक ही समय में: या तो एक nullptr
मौजूदा tokenizing जारी रखने के लिए पास किया जाना चाहिए string
या एक नया char*
करने के लिए tokenize पास किया जाना चाहिए (कुछ गैर-मानक कार्यान्वयन है जो इस तरह के रूप में लेकिन इस का समर्थन करते हैं, कर रहे हैं: strtok_s
)strtok
एक साथ कई थ्रेड्स पर उपयोग नहीं किया जा सकता है (हालांकि यह कार्यान्वयन को परिभाषित किया जा सकता है, उदाहरण के लिए: विजुअल स्टूडियो का कार्यान्वयन सुरक्षित है )strtok
को संशोधित करता है string
यह चालू है, इसलिए इसका उपयोग const string
s, const char*
s, या शाब्दिक तार पर नहीं किया जा सकता है , इनमें से किसी के साथ strtok
या किसी string
की सामग्री को संरक्षित करने के लिए संचालित करने की आवश्यकता है, str
जिसे कॉपी किया जाना है, फिर प्रतिलिपि बनाई जा सकती है चालू होc ++ 20हमें split_view
एक गैर-विनाशकारी तरीके से टोकन स्ट्रिंग्स के साथ प्रदान करता है : https://topanswers.xyz/cplusplus?q=749#a874
पिछले तरीकों से एक टोकन vector
-इन- जेनरेट नहीं किया जा सकता है, जिसका अर्थ है कि उन्हें एक सहायक फ़ंक्शन में अमूर्त किए बिना वे प्रारंभ नहीं कर सकते const vector<string> tokens
। उस कार्यक्षमता और किसी भी श्वेत-स्थान परिसीमन को स्वीकार करने की क्षमता का उपयोग करके दोहन किया जा सकता है istream_iterator
। दिए गए उदाहरण के लिए: const string str{ "The quick \tbrown \nfox" }
हम यह कर सकते हैं:
istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };
istringstream
इस विकल्प के लिए आवश्यक निर्माण में पिछले 2 विकल्पों की तुलना में कहीं अधिक लागत है, हालांकि यह लागत आम तौर पर string
आवंटन के खर्च में छिपी हुई है ।
यदि उपरोक्त विकल्पों में से कोई भी आपकी टोकन आवश्यकताओं के लिए पर्याप्त रूप से लचीले नहीं हैं, तो सबसे अधिक लचीले विकल्प का उपयोग कर regex_token_iterator
रहे हैं बेशक इस लचीलेपन से अधिक खर्च आता है, लेकिन फिर से यह string
आवंटन लागत में छिपा हुआ है । उदाहरण के लिए कहें कि हम गैर-पलायन किए गए अल्पविरामों के आधार पर टोकन लेना चाहते हैं, साथ ही सफेद-अंतरिक्ष खाने से, निम्नलिखित इनपुट दिए गए हैं: const string str{ "The ,qu\\,ick ,\tbrown, fox" }
हम यह कर सकते हैं:
const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };
strtok_s
C11 मानक है, वैसे। strtok_r
POSIX2001 मानक है। उन दोनों के बीच, strtok
अधिकांश प्लेटफार्मों के लिए एक मानक फिर से प्रवेश संस्करण है ।
#include <cstring>
केवल c99 संस्करण शामिल है strtok
। इसलिए मेरी धारणा यह है कि आप इस टिप्पणी को सहायक सामग्री के रूप में प्रदान कर रहे हैं, strtok
एक्सटेंशन की कार्यान्वयन की विशिष्ट उपलब्धता का प्रदर्शन कर रहे हैं ?
strtok_s
C11 और Microsoft के C रनटाइम में स्टैंडअलोन एक्सटेंशन के रूप में प्रदान किया जाता है। यहाँ एक जिज्ञासु सा इतिहास है जहाँ Microsoft के _s
कार्य C मानक बन गए हैं।
मुझे पता है कि यह सवाल पहले से ही उत्तर दिया गया है लेकिन मैं योगदान देना चाहता हूं। हो सकता है कि मेरा समाधान थोड़ा सरल हो, लेकिन यह वही है जो मैं लेकर आया हूं:
vector<string> get_words(string const& text, string const& separator)
{
vector<string> result;
string tmp = text;
size_t first_pos = 0;
size_t second_pos = tmp.find(separator);
while (second_pos != string::npos)
{
if (first_pos != second_pos)
{
string word = tmp.substr(first_pos, second_pos - first_pos);
result.push_back(word);
}
tmp = tmp.substr(second_pos + separator.length());
second_pos = tmp.find(separator);
}
result.push_back(tmp);
return result;
}
कृपया टिप्पणी करें कि क्या मेरे कोड में किसी चीज़ के लिए बेहतर दृष्टिकोण है या यदि कुछ गलत है।
अद्यतन: जोड़ा सामान्य विभाजक
यहां एक दृष्टिकोण है जो आपको इस बात पर नियंत्रण करने की अनुमति देता है कि क्या खाली टोकन शामिल हैं (जैसे strsep) या बाहर रखा गया (जैसे strtok)।
#include <string.h> // for strchr and strlen
/*
* want_empty_tokens==true : include empty tokens, like strsep()
* want_empty_tokens==false : exclude empty tokens, like strtok()
*/
std::vector<std::string> tokenize(const char* src,
char delim,
bool want_empty_tokens)
{
std::vector<std::string> tokens;
if (src and *src != '\0') // defensive
while( true ) {
const char* d = strchr(src, delim);
size_t len = (d)? d-src : strlen(src);
if (len or want_empty_tokens)
tokens.push_back( std::string(src, len) ); // capture token
if (d) src += len+1; else break;
}
return tokens;
}
मेरे लिए यह अजीब लगता है कि हम सभी के साथ एसओ पर सचेत गति से गति करता है, किसी ने भी ऐसा संस्करण प्रस्तुत नहीं किया है जो परिसीमन के लिए संकलित समयबद्ध तालिका का उपयोग करता है (उदाहरण कार्यान्वयन आगे और नीचे)। एक लुक अप टेबल का उपयोग करते हुए और पुनरावृत्तियों को std :: regex को दक्षता में हरा देना चाहिए, यदि आपको regex को पीटने की आवश्यकता नहीं है, तो बस इसका उपयोग करें, इसका मानक C ++ 11 और सुपर लचीला है।
कुछ ने पहले से ही रेगेक्स का सुझाव दिया है, लेकिन यहां के नॉब्स के लिए एक पैकेज्ड उदाहरण है जो ओपी को उम्मीद है कि वास्तव में ऐसा करना चाहिए:
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};
while (std::regex_search (it,end,m,e)) {
ret.emplace_back(m.str());
std::advance(it, m.position() + m.length()); //next start position = match position + match length
}
return ret;
}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){ //comfort version calls flexible version
return split(s.cbegin(), s.cend(), std::move(e));
}
int main ()
{
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};
auto v = split(str);
for(const auto&s:v){
std::cout << s << std::endl;
}
std::cout << "crazy version:" << std::endl;
v = split(str, std::regex{"[^e]+"}); //using e as delim shows flexibility
for(const auto&s:v){
std::cout << s << std::endl;
}
return 0;
}
अगर हमें और तेज़ होने की ज़रूरत है और बाधा को स्वीकार करना है कि सभी चार्ट 8 बिट्स होने चाहिए, तो हम मेटाप्रोग्रामिंग का उपयोग करके संकलन समय पर एक नज़र रख सकते हैं:
template<bool...> struct BoolSequence{}; //just here to hold bools
template<char...> struct CharSequence{}; //just here to hold chars
template<typename T, char C> struct Contains; //generic
template<char First, char... Cs, char Match> //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
Contains<CharSequence<Cs...>, Match>{}; //strip first and increase index
template<char First, char... Cs> //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {};
template<char Match> //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};
template<int I, typename T, typename U>
struct MakeSequence; //generic
template<int I, bool... Bs, typename U>
struct MakeSequence<I,BoolSequence<Bs...>, U>: //not last
MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U>
struct MakeSequence<0,BoolSequence<Bs...>,U>{ //last
using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
/* could be made constexpr but not yet supported by MSVC */
static bool isDelim(const char c){
static const bool table[256] = {Bs...};
return table[static_cast<int>(c)];
}
};
using Delims = CharSequence<'.',',',' ',':','\n'>; //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<256,BoolSequence<>,Delims>::Type>;
इसके साथ एक getNextToken
समारोह बनाने में आसान है:
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end
auto second = std::find_if(begin,end,Table{}); //find first delim or end
return std::make_pair(begin,second);
}
इसका उपयोग करना भी आसान है:
int main() {
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};
auto it = std::begin(s);
auto end = std::end(s);
while(it != std::end(s)){
auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second) << std::endl;
it = token.second;
}
return 0;
}
यहाँ एक जीवंत उदाहरण है: http://ideone.com/GKtkLQ
आप बढ़ावा देने का लाभ ले सकते हैं :: make_find_iterator। इसके समान कुछ:
template<typename CH>
inline vector< basic_string<CH> > tokenize(
const basic_string<CH> &Input,
const basic_string<CH> &Delimiter,
bool remove_empty_token
) {
typedef typename basic_string<CH>::const_iterator string_iterator_t;
typedef boost::find_iterator< string_iterator_t > string_find_iterator_t;
vector< basic_string<CH> > Result;
string_iterator_t it = Input.begin();
string_iterator_t it_end = Input.end();
for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i != string_find_iterator_t();
++i) {
if(remove_empty_token){
if(it != i->begin())
Result.push_back(basic_string<CH>(it,i->begin()));
}
else
Result.push_back(basic_string<CH>(it,i->begin()));
it = i->end();
}
if(it != it_end)
Result.push_back(basic_string<CH>(it,it_end));
return Result;
}
व्हॉट्सएप द्वारा स्ट्रिंग्स को विभाजित करने के लिए मेरे स्विस® आर्मी नाइफ के स्ट्रिंग-टोकनर्स, एकल और डबल-कोट लिपटे स्ट्रिंग्स के साथ-साथ उन पात्रों को परिणामों से अलग करना। मैंने अधिकांश कोड-स्निपेट जेनरेट करने के लिए RexxBuddy 4.x का उपयोग किया , लेकिन मैंने बोली और कुछ अन्य चीजों को छोड़ने के लिए कस्टम हैंडलिंग को जोड़ा।
#include <string>
#include <locale>
#include <regex>
std::vector<std::wstring> tokenize_string(std::wstring string_to_tokenize) {
std::vector<std::wstring> tokens;
std::wregex re(LR"(("[^"]*"|'[^']*'|[^"' ]+))", std::regex_constants::collate);
std::wsregex_iterator next( string_to_tokenize.begin(),
string_to_tokenize.end(),
re,
std::regex_constants::match_not_null );
std::wsregex_iterator end;
const wchar_t single_quote = L'\'';
const wchar_t double_quote = L'\"';
while ( next != end ) {
std::wsmatch match = *next;
const std::wstring token = match.str( 0 );
next++;
if (token.length() > 2 && (token.front() == double_quote || token.front() == single_quote))
tokens.emplace_back( std::wstring(token.begin()+1, token.begin()+token.length()-1) );
else
tokens.emplace_back(token);
}
return tokens;
}
यदि टोके जाने वाले इनपुट स्ट्रिंग की अधिकतम लंबाई ज्ञात है, तो कोई भी इसका फायदा उठा सकता है और बहुत तेज संस्करण को लागू कर सकता है। मैं नीचे दिए गए मूल विचार को स्केच कर रहा हूं, जो कि स्ट्रेटोक () और "प्रत्यय सरणी" से प्रेरित था -डेटा संरचना ने जॉन बेंटले के "प्रोग्रामिंग पर्ल्स" 2 संस्करण, अध्याय 15. का वर्णन किया है। इस मामले में C ++ वर्ग केवल इस संगठन और सुविधा देता है काम का। दिखाए गए कार्यान्वयन को टोकन में अग्रणी और अनुगामी व्हाट्सएप पात्रों को हटाने के लिए आसानी से बढ़ाया जा सकता है।
मूल रूप से एक विभाजक पात्रों को स्ट्रिंग-टर्मिनेटिंग '\ 0'-वर्णों के साथ बदल सकता है और संशोधित स्ट्रिंग के साथ टोकन के लिए पॉइंटर्स सेट कर सकता है। चरम मामले में जब स्ट्रिंग में केवल विभाजक होते हैं, तो एक को स्ट्रिंग की लंबाई प्लस 1 प्राप्त होती है, जिसके परिणामस्वरूप खाली टोकन होते हैं। संशोधित होने के लिए स्ट्रिंग को डुप्लिकेट करना व्यावहारिक है।
शीर्ष लेख फ़ाइल:
class TextLineSplitter
{
public:
TextLineSplitter( const size_t max_line_len );
~TextLineSplitter();
void SplitLine( const char *line,
const char sep_char = ',',
);
inline size_t NumTokens( void ) const
{
return mNumTokens;
}
const char * GetToken( const size_t token_idx ) const
{
assert( token_idx < mNumTokens );
return mTokens[ token_idx ];
}
private:
const size_t mStorageSize;
char *mBuff;
char **mTokens;
size_t mNumTokens;
inline void ResetContent( void )
{
memset( mBuff, 0, mStorageSize );
// mark all items as empty:
memset( mTokens, 0, mStorageSize * sizeof( char* ) );
// reset counter for found items:
mNumTokens = 0L;
}
};
कार्यान्वयन फ़ाइल:
TextLineSplitter::TextLineSplitter( const size_t max_line_len ):
mStorageSize ( max_line_len + 1L )
{
// allocate memory
mBuff = new char [ mStorageSize ];
mTokens = new char* [ mStorageSize ];
ResetContent();
}
TextLineSplitter::~TextLineSplitter()
{
delete [] mBuff;
delete [] mTokens;
}
void TextLineSplitter::SplitLine( const char *line,
const char sep_char /* = ',' */,
)
{
assert( sep_char != '\0' );
ResetContent();
strncpy( mBuff, line, mMaxLineLen );
size_t idx = 0L; // running index for characters
do
{
assert( idx < mStorageSize );
const char chr = line[ idx ]; // retrieve current character
if( mTokens[ mNumTokens ] == NULL )
{
mTokens[ mNumTokens ] = &mBuff[ idx ];
} // if
if( chr == sep_char || chr == '\0' )
{ // item or line finished
// overwrite separator with a 0-terminating character:
mBuff[ idx ] = '\0';
// count-up items:
mNumTokens ++;
} // if
} while( line[ idx++ ] );
}
उपयोग का एक परिदृश्य होगा:
// create an instance capable of splitting strings up to 1000 chars long:
TextLineSplitter spl( 1000 );
spl.SplitLine( "Item1,,Item2,Item3" );
for( size_t i = 0; i < spl.NumTokens(); i++ )
{
printf( "%s\n", spl.GetToken( i ) );
}
उत्पादन:
Item1
Item2
Item3
boost::tokenizer
आपका मित्र है, लेकिन विरासत / प्रकारों के बजाय wstring
/ उपयोग करके अंतर्राष्ट्रीयकरण (i18n) के संदर्भ में अपने कोड को पोर्टेबल बनाने पर विचार करें ।wchar_t
string
char
#include <iostream>
#include <boost/tokenizer.hpp>
#include <string>
using namespace std;
using namespace boost;
typedef tokenizer<char_separator<wchar_t>,
wstring::const_iterator, wstring> Tok;
int main()
{
wstring s;
while (getline(wcin, s)) {
char_separator<wchar_t> sep(L" "); // list of separator characters
Tok tok(s, sep);
for (Tok::iterator beg = tok.begin(); beg != tok.end(); ++beg) {
wcout << *beg << L"\t"; // output (or store in vector)
}
wcout << L"\n";
}
return 0;
}
wchar_t
एक भयानक कार्यान्वयन पर निर्भर प्रकार है जिसे किसी को भी उपयोग नहीं करना चाहिए जब तक कि बिल्कुल आवश्यक न हो।
सरल सी ++ कोड (मानक सी ++ 98), कई सीमांकक (एक std :: string में निर्दिष्ट) को स्वीकार करता है, केवल वैक्टर, स्ट्रिंग्स और पुनरावृत्तियों का उपयोग करता है।
#include <iostream>
#include <vector>
#include <string>
#include <stdexcept>
std::vector<std::string>
split(const std::string& str, const std::string& delim){
std::vector<std::string> result;
if (str.empty())
throw std::runtime_error("Can not tokenize an empty string!");
std::string::const_iterator begin, str_it;
begin = str_it = str.begin();
do {
while (delim.find(*str_it) == std::string::npos && str_it != str.end())
str_it++; // find the position of the first delimiter in str
std::string token = std::string(begin, str_it); // grab the token
if (!token.empty()) // empty token only when str starts with a delimiter
result.push_back(token); // push the token into a vector<string>
while (delim.find(*str_it) != std::string::npos && str_it != str.end())
str_it++; // ignore the additional consecutive delimiters
begin = str_it; // process the remaining tokens
} while (str_it != str.end());
return result;
}
int main() {
std::string test_string = ".this is.a.../.simple;;test;;;END";
std::string delim = "; ./"; // string containing the delimiters
std::vector<std::string> tokens = split(test_string, delim);
for (std::vector<std::string>::const_iterator it = tokens.begin();
it != tokens.end(); it++)
std::cout << *it << std::endl;
}