I'm mencoba untuk iterate atas kata-kata dari sebuah string.
String dapat diasumsikan terdiri dari kata-kata yang dipisahkan oleh spasi.
Perhatikan bahwa I'm tidak tertarik pada C fungsi-fungsi string atau karakter manipulasi/akses. Juga, silakan mendahulukan keanggunan atas efisiensi dalam jawaban anda.
Solusi terbaik yang saya miliki sekarang adalah:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string s = "Somewhere down the road";
istringstream iss(s);
do
{
string subs;
iss >> subs;
cout << "Substring: " << subs << endl;
} while (iss);
}
Apakah ada cara yang lebih elegan untuk melakukan hal ini?
Saya menggunakan ini untuk split string dengan delimiter. Pertama menempatkan hasil di pra-dibangun vektor, pengembalian kedua vektor baru.
#include <string>
#include <sstream>
#include <vector>
#include <iterator>
template <typename Out>
void split(const std::string &s, char delim, Out result) {
std::istringstream iss(s);
std::string item;
while (std::getline(iss, item, delim)) {
*result++ = item;
}
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, std::back_inserter(elems));
return elems;
}
Perhatikan bahwa solusi ini tidak melewatkan kosong token, jadi berikut akan menemukan item 4, salah satu yang kosong:
std::vector<std::string> x = split("one:two::three", ':');
Untuk apa itu's worth, di sini's cara lain untuk mengekstrak bukti dari sebuah string masukan, hanya mengandalkan standar fasilitas perpustakaan. It's contoh dari kekuatan dan keanggunan di balik desain STL.
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
int main() {
using namespace std;
string sentence = "And I feel fine...";
istringstream iss(sentence);
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
ostream_iterator<string>(cout, "\n"));
}
Alih-alih menyalin diekstrak token ke output stream, yang bisa memasukkan mereka ke dalam sebuah wadah, menggunakan generik yang sama copy
algoritma.
vector<string> tokens;
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter(tokens));
... atau membuat vektor
langsung:
vector<string> tokens{istream_iterator<string>{iss},
istream_iterator<string>{}};
Solusi yang mungkin menggunakan Boost mungkin:
#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));
Pendekatan ini mungkin bahkan lebih cepat dari stringstream
pendekatan. Dan karena ini adalah template generik fungsi ini dapat digunakan untuk membagi jenis lain dari string (wchar, dll. atau UTF-8) menggunakan semua jenis pembatas.
Lihat dokumentasi untuk rincian.
#include <vector>
#include <string>
#include <sstream>
int main()
{
std::string str("Split me by whitespaces");
std::string buf; // Have a buffer string
std::stringstream ss(str); // Insert the string into a stream
std::vector<std::string> tokens; // Create vector to hold our words
while (ss >> buf)
tokens.push_back(buf);
return 0;
}
Untuk orang-orang dengan siapa ia tidak duduk baik untuk mengorbankan semua efisiensi untuk kode ukuran dan lihat "efisien" sebagai jenis keanggunan, berikut ini harus memukul sweet spot (dan saya pikir template wadah kelas awesomely selain elegan.):
template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
const std::string& delimiters = " ", bool trimEmpty = false)
{
std::string::size_type pos, lastPos = 0, length = str.length();
using value_type = typename ContainerT::value_type;
using size_type = typename ContainerT::size_type;
while(lastPos < length + 1)
{
pos = str.find_first_of(delimiters, lastPos);
if(pos == std::string::npos)
{
pos = length;
}
if(pos != lastPos || !trimEmpty)
tokens.push_back(value_type(str.data()+lastPos,
(size_type)pos-lastPos ));
lastPos = pos + 1;
}
}
Saya biasanya memilih untuk menggunakan std::vektor<std::string>
jenis sebagai parameter kedua (ContainerT
)... tapi daftar<>
adalah cara yang lebih cepat dari vektor<>
ketika akses langsung tidak diperlukan, dan anda bahkan dapat membuat sendiri class string dan menggunakan sesuatu seperti std::daftar<subString>
dimana subString
tidak melakukan rangkap untuk kecepatan yang luar biasa meningkat.
It's lebih dari dua kali lebih cepat seperti yang tercepat tokenize di halaman ini, dan hampir 5 kali lebih cepat dari beberapa orang lain. Juga dengan pilihan jenis parameter anda dapat menghilangkan semua string dan daftar salinan tambahan untuk meningkatkan kecepatan.
Selain itu tidak melakukan (sangat tidak efisien) kembali dari hasilnya, melainkan melewati token sebagai referensi, sehingga juga memungkinkan anda untuk membangun token menggunakan beberapa panggilan jika anda begitu ingin.
Akhirnya hal ini memungkinkan anda untuk menentukan apakah akan memangkas kosong token dari hasil melalui lalu parameter opsional.
Semua yang dibutuhkan adalah std::string
... sisanya adalah opsional. Tidak menggunakan sungai atau dorongan perpustakaan, tetapi cukup fleksibel untuk dapat menerima beberapa asing jenis secara alami.
Berikut ini's solusi lain. It's kompak dan cukup efisien:
std::vector<std::string> split(const std::string &text, char sep) {
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
tokens.push_back(text.substr(start, end - start));
start = end + 1;
}
tokens.push_back(text.substr(start));
return tokens;
}
Hal ini dapat dengan mudah menjadi templatised untuk menangani string pemisah, lebar string, dll.
Perhatikan bahwa membelah ","
hasil dalam satu string kosong dan membelah ","
(ie. sep) hasil di dua string kosong.
Hal ini juga dapat dengan mudah diperluas untuk melewatkan kosong token:
std::vector<std::string> split(const std::string &text, char sep) {
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
while ((end = text.find(sep, start)) != std::string::npos) {
if (end != start) {
tokens.push_back(text.substr(start, end - start));
}
start = end + 1;
}
if (end != start) {
tokens.push_back(text.substr(start));
}
return tokens;
}
Jika membelah string di beberapa pembatas sambil melompat-lompat kosong token yang diinginkan, versi ini dapat digunakan:
std::vector<std::string> split(const std::string& text, const std::string& delims)
{
std::vector<std::string> tokens;
std::size_t start = text.find_first_not_of(delims), end = 0;
while((end = text.find_first_of(delims, start)) != std::string::npos)
{
tokens.push_back(text.substr(start, end - start));
start = text.find_first_not_of(delims, end);
}
if(start != std::string::npos)
tokens.push_back(text.substr(start));
return tokens;
}
Ini adalah cara favorit saya untuk iterate melalui sebuah string. Anda dapat melakukan apapun yang anda inginkan per kata.
string line = "a line of text to iterate through";
string word;
istringstream iss(line, istringstream::in);
while( iss >> word )
{
// Do something on `word` here...
}
Hal ini serupa dengan Stack Overflow pertanyaan Bagaimana saya tokenize string di C++?.
#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int argc, char** argv)
{
string text = "token test\tstring";
char_separator<char> sep(" \t");
tokenizer<char_separator<char>> tokens(text, sep);
for (const string& t : tokens)
{
cout << t << "." << endl;
}
}
Aku seperti berikut karena menempatkan hasil dalam vektor, mendukung string sebagai pembatas dan memberikan kontrol lebih menjaga nilai-nilai kosong. Tapi, itu doesn't terlihat baik kemudian.
#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
vector<string> result;
if (delim.empty()) {
result.push_back(s);
return result;
}
string::const_iterator substart = s.begin(), subend;
while (true) {
subend = search(substart, s.end(), delim.begin(), delim.end());
string temp(substart, subend);
if (keep_empty || !temp.empty()) {
result.push_back(temp);
}
if (subend == s.end()) {
break;
}
substart = subend + delim.size();
}
return result;
}
int main() {
const vector<string> words = split("So close no matter how far", " ");
copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}
Tentu saja, Meningkatkan memiliki split()
yang bekerja sebagian seperti itu. Dan, jika dengan 'white-space', anda benar-benar tidak berarti semua jenis white-space, menggunakan Boost's split dengan is_any_of()
karya besar.
STL tidak memiliki metode tersebut sudah tersedia.
Namun, anda dapat menggunakan C's strtok()
fungsi dengan menggunakan std::string::c_str()
anggota, atau anda dapat menulis anda sendiri. Berikut ini adalah contoh kode yang saya temukan setelah pencarian Google cepat ("STL string split"):
void Tokenize(const string& str,
vector<string>& tokens,
const string& delimiters = " ")
{
// Skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos)
{
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
Diambil dari: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
Jika anda memiliki pertanyaan tentang kode sampel, meninggalkan komentar dan saya akan menjelaskan.
Dan hanya karena tidak menerapkan typedef
disebut iterator atau membebani <<
operator tidak berarti itu adalah kode yang buruk. Saya menggunakan fungsi C cukup sering. Misalnya, printf
dan scanf
kedua lebih cepat dari std::cin
dan std::cout
(signifikan), yang fopen
syntax ini jauh lebih ramah untuk biner jenis, dan mereka juga cenderung menghasilkan lebih kecil Ongkos.
Don't mendapatkan yang dijual ini "Keanggunan atas kinerja" kesepakatan.
Berikut ini adalah membagi fungsi yang:
template
Contoh penggunaan:
vector<string> v = split<string>("Hello, there; World", ";,");
vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");
Saya memiliki 2 jalur solusi untuk masalah ini:
char sep = ' ';
std::string s="1 This is an example";
for(size_t p=0, q=0; p!=s.npos; p=q)
std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;
Maka alih-alih mencetak anda dapat memasukkannya ke dalam vektor.
Namun lain fleksibel dan cepat cara
template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
const char* s = input;
const char* e = s;
while (*e != 0) {
e = s;
while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
if (e - s > 0) {
op(s, e - s);
}
s = e + 1;
}
}
Untuk menggunakannya dengan vektor string (Edit: Karena seseorang menunjukkan tidak mewarisi kelas STL... hrmf ;) ) :
template<class ContainerType>
class Appender {
public:
Appender(ContainerType& container) : container_(container) {;}
void operator() (const char* s, unsigned length) {
container_.push_back(std::string(s,length));
}
private:
ContainerType& container_;
};
std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");
Yang's itu! Dan yang's hanya salah satu cara untuk menggunakan tokenizer, seperti bagaimana hanya menghitung kata-kata:
class WordCounter {
public:
WordCounter() : noOfWords(0) {}
void operator() (const char*, unsigned) {
++noOfWords;
}
unsigned noOfWords;
};
WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t");
ASSERT( wc.noOfWords == 7 );
Dibatasi oleh imajinasi ;)
Berikut ini's solusi sederhana yang hanya menggunakan standar regex perpustakaan
#include <regex>
#include <string>
#include <vector>
std::vector<string> Tokenize( const string str, const std::regex regex )
{
using namespace std;
std::vector<string> result;
sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
sregex_token_iterator reg_end;
for ( ; it != reg_end; ++it ) {
if ( !it->str().empty() ) //token could be empty:check
result.emplace_back( it->str() );
}
return result;
}
Regex argumen memungkinkan memeriksa beberapa argumen (spasi, koma, dll.)
Saya biasanya hanya memeriksa untuk split pada ruang dan koma, jadi saya juga memiliki fungsi default:
std::vector<string> TokenizeDefault( const string str )
{
using namespace std;
regex re( "[\\s,]+" );
return Tokenize( str, re );
}
The " [\\\\\s,]+"
cek untuk spasi (\\\\\s
) dan koma (,
).
Catatan, jika anda ingin membagi wstring
bukan string
,
std::regex
untuk std::wregex
sregex_token_iterator
untuk wsregex_token_iterator
Catatan, anda mungkin juga ingin mengambil string argumen dengan referensi, tergantung pada compiler.
Menggunakan std::stringstream
seperti yang anda telah bekerja baik-baik saja, dan lakukan apa yang anda ingin. Jika anda're hanya mencari cara yang berbeda dalam melakukan sesuatu meskipun, anda dapat menggunakan std::cari()
/std::find_first_of()
dan std::string::substr()
.
Berikut ini's contoh:
#include <iostream>
#include <string>
int main()
{
std::string s("Somewhere down the road");
std::string::size_type prev_pos = 0, pos = 0;
while( (pos = s.find(' ', pos)) != std::string::npos )
{
std::string substring( s.substr(prev_pos, pos-prev_pos) );
std::cout << substring << '\n';
prev_pos = ++pos;
}
std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
std::cout << substring << '\n';
return 0;
}
Jika anda ingin menggunakan boost, tapi ingin menggunakan seluruh string sebagai pembatas (bukan karakter tunggal seperti di sebagian besar dari sebelumnya mengusulkan solusi), anda dapat menggunakan boost_split_iterator
.
Contoh kode termasuk perangkat template:
#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>
template<typename _OutputIterator>
inline void split(
const std::string& str,
const std::string& delim,
_OutputIterator result)
{
using namespace boost::algorithm;
typedef split_iterator<std::string::const_iterator> It;
for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
iter!=It();
++iter)
{
*(result++) = boost::copy_range<std::string>(*iter);
}
}
int main(int argc, char* argv[])
{
using namespace std;
vector<string> splitted;
split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));
// or directly to console, for example
split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
return 0;
}
Ada sebuah fungsi bernama strtok
.
#include<string>
using namespace std;
vector<string> split(char* str,const char* delim)
{
char* saveptr;
char* token = strtok_r(str,delim,&saveptr);
vector<string> result;
while(token != NULL)
{
result.push_back(token);
token = strtok_r(NULL,delim,&saveptr);
}
return result;
}
Heres regex solusi yang hanya menggunakan standar regex perpustakaan. (Saya'm sedikit berkarat, jadi mungkin ada beberapa kesalahan sintaks, tapi ini setidaknya gambaran umum)
#include <regex.h>
#include <string.h>
#include <vector.h>
using namespace std;
vector<string> split(string s){
regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
regex_iterator<string::iterator> rend; //iterators to iterate thru words
vector<string> result<regex_iterator>(rit, rend);
return result; //iterates through the matches to fill the vector
}
The stringstream dapat nyaman jika anda perlu untuk mengurai string dengan non-ruang simbol-simbol:
string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;
istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')
Sejauh ini saya menggunakan salah satu di Meningkatkan, tapi aku butuh sesuatu yang doesn't tergantung pada hal itu, jadi aku datang untuk ini:
static void Split(std::vector<std::string>& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
std::ostringstream word;
for (size_t n = 0; n < input.size(); ++n)
{
if (std::string::npos == separators.find(input[n]))
word << input[n];
else
{
if (!word.str().empty() || !remove_empty)
lst.push_back(word.str());
word.str("");
}
}
if (!word.str().empty() || !remove_empty)
lst.push_back(word.str());
}
Titik yang baik adalah bahwa dalam pemisah
anda dapat mengirimkan lebih dari satu karakter.