Ubuntu Suomen keskustelualueet
Ubuntun käyttö => Ohjelmointi, palvelimet ja muu edistyneempi käyttö => Aiheen aloitti: teele - 07.01.23 - klo:20.30
-
Haluaisin saada merkkijonon, jossa on välilyönnein eroteltuja sanoja luetuksi vector-luokan säiliöön.
Toivomus olisi, että homma toimii aika turvallisesti ilman ylivuotoja tai muita ongelmia, vaikka tulevamerkkijono olisi vähän omituisempikin.
Nykyinen versioni on tällainen
std::vector<std::string> str_to_vec(char* cptr)
{
std::vector<std::string> vec;
std::string tok = "";
while( *cptr != '\0')
{
if(*cptr == ' ' && tok != "")
{
vec.push_back(trim(tok)); // tok valmis ' '-merkki päätti sen
tok = "";
cptr ++;
}
else
{
tok += *cptr;
cptr++;
if( *cptr == '\0') // tok valmis \n -merkki päätti sen
{
vec.push_back(trim(tok));
}
}
}
return vec;
}
Jotta vektoriin ei tule luetuksi välilyöntejä, funktiossa on trim-käsky, joka on toteutettu näin
std::string ltrim(const std::string &s) {
return std::regex_replace(s, std::regex("^\\s+"), std::string(""));
}
std::string rtrim(const std::string &s) {
return std::regex_replace(s, std::regex("\\s+$"), std::string(""));
}
std::string trim(const std::string &s) {
return ltrim(rtrim(s));
}
Kokonaisuus ei vaikuta kovin tyylikkäältä, kun kyseessä on vain aika yksinkertainen vektoritalletus ja sanojen poiminta.
Olisiko joitain keinoja, miten asian voisi hoitaa varmemmin ja tyylikkäämmin. (esim.nyt kai ainakin pitäisi varautua siihen, että merkkijonon loppumerkkiä ei koskaan tulisikaa, ja varmaan moneen muuhunkin mahdolliseen ongelmaan)
-
cplusplus.comissa on varsin hyvä katsaus tähän ongelmaan, kuten jo edellisessä ketjussa (https://forum.ubuntu-fi.org/index.php?topic=57619.0) mainitsin: https://cplusplus.com/faq/sequences/strings/split/
Suosittelen jotain tuolla mainittua menetelmää. Jos haluat erotella sanat kaikilla whitespace-merkeillä ja mahdollisesti ohittaa tyhjät sanat (tai toistuvat välimerkit), sivulla esitetty string::find_first_of() -funktioon perustuva menetelmä on fiksu STL-pohjainen ratkaisu.
Jos sovelluksessa on kuitenkin jo muista syistä riippuvuuksina Boost tai Qt, kannattaa käyttää niiden tarjoamia split-funktioita.
-
Yritin vielä tällaista tavisversiota
#include <string>
#include<vector>
#include <iostream>
std::vector<std::string> ct_to_st(char *p) //char tokens to string tokens
{
std::vector<std::string> sv;
std::string s = "";
while(*p != '\0' ) // mitään ei lisäillä, jos merkkijono on loppu
{
while(*p != '\0' && *p == ' ' )
{
p++;
} // while
while(*p != '\0' && *p != ' ' )
{
s.push_back(*p);
p++;
} // while
sv.push_back(s);
s = "";
} // while
return sv;
}
int main(int argc, char **argv )
{
if(argc != 2)
{
std::cout << "ohjelmanimi merkkijono" << std::endl;
return 1;
}
std::vector<std::string> sv = ct_to_st(argv[1]); // eka ohjelmaparametri on merkkijono
for( auto e : sv)
{
std::cout << e << " " ;
}
std::cout << std::endl;
return 1;
}
Yllättäen kokeilu antoi
$ ./re-koe_03 'xxä yyy bb '
xxä yyy bb
$ ./re-koe_03 ' xxä yyy bb '
xxä yyy bb
$ ./re-koe_03 'ääåå k m yyy bb '
ääåå k m yyy bb
$ ./re-koe_03 äää
äää
$ ./re-koe_03 ' '
$
mistä päättelin, että voisi toimiakin. Pelkkä välilyönti riittää erotinmerkiksi, niin string::find_first_of() -funktion laajempaa valikoimaa ei vielä tarvittane.