Replacing word in position X of a string C++ - c++

I am trying to write a function in a program that will take a string, a word and an integer and use the int as the index value and the word as the replacement value. For example, if the string is "This is a test.", the word is "example", and the number is 4, then the result would be "This is an example". This is what I have so far (I had to make multiple copies of the string because eventually, I am going to be passing it into two other functions by reference instead of as value)Right now it is using the character index instead of the word index in order to replace. How do I fix that?
#include "pch.h"
#include<iostream>
#include<string>
#include<sstream>
using namespace std;
int main()
{
string Input = "";
string Word = "";
int Number = 0;
cout << "Pleas enter a string using only lower case letters. \n";
getline(cin, Input);
cout << "Please enter a word using only lower case lettersS. \n";
getline(cin, Word);
cout << "Please enter a number. \n";
cin >> Number;
string StringCopy1 = Input;
string StringCopy2 = Input;
string StringCopy3 = Input;
}
void stringFunctionValue(string StringCopy1, int Number, string Word)
{
StringCopy1.replace(Number, Word.length, Word);
return StringCopy1;
}

First thing you have to do is find the nth word.
The first thing to come to mind is using std::istringstream to pull the string apart with >> and a std::ostringstream to write the new string.
std::istringstream in(StringCopy1);
std::string token;
std::ostringstream out;
int count = 0;
while (in >> token) // while we can get more tokens
{
if (++count != number) // not the number of the token to replace
{
out << token << " "; // write the token
}
else
{
out << word << " "; // write the replacement word
}
}
return out.str();
While this is easy to write, it has two problems: It loses the correct type of whitespace in the string AND places an extra space on the end of the string. It's also kind of slow and uses a lot more memory than if you modify the string in place.
Use std::string::find_first_not_of to find the first non-whitespace character. This will be the start of the first word. Then use std::string::find_first_of to find the next whitespace character. This will be the end of the word. Alternate back and forth finding non-whitespace then whitespace until you find the beginning and ending of the nth word. std::string::replace that word. This approach requires you to write more and more complicated code, but is much more satisfying. This is why I outlined it rather than fully implementing it: To allow you the joy for yourself.
Note: void stringFunctionValue(string StringCopy1, int Number, string Word) gives you no way to provide the result back to the user. This makes for an unhelpful function. Consider returning a string rather than void.

Related

The problem of analyzing a string and searching

I want to write code that takes a string of text from the user and shows the number of characters and the number of words using the .find () function. then takes a word from user and Search the text and show the position of the word. I'm in trouble now, please help me.
#include<iostream>
#include <cctype>
#include<string>
#include<cstring>
using namespace std;
int main()
{ char quit;
int word=0;
string txt;
cout << "Enter a string: ";
getline(cin, txt);
cout << "The number of characters in the string is:" << txt.length() << endl;
while(string txt != NULL)
{ if(txt.find(" "))
++word;
}
cout<<"wors is "<<word;
while(quit!='q')
{
cout<<"wors is ";
cin>>search;
cout<<"Enter(c)if you want to continue, and enter(q)if you want quic:";
cin>>quit;
}
return 0;
}
Here's an example of extracting words. There are many other methods.
static const char end_of_word_chars[] = "!?., :\t";
//...
std::string::size_type previous_position = 0;
std::string::size_type position = txt.find_first_of(end_of_word_chars);
while (position != std::string::npos)
{
std::string word = txt.substr(previous_position, position - previous_position);
std::cout << word << "\n";
previous_position = txt.find_first_of(position + 1);
position = txt.find_first_not_of(end_of_word_chars);
}
The above code uses an array of "end of word characters", to denote the end of a word. The string txt is searched from the beginning to find the position of the first character that is in the set of word endinging characters. In the while loop, the spaces or non-word characters are skipped. And the position of the next "word ending" character is found and the loop may repeat again.
Edit 1: String as stream
Another method is to treat the txt as a string stream and use operator>> to skip whitespace:
std::istringstream text_stream(txt);
std::string word;
while (text_stream >> word)
{
std::cout << word << "\n";
}
One issue with the above code fragment is that it doesn't account for word ending characters that are not spaces or tabs. So for example, in the text "Yes. I'm Home.", the period is included as part of the "word", such as "Yes." and "Home."

How to count how many words are in line?Smarter way?

How to find out how many words are in line? I now that method where you count how many there are spaces. But what if someone hit 2 spaces or start line with space.
Is there any other or smarter way to solve this?
And is there any remark on my way of solving it or my code?
I solved it like this:
#include <iostream>
#include <cctype>
#include <cstring>
using namespace std;
int main( )
{
char str[80];
cout << "Enter a string: ";
cin.getline(str,80);
int len;
len=strlen(str);
int words = 0;
for(int i = 0; str[i] != '\0'; i++) //is space after character
{
if (isalpha(str[i]))
{
if(isspace(str[i+1]))
words++;
}
}
if(isalpha(str[len]))
{
words++;
}
cout << "The number of words = " << words+1 << endl;
return 0;
}
The std one-liner is:
words= distance(istream_iterator<string>(istringstream(str)), istream_iterator<string>());
streams by default skip spaces (multiple also).
So if you do something like:
string word;
int numWords = 0;
while (cin >> word) ++numWords;
That should count the number of words for simple cases (not considering what the format of a word is, skipping spaces).
If you want per line, you could read first the line, create a stream from a string, and do a similar thing like this:
string line, word;
int wordCount = 0;
getline(cin, line);
stringstream lineStream(line);
while (lineStream >> word) ++wordCount;
You should not use cin.getline and should prefer the free function std::getline, which takes a string that can be grown up and prevents stack overflows (lol). Stick to the free function for better safety.
First, you need a very specific definition of "word." Most of the answers will give slightly different counts than your attempt because you're using different definitions of what constitutes a word. Your example specifically requires alpha characters in certain positions. The answers based on streams will allow any non-space character to be part of a word.
The general solution is to come up with a precise definition of a word, transform this into a regular expression or finite state machine, and then count each instance of a match.
Here's a sample state machine solution:
std::size_t CountWords(const std::string &line) {
std::size_t count = 0;
enum { between_words, in_word } state = between_words;
for (const auto c : line) {
switch (state) {
case between_words:
if (std::isalpha(c)) {
state = in_word;
++count;
}
break;
case in_word:
if (std::isspace(c)) state = between_words;
break;
}
}
return count;
}
Some test cases to consider (and that highlight the differences among the definitions of a word):
"" empty string
" " just spaces
"a"
" one "
"count two"
"hyphenated-word"
"\"That's Crazy!\" she said." punctuation between alpha characters and adjacent spaces
"the answer is 42" should the number count as a word?

How to remove double quotation marks and comma in the output when I input space for the first string (C++)

I am trying to write a function to split the string and return it like a substring. The code is worked. I just meet a question: how to remove double quotation marks and comma in the output when I input space for the first string? Any help is appreciated! Thank you!
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <ctype.h>
using namespace std;
vector<string> split(string targer, string delimiter);
int main()
{
string s, delimiter;
vector<string> tokens;
cout << "Enter string to split:" << endl;
getline (cin,s);
cout << "Enter delimiter string:" << endl;
getline (cin,delimiter);
tokens = split(s, delimiter);
cout << "The substrings are: ";
for(int i = 0; i < tokens.size() - 1; i++)
{
if (tokens[i].length() != 0){
cout << "\"" << tokens[i] << "\"" << "," << " ";
}
}
if (tokens.size() != 0)
{
cout << "\"" << tokens[tokens.size() - 1] << "\"";
}
cout<<endl;
return 0;
}
vector<string> split(string target, string delimiter){
stringstream ss(target);
string item;
vector<string> tokens;
while (getline(ss, item, delimiter.at(0))) {
tokens.push_back(item);
}
return tokens;
}
Your issue is not "how to remove double quotation marks and comma in the output when I input space for the first string".
Your issue is correctly splitting the string, so that a quoted string that contains spaces gets correctly extracted as a single string.
The simple methods offered by the I/O library to parse input up until the next delimiting character is not sufficient in order to be able to handle splitting a string, in this manner, on its own. They don't know anything about quotes. You will have to do the job yourself:
Scan the input string one at a time, using the following logic.
If the current character is a delimiting character, continue to the next character.
If the current character is a quote, then continue to scan until the next quote character is seen, then extract everything between the quotes into a single word.
Otherwise, continue to scan until the next delimiting character is seen, and then extract everything until that point into a single word.
This is a basic high-level outline of a typical splitting algorithm. You will end up with individual extracted words, with quoted content as a single word. You can take this high-level overview, and rewrite it as a lower-level, more detailed algorithm, then explain it to your rubber duck. After your rubber duck agrees that your detailed algorithm will work, you can then translate it directly into code.
Once you have implemented this correctly, you can refine this further to allow the quote characters themselves to be included in a quoted word. Typically that's done by using either two consecutive quotes, in a row, or a backslash "escape" character.

When parsing a string using a string stream, it extracts a new line character

Description of the program : The program must read in a variable amount of words until a sentinel value is specified ("#" in this case). It stores the words in a vector array.
Problem : I use a getline to read in the string and parse the string with a stringstream. My problem is that the stringstream is not swallowing the new line character at the end of each line and is instead extracting it.
Some solutions I have thought of is to cut off the last character by creating a subset or checking if the next extracted word is a new line character, but I feel there is a better cost efficient solution such as changing the conditions for my loops.
I have included a minimized version of the overall code that reproduces the problem.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
const int MAX_LIST_SIZE = 1000;
string str;
string list[MAX_LIST_SIZE];
int numWords = 0;
// program starts here
getline(cin, str); // read innput
stringstream parse(str); // use stringstream to parse input
while(str != "#") // read in until sentinel value
{
while(!parse.fail()) // until all words are extracted from the line
{
parse >> list[numWords]; // store words
numWords++;
}
getline(cin,str); // get next line
parse.clear();
parse.str(str);
}
// print number of words
cout << "Number of words : " << numWords << endl;
}
And a set of test input data that will produce the problem
Input:
apples oranges mangos
bananas
pineapples strawberries
Output:
Number of words : 9
Expected Output:
Number of words : 6
I would appreciate any suggestions on how to deal with this problem in an efficient manner.
Your logic for parsing out the stream isn't quite correct. fail() only becomes true after a >> operation fails, so you'll doing an extra increment each time. For example:
while(!parse.fail())
{
parse >> list[numWords]; // fails
numWords++; // increment numWords anyway
} // THEN check !fail(), but we incremented already!
All of these operations have returns that you should check as you go to avoid this problem:
while (getline(cin, str)) { // fails if no more lines in cin
if (str != "#") { // doesn't need to be a while
stringstream parse(str);
while (parse >> list[numWords]) { // fails if no more words
++numWords; // *only* increment if we got one!
}
}
}
Even better would be to not use an array at all for the list of words:
std::vector<std::string> words;
Which can be used in the inner loop:
std::string temp;
while (parse >> temp) {
words.push_back(temp);
}
The increment on numwords happens one more time than you intend at the end of each line. Use a std::vector< std::string > for your list. Then you can use list.size().

C++/ ofstream with only a string type

Here is my code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
string nom_fich("data.dat");
ofstream fichier(nom_fich.c_str());
string name;
cout <<"The name is: "<< name ;
cin>>ws;
if (getline(cin, name)){
fichier << name <<endl;
} else {
cerr <<"Error!";
}
fichier.close();
return 0;
}
Question: why if I enter a number instead of a string my program doesn't say me "Error!" ?
EDIT: how can I attempt to my purpose ? I want that get an "Error!" when I enter a type that isn't a type string.
Because a number is a valid string.
Numbers can be represented as strings, i.e. strings can contain digit characters. For example:
std::string my_string("42");
std::cout << my_string[0]; // prints 4
std::cout << my_string[1]; // prints 2
You can't enter a number. You can enter a sequence of characters that can be interpreted as a number, but that sequence is still characters. Those characters arw what getline reads.
A number can be represented as a string, so std::ifstream::operator >> takes the intuitive approach: it treats any sequence of non-blank characters as a string. This includes decimal digits as well.
Unrelated, but instead of creating a superfluous nom_fich temporary variable for the name, you could just write ofstream fichier("data.dat");.
Because the string "123" is just as valid as the string "abc", or the string "def999".
As for "how can I attempt to my purpose?", you'd have to explain to us what your purpose is because, by your own admission, your own code does not describe that purpose and therefore we cannot extract your purpose from it.
Reading a number as a string will certainly work: the number is just represented as a sequence of characters. If you want the stream to attempt reading a number and fail if it doesn't get one, you'd use a different type to read, e.g.:
int number;
if (std::cin >> number) {
std::cout << "ERROR: read a number: " << number << '\n';
}
else if (std::cin.clear(), std::getline(std::cin, name)) {
std::cout << "read a name: " << name << '\n';
}
After the unsuccessful read of a number the stream's state is clear()ed and, instead, it is attempted to read a name. There is a subtle issue as the formatted input for an int will skip leading whitespace while std::getline() doesn't skip whitespace. If skipping leading whitespace is a problem just use the manipulator std::noskipws before trying to read an int.