Find second to last word in a string C++ - c++

Hi I need to find second to last word in a string. Right now below program is printing the last one.
#include <iostream>
#include <string>
using namespace std;
int main() {
string text{"some line with text"};
// find last space, counting from backwards
int i = text.length() - 2; // last character
while (i != 0 && !isspace(text[i]))
{
--i;
}
string lastword = text.substr(i+1); // +1 to skip leading space
cout << lastword << endl;
return 0;
}
Output: (Printing last word)
text

You can split the string into words and hold the previous word before saving the current word.
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::string text{"some line with text"};
std::stringstream ss(text);
std::string previousword, lastword, newword;
while (ss >> newword) {
previousword = lastword;
lastword = newword;
}
std::cout << previousword << std::endl;
return 0;
}
Also note that using using namespace std; is discouraged.

You don't need any loops. Just add error checking:
int main() {
std::string text{ "some line with text" };
std::size_t pos2 = text.rfind(' ');
std::size_t pos1 = text.rfind(' ', pos2-1);
std::cout << text.substr(pos1+1, pos2-pos1-1) << std::endl;
return 0;
}

Just keep counting spaces until you arrive to the word you want or use a stringstream as MikeCAT proposed.
Here there is a function that finds any last word number without having to copy the entire string in a stringstream:
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
string getNLastWord(string text, int n)
{
bool insideAWord = false;
int wordNum = 0;
int wordEnd = -1;
for(int i = text.size() - 1; i > 0; i--)
{
if(text[i] != ' ' && !insideAWord)
{
wordNum++;
insideAWord = true;
}
else if(text[i] == ' ')
{
insideAWord = false;
}
if(wordNum == n)
{
wordEnd = i;
break;
}
}
if(wordEnd == -1)
{
cout << "There are no " << n << " words from right." << endl;
}
else
{
int wordStart;
for(wordStart = wordEnd; wordStart > 0; wordStart--)
{
if(text[wordStart] == ' ')
{
wordStart++;
break;
}
}
return text.substr(wordStart,wordEnd+1-wordStart);
}
return "";
}
int main() {
string text = "some text";
cout << getNLastWord(text,2);
return 0;
}

Related

How do I remove repeated words from a string and only show it once with their wordcount

Basically, I have to show each word with their count but repeated words show up again in my program.
How do I remove them by using loops or should I use 2d arrays to store both the word and count?
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <conio.h>
#include <time.h>
using namespace std;
char* getstring();
void xyz(char*);
void tokenizing(char*);
int main()
{
char* pa = getstring();
xyz(pa);
tokenizing(pa);
_getch();
}
char* getstring()
{
static char pa[100];
cout << "Enter a paragraph: " << endl;
cin.getline(pa, 1000, '#');
return pa;
}
void xyz(char* pa)
{
cout << pa << endl;
}
void tokenizing(char* pa)
{
char sepa[] = " ,.\n\t";
char* token;
char* nexttoken;
int size = strlen(pa);
token = strtok_s(pa, sepa, &nexttoken);
while (token != NULL) {
int wordcount = 0;
if (token != NULL) {
int sizex = strlen(token);
//char** fin;
int j;
for (int i = 0; i <= size; i++) {
for (j = 0; j < sizex; j++) {
if (pa[i + j] != token[j]) {
break;
}
}
if (j == sizex) {
wordcount++;
}
}
//for (int w = 0; w < size; w++)
//fin[w] = token;
//cout << fin[w];
cout << token;
cout << " " << wordcount << "\n";
}
token = strtok_s(NULL, sepa, &nexttoken);
}
}
This is the output I get:
I want to show, for example, the word "i" once with its count of 5, and then not show it again.
First of all, since you are using c++, I would recommend you to split text in c++ way(some examples are here), and store every word in map or unordered_map. Example of my realization you can find here
But if you don't want to rewrite your code, you can simply add a variable that will indicate whether a copy of the word was found before or after the word position. If a copy was not found in front, then print your word
This post gives an example to save each word from your 'strtok' function into a vector of string. Then, use string.compare to have each word compared with word[0]. Those indexes match with word[0] are marked in an int array 'used'. The count of match equals to the number marks in the array used ('nused'). Those words of marked are then removed from the vector, and the remaining carries on to the next comparing process. The program ends when no word remained.
You may write a word comparing function to replace 'str.compare(str2)', if you prefer not to use std::vector and std::string.
#include <iostream>
#include <string>
#include <vector>
#include<iomanip>
#include<cstring>
using namespace std;
char* getstring();
void xyz(char*);
void tokenizing(char*);
int main()
{
char* pa = getstring();
xyz(pa);
tokenizing(pa);
}
char* getstring()
{
static char pa[100] = "this is a test and is a test and is test.";
return pa;
}
void xyz(char* pa)
{
cout << pa << endl;
}
void tokenizing(char* pa)
{
char sepa[] = " ,.\n\t";
char* token;
char* nexttoken;
std::vector<std::string> word;
int used[64];
std::string tok;
int nword = 0, nsize, nused;
int size = strlen(pa);
token = strtok_s(pa, sepa, &nexttoken);
while (token)
{
word.push_back(token);
++nword;
token = strtok_s(NULL, sepa, &nexttoken);
}
for (int i = 0; i<nword; i++) std::cout << word[i] << std::endl;
std::cout << "total " << nword << " words.\n" << std::endl;
nsize = nword;
while (nsize > 0)
{
nused = 0;
tok = word[0] ;
used[nused++] = 0;
for (int i=1; i<nsize; i++)
{
if ( tok.compare(word[i]) == 0 )
{
used[nused++] = i; }
}
std::cout << tok << " : " << nused << std::endl;
for (int i=nused-1; i>=0; --i)
{
for (int j=used[i]; j<(nsize+i-nused); j++) word[j] = word[j+1];
}
nsize -= nused;
}
}
Notice that the removal of used words has to do in backward order. If you do it in sequential order, the marked indexes in the 'used' array will need to be changed. A running test:
$ ./a.out
this is a test and is a test and is test.
this
is
a
test
and
is
a
test
and
is
test
total 11 words.
this : 1
is : 3
a : 2
test : 3
and : 2
I read your last comment.
But I am very sorry, I do not know C. So, I will answer in C++.
But anyway, I will answer with the C++ standard approach. That is usually only 10 lines of code . . .
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <regex>
// Regex Helpers
// Regex to find a word
static const std::regex reWord{ R"(\w+)" };
// Result of search for one word in the string
static std::smatch smWord;
int main() {
std::cout << "\nPlease enter text: \n";
if (std::string line; std::getline(std::cin, line)) {
// Words and its appearance count
std::map<std::string, int> words{};
// Count the words
for (std::string s{ line }; std::regex_search(s, smWord, reWord); s = smWord.suffix())
words[smWord[0]]++;
// Show result
for (const auto& [word, count] : words) std::cout << word << "\t\t--> " << count << '\n';
}
return 0;
}

Trying to use getline (string) in C++ to parse a kml file

I'm using getline() to parse coordinate lines from a kml file into a vector.
The relevant part of the kml file looks like this (replaced numbers with x)...
<coordinates>
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
-xxxxxxxxx,xxxxxxxxx,0
</coordinates>
When using getline() to print the kml file line by line it does it just fine so I figured something like this would work to parse coordinates into a vector...
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
using namespace std;
vector <string> coordinates;
int main() {
fstream inputFile("Fish.kml", fstream::in);
string str;
bool running = true;
int counter = 0;
while (running) {
getline(inputFile, str, '\0');
if (str == " <coordinates>") {
counter++;
}
if (counter > 0 && str != " </coordinates>") {
coordinates.push_back(str);
}
if (counter > 0 && str == " </coordinates>") {
counter = -1;
running = false;
}
}
inputFile.close();
for (int i = 0; i < coordinates.size(); i++) {
cout << coordinates[i] << "\n";
}
return 0;
}
My thinking was to use if statements to check whether the getline() function is about to read the coordinates by checking if the line, "coordinates", has been read or not. If it has it will increase the counter to above zero that way in the next loop the rest of the program knows to start logging coordinates. Same thinking applies to stopping the code by checking to see if the line "/coordinates" has been read. It's all I could think of with my current knowledge, but obviously I must be missing something important because...
When I compile and run I don't get any errors but the program doesn't do anything. This is what it looks like, It does nothing, my computer fans kick on and I have to cancel the process...
irectory>test.exe
^C
Any ideas what is going on here?
Thank you!
Update:
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
using namespace std;
vector <string> coordinates;
int main() {
fstream inputFile("Fish.kml", fstream::in);
string str;
bool running = true;
int counter = 0;
while (getline(inputFile, str, '\0')) {
if (str == " <coordinates>") {
counter++;
}
if (counter > 0 && str != " </coordinates>") {
coordinates.push_back(str);
}
if (counter > 0 && str == " </coordinates>") {
counter = -1;
inputFile.close();
}
}
for (int i = 0; i < coordinates.size(); i++) {
cout << coordinates[i] << "\n";
}
return 0;
}
I updated the code above as suggested and it does not get stuck while executing anymore. But it does not print the vector back. I'm not sure if it is failing to record the lines of coordinates or if I didn't print it properly.
replace while(running) with while(getline(inputFile, str) and remove running variable as it is no longer needed.
And also
if (str == "<coordinates>") {
counter++;
}
if (counter > 0 && str != "</coordinates>") {
coordinates.push_back(str);
}
if (counter > 0 && str == "</coordinates>") {
counter = -1;
}
Hopefully the following code would help you a little bit. I worte it myself. It only reads the first coordinates part.
//
// Created by albert on 4/1/22.
//
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
using namespace std;
vector<float> split(string s, string del) {
int start = 0;
int end = s.find(del, start);
vector<float> ret;
while (end != string::npos) {
string substr = s.substr(start, end - start);
if (!substr.empty())
ret.push_back(stof(substr));
start = end + del.size();
end = s.find(del, start);
}
string substr = s.substr(start, end - start);
if (!substr.empty())
ret.push_back(stof(substr));
return ret;
}
int main() {
fstream istream;
istream.open("map.kml", fstream::in);
vector<vector<float>> coordinates;
bool flag = false;
if (istream.is_open()) {
cout << "file is open" << endl;
string str;
while (getline(istream, str, '\n')) {
if (str == " <coordinates>")
flag = true;
else if (str == " </coordinates>") {
flag = false;
break;
} else if (flag)
coordinates.push_back(split(str, string(1, ',')));
}
}
istream.close();
for (int i = 0; i < coordinates.size(); i++) {
cout << coordinates[i][0] << " " << coordinates[i][1] << " " << coordinates[i][2] << "\n";
}
cout << coordinates.size() << endl;
return 0;
}

C++ Decrease value every time string passes

I'm struggling to find a way to decrease the value in a string every time the string is shown.
Using the code below, consider that the 1st line of the text file is some text #N. #N should be replaced by a number decreasing from 18 to 1. When it reaches 0 it should go back to 18.
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
void find_and_replace(string & source, string const & find, string const & replace)
{
for (string::size_type i = 0; (i = source.find(find, i)) != string::npos;) {
source.replace(i, find.length(), replace);
i += replace.length();
}
}
int main(int argc, char * argv[])
{
std::ifstream fileIn("Answers.txt", std::ios::in | std::ios::binary);
string question;
string line;
if (!fileIn) {
cout << "Cannot open input file!" << endl;
return 1;
}
while (getline(fileIn, line)) {
if (line == "The answer can be found in a secret place in the woods.") {
fileIn.clear();
fileIn.seekg(0, ios::beg);
}
cout << "Ask a question followed by the Enter key. Or type 'exit' to Exit program.\n";
getline(cin, question);
system("CLS");
find_and_replace(line, "#N", "18");
if (question == "") {
cout << "Your input cannot be blank. Please try again.\n\n";
}
else if (question == "exit")
exit(0);
else {
cout << "Q: " + question
<< "\nA: " + line + "\n\n";
}
}
}
This code only changes #N to 18, nothing more.
Please help guys.
You have hardcoded the value to 18, and you don't have any code which decrements the number.
Try these changes
put this at the start of main
int tempVar=18;
char buffer[100];
and replace
find_and_replace(line, "#N", "18");
with
sprintf(buffer,"%d",tempVar--)
if(tempVar<0)
tempVar=18;
find_and_replace(line, "#N", buffer);
https://www.programiz.com/cpp-programming/library-function/cstdio/sprintf
You can use something like:
#include <sstream>
#include <string>
class Replacer
{
const std::string token_;
const int start_;
int current_;
public:
explicit Replacer(const std::string & token, int start)
: token_(token), start_(start), current_(start)
{
}
std::string replace(const std::string & str)
{
const std::size_t pos = str.find(token_);
if (pos == std::string::npos)
return str;
std::string ret(str);
std::ostringstream oss;
oss << current_;
ret.replace(pos, token_.size(), oss.str());
--current_;
if (current_ == 0)
current_ = start_;
return ret;
}
};
And then you can use it like:
std::string examples[] = {
"",
"nothing",
"some number #N",
"nothing",
"some other #N number",
"nothing",
"#N another test",
"nothing",
};
Replacer replacer("#N", 18);
for (int i = 0; i < 8; ++i)
std::cout << replacer.replace(examples[i]) << '\n';

C++ remove punctuation marks and spaces from a string

How can I remove punctuation marks and spaces from a string in a simple way without using any library functions?
int main()
{
string s = "abc de.fghi..jkl,m no";
for (int i = 0; i < s.size(); i++)
{
if (s[i] == ' ' || s[i] == '.' || s[i] == ',')
{
s.erase(i, 1); // remove ith char from string
i--; // reduce i with one so you don't miss any char
}
}
cout << s << endl;
}
Assuming you can use library I/O like <iostream> and types like std::string and you just don't want to use the <cctype> functions like ispunct().
#include <iostream>
#include <string>
int main()
{
const std::string myString = "This. is a string with ,.] stuff in, it.";
const std::string puncts = " [];',./{}:\"?><`~!-_";
std::string output;
for (const auto& ch : myString)
{
bool found = false;
for (const auto& p : puncts)
{
if (ch == p)
{
found = true;
break;
}
}
if (!found)
output += ch;
}
std::cout << output << '\n';
return 0;
}
No idea about the performance, I'm sure it can be done in multiple better ways.

Print out the words of a line in reverse order through recursion

I'm trying not to use any storage containers. I don't know if it's even possible. Here is what I have so far. (I'm getting a segmentation fault).
#include <iostream>
#include <string>
using namespace std;
void foo(string s)
{
size_t pos;
pos = s.find(' ');
if(pos == string::npos)
return;
foo(s.erase(0, pos));
cout << s.substr(0, pos) << " ";
}
int main()
{
foo("hello world");
return 0;
}
I know there's probably many things wrong with this code. So rip away. I'm eager to learn. I'm trying to imitate a post order print as you would do in a reverse print of a singly linked list. Thanks.
EDIT:
An example:
"You are amazing" becomes "amazing are You"
The segfault is a stack overflow.
foo( "hello world" ) erases everything up to the first space (" world") and recurses.
foo( " world" ) erases everything up to the first space (" world") and recurses.
foo( " world" )... you get the idea.
Also, once you called foo( s.erase( 0, pos ) ), trying to print s.substr( 0, pos ) after the recursion returns does not make sense. You need to save the substring somewhere before you erase it, so you still have it to print afterwards.
void foo(string s)
{
size_t pos = s.find(' '); // declare-and-use in one line
string out = s.substr( 0, pos ); // saving the substring
if ( pos != string::npos )
{
foo( s.erase( 0, pos + 1 ) ); // recurse, skipping the space...
cout << " "; // ...but *print* the space
}
cout << out; // print the saved substring
}
The problem is that your recursion continues until you run out of memory.
Pay attention to this line:
if(pos == string::npos)
when your erase the substring you don't erase the white space so in the next recursion s.find returns pos = 0 which means that your recursion never ends.
Here is a code that works. Also note that I added a level variable to be able to control the behaviour on the first level (in this case add a endl)
#include <iostream>
#include <string>
using namespace std;
void foo(string s, int l)
{
size_t pos;
pos = s.find(' ');
if(pos == string::npos){
cout << s << " ";
return;
}
string temp = s.substr(0, pos);
foo(s.erase(0, pos+1),l+1);
cout << temp << " ";
if(l == 0)
cout << endl;
}
int main()
{
foo("hello world", 0);
return 0;
}
An approach to recursion, which may allow your compiler to transform automatically to iteration, is to accumulate the result in the function arguments. This will be familiar if you've written recursive functions in any of the Lisp family of languages:
#include <iostream>
#include <string>
std::string reverse_words(const std::string& s, const std::string& o = {})
{
using std::string;
const auto npos = string::npos;
static const string whitespace(" \n\r\t");
// find start and end of the first whitespace block
auto start = s.find_first_of(whitespace);
if (start == npos)
return s + o;
auto end = s.find_first_not_of(whitespace, start);
if (end == npos)
return s + o;
auto word = s.substr(0, start);
auto space = s.substr(start, end-start);
auto rest = s.substr(end);
return reverse_words(rest, space + word + o);
}
int main()
{
std::cout << reverse_words("hello to all the world") << std::endl;
std::cout << reverse_words(" a more difficult\n testcase ") << std::endl;
return 0;
}
I tried to make a brief example by using standard algorithms. I also handles more kinds of spaces than just standard whitespace (tabs for instance).
#include <cctype>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
void print_reverse(string words) {
// Termination condition
if(words.empty())
return;
auto predicate = (int(*)(int))isspace;
auto sit = begin(words);
auto wit = find_if_not(sit, end(words), predicate);
auto nit = find_if (wit, end(words), predicate);
print_reverse(string(nit, end(words)));
// word spaces
cout << string(wit, nit) << string(sit, wit);
}
int main() {
string line;
getline(cin, line);
print_reverse(line);
cout << endl;
}
Here is an example run:
$ ./print-out-the-words-of-a-line-in-reverse-order-through-recursion
You are amazing
amazing are You
The key is in adding 1 to pos in the erase statement.
So try:
#include <iostream>
#include <string>
using namespace std;
void foo(string s)
{
size_t pos;
pos = s.find(' ');
if(pos == string::npos)
{
cout << s << " ";
return;
}
string out = s.substr(0, pos);
foo(s.erase(0, pos+1));
cout << out << " ";
}
int main()
{
foo("hello world");
cout << endl;
return 0;
}
EDIT
Alternatively you could use a char* instead of a std::string, then you do not need to make a temp variable. Try it online.
#include <iostream>
#include <cstring>
void foo(char* s)
{
char* next = std::strchr(s, ' ');
if(next != nullptr)
{
foo(next + 1);
*next = 0;
}
std::cout << s << " ";
}
int main()
{
char s[] = "You are amazing";
foo(s);
std::cout << std::endl;
}
The problem is that you're not doing anything with the last word and you're not doing anything with the remaining chunk.
If you have a recursive reverse printer, you'll want something like this (pseudocode):
def recursive-reverse(string) {
pos = string.find-last(" ");
if pos doesn't exist {
print string;
return;
} else {
print string.create-substring(pos+1, string.end);
recursive-reverse(string.create-substring(0, pos));
}
}
To implement this in C++:
#include <iostream>
#include <string>
void recursive_reverse(std::string &s) {
// find the last space
size_t pos = s.find_last_of(" ");
// base case - there's no space
if(pos == std::string::npos) {
// print only word in the string
std::cout << s << std::endl;
// end of recursion
return;
} else {
// grab everything after the space
std::string substring = s.substr(pos+1);
// print it
std::cout << substring << std::endl;
// grab everything before the space
std::string rest = s.substr(0, pos);
// recursive call on everything before the space
recursive_reverse(rest);
}
}
int main() {
std::string s("Hello World!");
recursive_reverse(s);
return 0;
}
ideone