How can I erase first and last character in string? - c++

This function returns an array of strings with a list of files in a folder. It looks like this:
"folder//xyz.txt"
How can I make it look like this?
folder//xyz.txt
Its the same but without "".
vector<string> list_of_files(string folder_name)
{
vector<string> files;
string path = folder_name;
for (const auto& entry : fs::directory_iterator(path))
{
stringstream ss;
ss << entry.path(); //convert entry.path() to string
string str = ss.str();
files.push_back(ss.str());
}
return files;
}

Erasing the first and last characters of a string is easy:
if (str.size() >= 1)
str.erase(0, 1); // from 1st char (#0), len 1; bit verbose as not designed for this
if (str.size() >= 1)
str.pop_back(); // chop off the end
Your quotes have come from inserting the path to a stream (quoted is used to help prevent bugs due to spaces down the line).
Fortunately, you don't need any of this: as explored in the comments, the stringstream is entirely unnecessary; the path already converts to a string if you ask it to:
vector<string> list_of_files(string folder_name)
{
vector<string> files;
for (const auto& entry : fs::directory_iterator(folder_name))
files.push_back(entry.path().string());
return files;
}

Related

How to replace multiple sets of keywords in a string?

So I have a file of strings that I am reading in, and I have to replace certain values in them with other values. The amount of possible replacements is variable. As in, it reads the patterns to replace with in from a file. Currently I'm storing in a vector<pair<string,string>> for the patterns to find and match. However I run into issues:
Example:
Input string: abcd.eaef%afas&333
Delimiter patterns:
. %%%
% ###
& ###
Output I want: abcd%%%eaef###afas###333
Output I get: abcd#########eaef###afas###333
The issue being it ends up replacing the % sign or any other symbol that was already a replacement for something else, it should not be doing that.
My code is (relevant portions):
std::string& replace(std::string& s, const std::string& from, const std::string& to){
if(!from.empty())
for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size()) s.replace(pos, from.size(), to);
return s;
}
string line;
vector<pair<string, string>> myset;
while(getline(delimiterfile, line)){
istringstream is(line);
string delim, pattern;
if(is >> delim >> pattern){
myset.push_back(make_pair(delim, pattern));
} else {
throw runtime_error("Invalid pattern pair!");
}
}
while(getline(input, line)){
string temp = line;
for(auto &item : myset){
replace(temp, item.first, item.second);
}
output << temp << endl;
}
Can someone please tell me what I'm messing up and how to fix it?
In pseudo-code a simple replacement algorithm could look something like this:
string input = getline();
string output; // The string containing the replacements
for (each char in input)
{
if (char == '.')
output += "%%%";
// TODO: Other replacements
else
output += char;
}
If you implement the above code, once it's done the variable output will contain the string with all replacements made.
I would suggest you use stringstream. This way you will be able to achieve what you are looking for very easily.

Reading a iostream until a string delimiter is found

I currently have a function that reads from a stream until a predefined stream-stopper is found. The only way I could currently get it up and running is by using std::getline and having one character followed by a newline (in my case char(3)) as my stream-stopper.
std::string readuntil(std::istream& in) {
std::string text;
std::getline(in, text, char(3));
return text;
}
Is there any way to achieve the same but with a larger string as my stream-stopper? I don't mind it having to be followed by a new-line, but I want my delimiter to be a random string of some size so that the probability of it occurring by change in the stream is very very low.
Any idea how to achieve this?
I assume that your requirements are:
a function taking an istream ref and a string as parameter
the string is a delimiter and the function must return a string containing all the characters that arrived before it
the stream must be positioned immediately after the delimiter for further processing.
AFAIK, neither the C++ nor the C standard library contain a function for that. I would just:
read until the last character of the delimiter in a temporary string
accumulate that in a global string
iterate the 2 above actions if the global string does not end with the delimiter
optionaly remove the delimiter from the end of the global string
return the global string
A possible C++ implementation is:
std::string readuntil(std::istream& in, std::string delimiter) {
std::string cr;
char delim = *(delimiter.rbegin());
size_t sz = delimiter.size(), tot;
do {
std::string temp;
std::getline(in, temp, delim);
cr += temp + delim;
tot = cr.size();
} while ((tot < sz) || (cr.substr(tot - sz, sz) != delimiter));
return cr.substr(0, tot - sz); // or return cr; if you want to keep the delimiter
}

std::string returning inappropriate value

I wrote a program which perform string compression using counts of repeated characters. The program in C++ is :
#include<iostream>
#include<cstring>
std::string compressBad(std::string str)
{
std::string mystr = "";
int count = 1;
char last = str[0];
for (int i = 0; i < str.length();++i)
{
if(str[i] == last)
count++;
else
{
std::string lastS = last+"";
std::string countS = std::to_string(count);
mystr.append(lastS);
mystr.append(countS);
//mystr = mystr + last + count;
count = 1;
last = str[i];
}
}
std::string lastS = last+"";
std::string countS = std::to_string(count);
mystr.append(lastS);
mystr.append(countS);
return mystr;
//return mystr+last+count;
}
int main()
{
std::string str;
std::getline(std::cin, str);
std::string str2 = compressBad(str);
std::cout<<str2;
/*if (str.length() < str2.length())
std::cout<<str;
else
std::cout<<str2;*/
std::cout<<std::endl;
return 0;
}
Few example on running this are :
Input : sssaaddddd
Output : ùÿÿ*425
Output it should print : s3a2d5
Second example:
Input : sssaaddd
Output: ùÿÿ*423
Output it should print : s3a2d3
I also implemented the same concept in Java and there it is working fine. The java implementation is here
Why is this problem happening with above code.
There may be other issues in your code, but I think that this line might be to blame:
std::string lastS = last+"";
Here, you're trying to convert the character last to a string by concatenating the empty string to the end. Unfortunately, in C++ this is interpreted to mean "take the numeric value of the character last, then add that to a pointer that points to the empty string, producing a new pointer to a character." This pointer points into random memory, hence the garbage you're seeing. (Notice that this is quite different from how Java works!)
Try changing this line to read
std::string lastS(1, last);
This will initialize lastS to be a string consisting of just the character stored in last.
Another option would be to use an ostringstream:
std::ostringstream myStr;
myStr << last << count;
// ...
return myStr.str();
This eliminates all the calls to .append() and std::to_string and is probably a lot easier to read.
last + "" doesn't do what you think.
just do
mystr.append(1, last);

How to split vector<char> into strings using delimeter

I have a vector of characters which contains some words delimited by comma.
I need to separate text by words and add those words to a list.
Thanks.
vector<char> text;
list<string> words;
I think I'd do it something like this:
while ((stop=std::find(start, text.end(), ',')) != text.end()) {
words.push_back(std::string(start, stop));
start = stop+1;
}
words.push_back(std::string(start, text.end()));
Edit: That said, I have to point out that the requirement seems a bit odd -- why are you starting with a std::vector<char>? A std::string would be much more common.
vector<char> text = ...;
list<string> words;
ostringstream s;
for (auto c : text)
if (c == ',')
{
words.push_back(s.str());
s.str("");
}
else
s.put(c);
words.push_back(s.str());
Try to code this simple psuedocode and see how it goes
string tmp;
for i = 0 to text.size
if text[i] != ','
insert text[i] to tmp via push_back
else add tmp to words via push_back and clear out tmp

how to remove substring from string c++

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