জাভাতে একটি সুবিধাজনক বিভাজন পদ্ধতি রয়েছে:
String str = "The quick brown fox";
String[] results = str.split(" ");
সি ++ এ করার কোন সহজ উপায় আছে?
জাভাতে একটি সুবিধাজনক বিভাজন পদ্ধতি রয়েছে:
String str = "The quick brown fox";
String[] results = str.split(" ");
সি ++ এ করার কোন সহজ উপায় আছে?
উত্তর:
সি ++ স্ট্যান্ডার্ড লাইব্রেরি অ্যালগরিদমগুলি কংক্রিটের পাত্রে না হয়ে পুনরুক্তিদের কাছাকাছি প্রায় সার্বজনীন ভিত্তিক। দুর্ভাগ্যক্রমে split
এটি সি ++ স্ট্যান্ডার্ড লাইব্রেরিতে জাভা জাতীয় ফাংশন সরবরাহ করা শক্ত করে তোলে , যদিও কেউ যুক্তি দেয় না যে এটি সুবিধাজনক হবে। তবে এর রিটার্ন টাইপ কেমন হবে? std::vector<std::basic_string<…>>
? হতে পারে, তবে তারপরে আমরা বরাদ্দগুলি (সম্ভাব্য অপ্রয়োজনীয় এবং ব্যয়বহুল) করতে বাধ্য হয়েছি।
পরিবর্তে, সি ++ স্বেচ্ছাসেবী জটিল ডেলিমিটারগুলির উপর ভিত্তি করে স্ট্রিংগুলিকে বিভক্ত করার উপায়গুলির আধিক্য সরবরাহ করে তবে এগুলির কোনওটিই অন্য ভাষার মতো সুন্দরভাবে আবদ্ধ হয় না। অসংখ্য উপায় পুরো ব্লগ পোস্ট পূরণ করে ।
এর সহজতম সময়ে, আপনি 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
গুলি ব্যবহার করে স্ট্রিং স্ট্রিমের বিষয়বস্তুগুলি এর পুনরুক্তকারী পরিসীমা নির্মাণকারী ব্যবহার করে কোনও ভেক্টরে অনুলিপি করা যেতে পারে।
একাধিক গ্রন্থাগার (যেমন বুস্ট টোকেনাইজার ) নির্দিষ্ট টোকেনিসার সরবরাহ করে।
আরও উন্নত বিভাজনের জন্য নিয়মিত প্রকাশের প্রয়োজন। সি ++ 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;
}
}
সি ++ 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, " ");
}
কয়েকটি সতর্কতা (যা আপনার প্রয়োজন অনুসারে নাও পারে)। প্রক্রিয়াটিতে স্ট্রিংটি "ধ্বংস" হয়, এর অর্থ EOS অক্ষরগুলি ডিলিমটার স্পটে ইনলাইন স্থাপন করা হয়। সঠিক ব্যবহারের জন্য আপনাকে স্ট্রিংয়ের একটি নন-কনস্ট্যান্ড সংস্করণ তৈরি করতে হবে। আপনি মাঝারি পার্সের ছাড়কদের তালিকাও পরিবর্তন করতে পারেন।
আমার নিজের মতামত অনুসারে, উপরের কোডটি এর জন্য পৃথক ক্লাস লেখার চেয়ে অনেক সহজ এবং ব্যবহার করা সহজ। আমার কাছে, ভাষাটি সরবরাহ করে এমন একটি ফাংশন এবং এটি এটি ভাল এবং পরিষ্কারভাবে করে। এটি কেবল একটি "সি ভিত্তিক" সমাধান। এটি উপযুক্ত, এটি সহজ, এবং আপনাকে প্রচুর অতিরিক্ত কোড লিখতে হবে না :-)
আর একটি দ্রুত উপায় ব্যবহার করা হয় 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] );
}
এবং হ্যাঁ, আমরা বিভক্ত করতে পারতাম () একটি উত্তীর্ণের পরিবর্তে কোনও নতুন ভেক্টরকে ফিরিয়ে দিতে পারি It's এটি মোড়ানো এবং ওভারলোডের পক্ষে তুচ্ছ। তবে আমি যা করছি তার উপর নির্ভর করে আমি প্রায়শই নতুন তৈরির চেয়ে প্রাক-বিদ্যমান বস্তুগুলিকে পুনরায় ব্যবহার করা ভাল বলে মনে করি। (যতক্ষণ না আমি মাঝেমধ্যে ভেক্টরটি খালি করতে ভুলি না!)
তথ্যসূত্র: http://www.cplusplus.com/references/string/string/ ।
(আমি মূলত ডগের প্রশ্নের জবাব লিখছিলাম: সি ++ স্ট্রিংস মডিফাইং এবং এক্সট্র্যাক্টিং এর উপর ভিত্তি করে বিভাজক (বদ্ধ) But
std::string
শ্রেণিতে একটি বিভাজন () ফাংশন অন্তর্ভুক্ত নেই?
start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());
এবং যখন লুপটি হওয়া উচিত while (start != string::npos)
। এছাড়াও, আমি ভেক্টরের ভিতরে beforeোকানোর আগে এটি খালি নয় তা নিশ্চিত করার জন্য আমি সাবস্ট্রিংগুলি পরীক্ষা করে দেখি।
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"
""
আমি জানি আপনি একটি সি ++ সমাধান চেয়েছিলেন, তবে আপনি এটি সহায়ক হিসাবে বিবেচনা করতে পারেন:
কিউটি
#include <QString>
...
QString str = "The quick brown fox";
QStringList results = str.split(" ");
এই উদাহরণে বুস্টের চেয়ে সুবিধাটি হ'ল এটি হ'ল এটি আপনার পোস্টের কোডটিতে সরাসরি এক থেকে এক ম্যাপিং।
কিউটি ডকুমেন্টেশনে আরও দেখুন
এখানে একটি নমুনা টোকেনাইজার ক্লাস যা আপনি যা চান তা করতে পারে
//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);
}
}
পাইস্ট্রিং একটি ছোট লাইব্রেরি যা স্পাইথ পদ্ধতি সহ পাইথনের স্ট্রিং ফাংশনগুলির একটি গোছা প্রয়োগ করে:
#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, "-");
আমি অনুরূপ প্রশ্নের জন্য এই উত্তর পোস্ট।
চাকা পুনরুদ্ধার করবেন না। আমি বেশ কয়েকটি গ্রন্থাগার ব্যবহার করেছি এবং আমার কাছে আসা দ্রুত এবং সবচেয়ে নমনীয় হ'ল: সি ++ স্ট্রিং টুলকিট লাইব্রেরি ।
এখানে স্ট্যাকওভারফ্লোতে আমি যেখানে পোস্ট করেছি সেখানে এটি কীভাবে ব্যবহার করতে হয় তার উদাহরণ is
#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"; }
এমএফসি / এটিএল একটি খুব সুন্দর টোকনাইজার রয়েছে। এমএসডিএন থেকে:
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
আপনি সি ব্যবহার করতে ইচ্ছুক হলে, আপনি স্ট্রটোক ফাংশনটি ব্যবহার করতে পারেন । মাল্টি-থ্রেডিং ইস্যুগুলি ব্যবহার করার সময় আপনার মনোযোগ দেওয়া উচিত।
strtok_s
যা মূলত 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;
}
কাপুরুষোচিতভাবে দাবি অস্বীকার: আমি রিয়েল-টাইম ডেটা প্রসেসিং সফ্টওয়্যারটি লিখি যেখানে বাইনারি ফাইল, সকেট বা কিছু এপিআই কল (আই / ও কার্ড, ক্যামেরার) মাধ্যমে ডেটা আসে। আমি এই ফাংশনটি কখনই প্রারম্ভকালে বাহ্যিক কনফিগারেশন ফাইলগুলি পড়ার চেয়ে আরও জটিল বা সময়-সমালোচনার জন্য ব্যবহার করি না।
আপনি কেবল একটি নিয়মিত এক্সপ্রেশন লাইব্রেরি ব্যবহার করতে পারেন এবং নিয়মিত এক্সপ্রেশন ব্যবহার করে এটি সমাধান করতে পারেন।
এক্সপ্রেশন (\ ডাব্লু +) এবং \ 1 (বা নিয়মিত এক্সপ্রেশনগুলির গ্রন্থাগার বাস্তবায়নের উপর নির্ভর করে $ 1) এর পরিবর্তনশীল ব্যবহার করুন।
অনেক অতিরিক্ত জটিল পরামর্শ এখানে। এই সহজ স্ট্যান্ডিং :: স্ট্রিং সমাধানটি ব্যবহার করে দেখুন:
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;
}
আমি ভেবেছিলাম >>
স্ট্রিং স্ট্রিমগুলির অপারেটর এটি ছিল:
string word; sin >> word;
অ্যাডাম পিয়ার্সের উত্তরটি হ'ল একটি হাতের কাঁটা টোকেনাইজার সরবরাহ করে const char*
। পুনরাবৃত্তকারীদের সাথে এটি করা আরও কিছুটা সমস্যাযুক্ত কারণ কোনও string
প্রান্তের পুনরাবৃত্তিকে বাড়ানো অপরিজ্ঞাত । এটি বলেছিল, প্রদত্ত 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);
যদি আপনার সি ++ 17 এ অ্যাক্সেস না থাকে তবে আপনাকে data(str)
এই উদাহরণ হিসাবে যেমন বিকল্প করতে হবে : http://ideone.com/8kAGoa
উদাহরণে প্রদর্শিত না হলেও strtok
প্রতিটি টোকেনের জন্য একই ডিলিমিটার ব্যবহার করার দরকার নেই। যদিও এই সুবিধার পাশাপাশি, বেশ কয়েকটি ত্রুটি রয়েছে:
strtok
একসাথে একাধিকটিতে ব্যবহার করা যাবে না strings
: হয় nullptr
বর্তমানের টোকানাইজিং চালিয়ে যেতে একটি অবশ্যই পাস করতে হবে string
অথবা একটি নতুন char*
টোকেনাইজ পাস করতে হবে (এমন কিছু মানহীন বাস্তবায়ন রয়েছে যা এটি সমর্থন করে যেমন, যেমন strtok_s
:)strtok
একাধিক থ্রেডে একসাথে ব্যবহার করা যাবে না (এটি তবে বাস্তবায়িত সংজ্ঞায়িত হতে পারে, উদাহরণস্বরূপ: ভিজ্যুয়াল স্টুডিওর বাস্তবায়ন থ্রেড নিরাপদ )strtok
সংশোধন string
করে, সুতরাং এটি const string
এস, const char*
গুলি, বা আক্ষরিক স্ট্রিংগুলিতে ব্যবহার করা যাবে না , এর সাথে টোকনাইজ strtok
করতে বা string
যার বিষয়বস্তু সংরক্ষণ করা দরকার তার সাথে কাজ করতে, str
অনুলিপি করতে হবে, তারপরে অনুলিপিটি তৈরি করা যেতে পারে চালিত করাC ++ 20split_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
সি 11 মান, উপায় দ্বারা। strtok_r
একটি POSIX2001 মান। এই উভয়ের মধ্যেই strtok
বেশিরভাগ প্ল্যাটফর্মগুলির জন্য একটি স্ট্যান্ডার্ড পুনরায় প্রবেশের সংস্করণ রয়েছে ।
#include <cstring>
কেবলমাত্র c99 সংস্করণ অন্তর্ভুক্ত strtok
। সুতরাং আমার ধারণাটি হ'ল আপনি strtok
এক্সটেনশনের নির্দিষ্ট প্রাপ্যতা কার্যকরভাবে প্রদর্শন করে এই মন্তব্যটি সমর্থনকারী উপাদান হিসাবে সরবরাহ করছেন ?
strtok_s
সি 11 এবং মাইক্রোসফ্ট এর সি রানটাইমের একক এক্সটেনশন হিসাবে উভয়ই সরবরাহ করেছেন। মাইক্রোসফ্টের _s
ফাংশনগুলি সি স্ট্যান্ডার্ড হয়ে উঠেছে এখানে ইতিহাসের একটি কৌতূহলজনক বিষয় রয়েছে ।
আমি জানি এই প্রশ্নের উত্তর ইতিমধ্যে দেওয়া হয়েছে তবে আমি অবদান রাখতে চাই। হতে পারে আমার সমাধানটি কিছুটা সহজ তবে এটিই আমি এনেছি:
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;
}
আমার কোডে কোনও কিছুর আরও ভাল পন্থা আছে বা কিছু ভুল হলে দয়া করে মন্তব্য করুন।
আপডেট: জেনেরিক বিভাজক যুক্ত
এখানে এমন একটি দৃষ্টিভঙ্গি যা আপনাকে খালি টোকেন অন্তর্ভুক্ত (স্টার্সপের মতো) বা বাদ দেওয়া (স্ট্রটোকের মতো) নিয়ন্ত্রণ করতে দেয়।
#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;
}
আমার কাছে বেআইনি বলে মনে হচ্ছে যে আমরা সকলেই এখানে গতিশীল সচেতন নার্দের সাথে SO তে প্রত্যেকেই এমন সংস্করণ উপস্থাপন করতে পারি নি যা ডিলিমিটারের জন্য একটি সংকলন সময় উত্পন্ন লুক আপ টেবিল ব্যবহার করে (উদাহরণস্বরূপ আরও নীচে বাস্তবায়ন করা)। সন্ধানী টেবিল এবং পুনরুক্তি ব্যবহারকারীর দক্ষতাতে স্টাড :: রেজেক্সকে পরাজিত করা উচিত, যদি আপনার রেজেক্সকে বীট করার প্রয়োজন না হয় তবে কেবল এটি ব্যবহার করুন, এর মানটি সি ++ 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
আপনি boost :: 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;
}
হোয়াইটস্পেসে স্ট্রিং বিভক্ত করার জন্য স্ট্রিং-টোকেনাইজারগুলির আমার সুইস আর্মি নাইফ, একক এবং ডাবল-কোট মোড়ানো স্ট্রিংগুলির জন্য অ্যাকাউন্টিং করার পাশাপাশি ফলাফলগুলি থেকে এই অক্ষরগুলি কেটে ফেলার জন্য। আমি বেশিরভাগ কোড-স্নিপেট তৈরি করতে RegexBuddy 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;
}
টোকেনাইজ করার জন্য যদি ইনপুট স্ট্রিংয়ের সর্বাধিক দৈর্ঘ্য জানা থাকে তবে যে কেউ এটি ব্যবহার করতে পারে এবং খুব দ্রুত সংস্করণটি প্রয়োগ করতে পারে। আমি নীচের মূল ধারণাটি স্কেচ করছি, যা স্ট্রোকক () এবং "প্রত্যয় অ্যারে" -ডাটা কাঠামোতে জন বেন্টলির "প্রোগ্রামিং পার্লস" ২ য় সংস্করণ, অধ্যায় 15 বর্ণিত হয়েছে দ্বারা অনুপ্রাণিত হয়েছিল। এই ক্ষেত্রে সি ++ শ্রেণি কেবল কিছু সংস্থা এবং সুবিধা দেয় ব্যবহার. টোকেনের শীর্ষস্থানীয় এবং পিছনের সাদা অংশের অক্ষরগুলি অপসারণের জন্য প্রদর্শিত বাস্তবায়নটি সহজেই বাড়ানো যেতে পারে।
মূলত কেউ স্ট্রিং-টার্মিনেটিং '\ 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
/ ব্যবহার করে আন্তর্জাতিককরণের (আই 18 এন) ইস্যুগুলির সাথে আপনার কোডকে পোর্টেবল তৈরি করার বিষয়টি বিবেচনা করুন ।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), একাধিক ডিলিমিটার গ্রহণ করে (একটি স্ট্যান্ড :: স্ট্রিংয়ে নির্দিষ্ট), কেবল ভেক্টর, স্ট্রিং এবং পুনরুক্তি ব্যবহার করে।
#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;
}