I have a string s="home/dir/folder/name"
I want to split s
in s1="home/dir/folder"
and s2="name";
I did:
char *token = strtok( const_cast<char*>(s.c_str() ), "/" );
std::string name;
std::vector<int> values;
while ( token != NULL )
{
name=token;
token = strtok( NULL, "/"开发者_如何学运维 );
}
now s1=name
. What about s2
?
I'd recommend against using strtok
. Take a look at Boost Tokenizer instead (here are some examples).
Alternatively, to simply find the position of the last '/'
, you could use std::string::rfind
:
#include <string>
#include <iostream>
int main() {
std::string s = "home/dir/folder/name";
std::string::size_type p = s.rfind('/');
if (p == std::string::npos) {
std::cerr << "s contains no forward slashes" << std::endl;
} else {
std::string s1(s, 0, p);
std::string s2(s, p + 1);
std::cout << "s1=[" << s1 << "]" << std::endl;
std::cout << "s2=[" << s2 << "]" << std::endl;
}
return 0;
}
If your goal is only to get the position of the last \
or /
in your string
, you might use string::find_last_of which does exactly that.
From there, you can use string::substr or the constructor for std::string
that takes iterators to get the sub-part you want.
Just make sure the original string contains at least a \
or /
, or that you handle the case properly.
Here is a function that does what you need and returns a pair
containing the two parts of the path. If the specified path does not contain any \
or /
characters, the whole path is returned as a second member of the pair and the first member is empty. If the path ends with a /
or \
, the second member is empty.
using std::pair;
using std::string;
pair<string, string> extract_filename(const std::string& path)
{
const string::size_type p = path.find_last_of("/\\");
// No separator: a string like "filename" is assumed.
if (p == string::npos)
return pair<string, string>("", path);
// Ends with a separator: a string like "my/path/" is assumed.
if (p == path.size())
return pair<string, string(path.substr(0, p), "");
// A string like "my/path/filename" is assumed.
return pair<string, string>(path.substr(0, p), path.substr(p + 1));
}
Of course you might as well modify this function to throw an error instead of gracefully exiting when the path does not have the expected format.
Several points: first, your use of strtok
is undefined behavior; in
the case of g++, it could even lead to some very strange behavior. You
cannot modify the contents of an std::string
behind the strings back
and expect to get away with it. (The necessity of a const_cast
should
have tipped you off.)
Secondly, if you're going to be manipulating filenames, I'd strongly
recommend boost::filesystem
. It knows all about things like path
separators and the like, and the fact that the last component of a path
is generally special (since it may be a filename, and not a directory).
Thirdly, if this is just a one-of, or for some other reason you can't or don't want to use Boost:
std::string::const_iterator pivot
= std::find( s.rbegin(), s.rend(), '/' ).base();
will give you an iterator to the first character after the last '/', or to the first character in the string if there isn't one. After that, it's a simple to use the two iterator constructors of string to get the two components:
std::string basename( pivot, s.end() );
std::string dirname( s.begin(), pivot == s.begin() ? pivot : pivot - 1 );
And if you later have to support Windows, just replace the find
with:
static std::string const dirSeparators( "\\/" );
std::string::const_iterator pivot
= std::find_first_of( s.rbegin(), s.rend(),
dirSeparators.begin(), dirSeparators.end() );
Check out boost string split.
Example:
string str1("hello abc-*-ABC-*-aBc goodbye");
typedef vector< iterator_range<string::iterator> > find_vector_type;
find_vector_type FindVec; // #1: Search for separators
ifind_all( FindVec, str1, "abc" ); // FindVec == { [abc],[ABC],[aBc] }
typedef vector< string > split_vector_type;
split_vector_type SplitVec; // #2: Search for tokens
split( SplitVec, str1, is_any_of("-*"), token_compress_on );
// SplitVec == { "hello abc","ABC","aBc goodbye" }
You can't use strtok
on std::string
.
strtok
would modify the string. It break the c_str()
contract.
Doing const_cast<>
is a big sign for error.
Just use the string methods:
std::string s="home/dir/folder/name"
std::string::size_type n = s.find_last_of("/");
std::string s1 = s.substr(0,n);
if (n != std::string::npos) // increment past the '/' if we found it
{ ++n;
}
std::string s2 = s.substr(n);
Two bits of advice:
- Don't use strtok EVER
- If you are playing with file system paths look at boost::filesystem
- If you want to play generally with tokenization use the stream operators
- Or boost::tokenizer
精彩评论