How to read the last character in a string - c++

I was trying to print the last character of a string, for example str[]="This is an example", I tried to print the 'e' of "example" with some functions, but none of them funct as I expected. I know it's more simple to write in the code the position number of the last character, but as in strrchr function, the code work by itself. Is there a function that works similar?
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main ()
{
char str[] = "This is an example";
char * pch;
pch=strrchr(str,'s');
cout<<str<<endl;
cout<<"Last time 's' was found was in position: "<<pch-str+1<<endl;
cout<<"Last character in this example is "<<str[X];
return 0;
}

From the documentation for strrchr:
The terminating null-character is considered part of the C string. Therefore, it can also be located to retrieve a pointer to the end of a string.
Thus, strrchr(str, '\0')[-1] will have the last character. Note that this is only safe if you're sure str isn't empty.

Simple: use the standard strlen function, as follows:
int main ()
{
char str[] = "This is an example";
char * pch;
pch=strrchr(str,'s');
cout<<str<<endl;
cout<<"Last time 's' was found was in position: "<<pch-str+1<<endl;
size_t X = strlen(str) - 1; // X will be the index of the last character!
cout<<"Last character in this example is "<<str[X];
return 0;
}
Or, just for fun, if you want to handle the case where the string could be empty:
size_t X = strlen(str); X -= !!X; // Non-zero: decrement, Zero: Leave as is
cout<<"Last character in this example is "<<str[X];
Then, for an empty string, cout << str[X] will show whatever the implementation does for a NULL character.

If you don't mind to use std::string this snippet would do the job.
#include <string>
#include <iostream>
int main() {
std::string str = "This is some text";
std::cout << str.back() << std::endl;
}

I assume you choose char[] to avoid allocation or something similar so am not going to discuss std::string as an option.
Three solutions, one in modern C++ using string_view, one using templates ;
and one using std::size and the index operator.
Solution 1.1:
I recommend you use this, its nearly optimal and is much more readable than the alternative. It also doesn't require as much boiler plate to handle empty strings, or strings without null termination.
#include <string_view>
#include <iostream>
int main()
{
std::string_view str = "This is an example";
auto X = str.find_last_of('s');
//
// Make sure the character exists in the string
if (X != std::string_view::npos)
{
std::cout<< str << std::endl;
std::cout<< "Last time 's' was found was in position: " << X << std::endl;
}
else
{
std::cout<<"Character did not exist in string.\n";
}
if (!str.empty()) std::cout<< "Last character in this example is " << str.back();
else std::cout << "Cannot get the last character in an empty string!";
return 0;
}
You can run the solution here:
https://onlinegdb.com/SJK2hjPEB
The same code will work with std::string.
Solution 1.2
This is a compile time only solution, it relies on the string being aggregate constructed or constructed as a string.
template <size_t N>
constexpr char LastCharacter(char (&input)[N])
{
static_assert(N >= 1, "A character array representing a string must have atleast 1 character AND a null terminator.");
return (input[N - 1] == '\0') ? input[N - 2] : input[N - 1];
}
Tests and examples shown here:
https://onlinegdb.com/HJ_IXEd4H
Solution 2
This has the required checks to avoid issues with empty strings.
In this version it is a compile time error to have an empty array. str[] = "" is not an empty array it has 1 character, a null. An empty string has no last character, this needs to be handled. It also should be handled for the strrchr.
If you must use strrchr(...) then consider checking whether the result is nullptr. If a nullptr is returned then the character wasn't found in the string:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <cassert>
using namespace std;
int main ()
{
char str[] = {'h', 'e','l', 'l', 'o', '\0'};
//
// Prevent use with an empty array (1 character + 1 null character minimum)
if (std::size(str) > 1)
{
//
// Only allow null terminated strings.
assert( str[std::size(str) - 1] == '\0' );
//
// There skip the last null character and get the last character
// No null character, not compensation needed
cout<<"Last character in this example is "<< str[ std::size(str) - 2 ];
}
else
{
cout << "Cannot process empty string\n";
}
return 0;
}
https://onlinegdb.com/SkrP2Q_NB
Please note, defining strings as arrays enables strings to exist without null terminators. In that case the above solution does not function. If you want to handle that case you need to check for the existance of a null terminator and if it is present compensate in code. assert causes an error if there isn't a null terminator.
--
To elaborate on the problem with strrchr. The function requires a null terminated string.
The terminating null-character is considered part of the C string.
Therefore, it can also be located to retrieve a pointer to the end of
a string.
http://www.cplusplus.com/reference/cstring/strrchr/
This was quoted by the previous answer, but for completeness also here.
The datatype you are using allows a character array with no null termination:
char data[] = {'a', 'b', 'c'};
That is what the assert handles in the solution 2.

Related

Replace each alphabetic character in passCode with '&'

new to programming here. I'm confused as to how I'm supposed to use "isalpha" to figure this out. I have no clue how to start it.
A 2-character string, passCode, is read from input. Replace each alphabetic character in passCode with '&'. Otherwise, passCode is not changed.
Ex: If the input is c4, then the output is:
&4
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string passCode;
getline(cin, passCode);
cout << passCode << endl;
return 0;
}
In many many programming languages, so called loops are used to execute or repeat blocks of code.
Or do iterate over "something". Therefore loops are also called Iteration statements
Also C++ has loops or iteration statements. The basic loop constructs are
for loops,
while loops,
do-while loops
Range-based for loops
Please click on the links and read the descriptions in the C++ reference. You can use any of them to solve your problem.
Additionally, you need to know that a string is a container. Container means that a varibale of such a type contains other elements from nearly any type.
A string for example, contains characters.
Example: If you have a string equivalent to "Hello", then it contains the characters 'h', 'e', 'l', 'l', 'o'.
Another nice property of some containers is, that they have an index operator or better said, a subscript operator []. So, if you want to access a charcter in your string, you may simply use your variable name with an index specified in the subscript operator.
Very important: Indices in C++ start with 0. So the first character in a string is at index 0. Example:
std::string test = "Hello";
std::cout << test[0];
will print H
With all the above gained know how, we can now solve your problem easily. We will iterate over all characters in your string, and then check, if a character is an alphabetic charcter. In that case, we will replace it.
One of many many possible implementations:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string passCode;
getline(cin, passCode);
for (unsigned int i = 0; i < passCode.length(); ++i) {
if (isalpha(passCode[i])) {
passCode[i] = '&';
}
}
cout << passCode << endl;
return 0;
}
I believe this is what you're looking for when using isalpha.
Looking at the 2 character input for passCode, check each place (0 and 1) if it is a an alpha and change it to &.
if (isalpha(passCode.at(0)) {
passCode.at(0) = '&';
}
if (isalpha(passCode.(1)) {
passCode.at(1) = '&';
}

C-String implementation in C++ outputs incorrectly

I was recently working on a problem teaching new users of C++, which I myself am, how to use cstrings and the different implementations of them compared to the imported string object in C++. As I was working on the problem, I came across an error where, despite initializing the size of the cstring to an appropriate length for the operations that were being done, the cstring was being outputted strangely.
When I would go to print out the cstring using cout, it would print some of the cstring correctly, but oftentimes the first several characters were random characters that had nothing to do with the operations being done to the cstring. However, I found a way to definitively prevent those characters from being printed; however, I am curious as to why this works as well as what the issue is here.
I found that adding cout << ""; on its own line prior to printing the cstring resolved the issue of the random characters being outputted when printing the cstring. However, this seems like only a temporary fix and I am looking to find a more educated approach to solving this issue.
Below I have included the code that was causing the errors.
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
using namespace std;
int main() {
vector<string> words = {"Hello,", "and", "welcome", "to", "the", "world", "of", "C++!"};
// Calculate the total number of characters in the words vector
// (including an additional character for space)
int length = 0;
for(int i = 0; i < words.size(); i++) {
length += words.at(i).length() + 1;
}
cout << ""; // Removing this line of code will cause the output to do strange things
// Initialize the cstring to be of size length
char cstring[length];
// Build the cstring using cstring library functions
for(int i = 0; i < words.size(); i++) {
strcat(cstring, (words.at(i) + " ").c_str());
}
// Null-terminate the cstring
cstring[length-1] = '\0';
// Output the cstring
cout << cstring << " " << strlen(cstring) << endl;
return 0;
}
If the line of code containing cout << ""; is removed, the output looks something like this, with a random amount and random set of characters at the beginning of the output each time:
`k+��Hello, and welcome to the world o 39
However, by including the line, I am able to achieve the desired output:
Hello, and welcome to the world of C++! 39
For starters variable length arrays is not a standard C++ feature
// Initialize the cstring to be of size length
char cstring[length];
Secondly you defined an uninitialized array. So using strcat invokes undefined behavior
strcat(cstring, (words.at(i) + " ").c_str());
This statement
// Null-terminate the cstring
cstring[length-1] = '\0';
is redundant because the function strcat appends also the terminating zero provided that the character array you declared has a space to accommodate the zero character (and you forgot to reserve a space for the terminating zero in the array).
If the compiler supports variable length arrays then the program can look the following way
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
int main()
{
std::vector<std::string> words =
{
"Hello,", "and", "welcome", "to", "the", "world", "of", "C++!"
};
// Calculate the total number of characters in the words vector
// (including an additional character for space)
size_t length = words.size();
for ( const auto &s : words ) length += s.length();
// Initialize the cstring to be of size length
char cstring[length + 1];
cstring[0] = '\0';
// Build the cstring using cstring library functions
for ( const auto &s : words )
{
std::strcat( cstring, ( s + ' ' ).c_str() );
}
// Output the cstring
std:: cout << cstring << ' ' << length << std::endl;
return 0;
}
The program output is
Hello, and welcome to the world of C++! 40

Substracting a string from an int

I following code illustrates the use of c_str function
#include <iostream>
#include <string.h>
using namespace std;
int main() {
std::string str("Hello world!");
int pos1 = str.find_first_of('w');
cout<< "pos1: "<< pos1 <<endl;
int pos2 = strchr(str.c_str(), 'w') - str.c_str(); //*
//int pos2 = strchr(str.c_str(), 'w')
cout<< "pos2: "<< pos2 <<endl;
cout<< "str.c_str(): "<< str.c_str() <<endl;
if (pos1 == pos2) {
printf("Both ways give the same result.\n");
}
}
The output is
pos1: 6
pos2: 6
str.c_str(): Hello world!
Both ways give the same result.
I don't get the str.c_str() role in line * . I am substracting a string from an int, what is the meaning of that?
When I erase it, that is when I comment line * , and uncomment the following line I get an error: invalid conversion from 'char*' to 'int'. How come there is not error in the original code?
.c_str() returns the address to the start of the string. strchr the address to the first occurence of a specific character inside the given string or a nullptr, if the character is not found.
If you subtract one address from another, you get the distance of the to pointers, which is the offset of the character inside the string in this case.
The find_* functions of the string class all return the offset or std::string::npos, if the character is not found.
Reference:
strchr
c_str
find_first_of
First,strchr returns a pointer.Pointrer types require a forced cast.
Second,The data type of the pointer is related to the number of CPU bits.So you should use unsigned long type.

C++ C-string has (apparently) no content to display

First of all, I am nothing but new to both programming and Stack Overflow.
I am self-studying with Schaum's outline for Programming with C++ and I have some issues with problem 8.24 (solutions are given to almost every problem in the book, but I want to know why my code in particular isn't working as expected).
You are supposed to be given a c-string and return the given string, but with all its tokens in reverse order (but keeping the natural order of the token itself).
That is, given "Enter a sentence" it would show on screen "sentence a Enter".
My code is the following:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char line1[100];
cout << "Enter a sentence (enter \".\" to terminate input):\n";
cin.getline(line1,100,'.');
char line2[strlen(line1) + 1]; // we add 1 for the empty char that ends every c string
int char_count = strlen(line1); //strlen() does not include the empty char
char* p = strtok(line1," ");
while (p)
{
char_count -= strlen(p); // we substract p's len to start adding its chars
for (int i = 0; i <= strlen(p); i++)
line2[char_count + i] = p[i]; // we then add the chars themselves
if ((char_count - 1) > 0)
line2[--char_count] = ' '; // a blanck space is needed between the different tokens
p = strtok(NULL, " ");
}
cout << "\n" << line2 << "\n";
}
Unfortunately, the code is wrong in many ways. The most obvious thing is the obscurity of the word reversal process (and the fact it is mixed with word iteration).
According to the commenters, you are not using C++. In C++ it would be rather straightforward:
#include <algorithm>
#include <iostream>
#include <string>
void reverse_words(std::string& s) {
/* starting position of the word */
size_t last_pos = 0;
do {
/* find the end of current word */
size_t end_pos = std::min( s.find(' ', last_pos + 1), s.size() );
/* reverse one word inplace */
std::reverse(s.begin() + last_pos, s.begin() + end_pos);
/* advance to the begining of the next word */
last_pos = end_pos + 1;
} while (pos != std::string::npos);
std::reverse(s.begin(), s.end());
}
int main()
{
std::string s = "This is a sentence";
reverse_words(s);
std::cout << s << std::endl;
}
Hopefully, you can see the essence of the method: sequentially find start and finish of each word, reverse letter order in this word and then finally reverse the entire string.
Now, getting back to the C-string question. You can replace std::string::find call with strtok and write your version of std::reverse specialized for C strings (the reversal of the entire string or its part is simpler than reversing the word order and this is also the recommended exercise).
Start from a simpler program which prints out pairs of integers (start_pos and end_pos for each word) using strtok. Then write a reverse procedure and test it also. Finally, combine this word iteration with reverse. I personally think this is the only way to be sure your implementation is correct - being sure in each of its parts and being able to test each part individually.
A lot of improvements have been added to C++ since that book was originally written, and we can do it in a lot cleaner and safer way now. We'll break the problem into two parts:
A function to convert a string into a list of tokens
The main function, which reads the string; reverses it; and prints it.
These functions will be tokenize, which returns a vector of string_view, and main. A string_view is just a class that stores a pointer and a size to some other string. It's efficient because it won't make a copy of the string or allocate any memory. In this case, it's the right tool for the job because we're going to be breaking up an existing string.
#include <string_view>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
auto tokenize(std::string_view line) {
std::vector<std::string_view> tokens;
for (size_t token_size = line.find(' ');
token_size != line.npos;
token_size = line.find(' '))
{
tokens.push_back(line.substr(0, token_size));
line.remove_prefix(token_size + 1);
}
tokens.push_back(line);
return tokens;
}
int main() {
std::string line;
std::getline(std::cin, line);
auto tokens = tokenize(line);
std::reverse(tokens.begin(), tokens.end());
for(auto token : tokens) {
std::cout << token << ' ';
}
std::cout << std::endl;
}
Explaining tokenize
Tokenize takes a string_view as input, and returns a list of the tokens. line.find(' ') will look for a space. If it finds one, it'll return the position of the space; otherwise, it'll return line.npos (which is basically the biggest possible size).
For every token we find, we
get the token via view.substr(0, token_size)
Add the token to the vector via tokens.push_back
Then, we update the line by removing the first token and the corresponding space. This is line.remove_prefix(token_size + 1);
Once there are no more spaces, we'll add the remainder of the line to the vector using tokenize.push_back(line);, and then we'll return the vector of tokens.
Explaining main
We can get the line via std::getline(std::cin, line);, which will read a line from cin and put it in the variable we give it (line). After that, we can read all the tokens in the line using the tokenize function we wrote. We'll reverse the vector of tokens via std::reverse, and then we'll print out all the tokens.
Thanks to each of you.
Seeing your answers I have learnt quite a lot about good programming (both regarding syntax and original ways to solve the problem itself, as Viktor's).
I apologise if I have not given the proper feedback, but again I am (still) unfamiliar with Stack's customs and ''policies''.

C++ comparing the index of a string to another string?

How can I compare a single character from a string, and another string (which may or may not be greater than one character)
This program gives me almost 300 lines of random errors. The errors don't reference a specific line number either, just a lot of stuff about "char* ", "", or "std::to_string".
#include <iostream>
#include <string>
using std::cout;
using std::string;
int main() {
string str = "MDCXIV";
string test = "D";
if (test == str[4]) { // This line causes the problems
cout << test << endl;
}
return 0;
}
str[4] is a char type, which will not compare with a string.
Compare apples with apples.
Use
test[0] == str[4]
instead.
You need to convert str[4] (which is a char) to a string before you can compare it to another string. Here's a simple way to do this
if (test == string(1, str[4])) {
You're comparing a char to a std::string, this is not a valid comparison.
You're looking for std::string::find, as follows:
if( test.find( str[4] ) != std::string::npos ) cout << test << "\n";
Note that this will return true if test contains str[4].
You're mixing types. It doesn't know how to compare a string (test) to a char (str[4]).
If you change test to a char that will work fine. Or reference the specific character within test you want to compare such as if (test[0] == str[4]) it should compile and run.
However, as this is merely an example and not really the true question what you'll want to do is look at the functionality that the std::string class supplies
Also you need "D" to be a char value not a string value if you are comparing it like that.
std::string myString = "Hello World";
const char *myStringChars = myString.c_str();
You have to turn it into a char array before can access it. Unless you do.
str.at(i);
which you can also write as
str[i] <-- what you did.
Essentially, this all boils down to test needs to initialized like char test = 'D';
Final Output..
#include <iostream>
#include <string>
using std::cout;
using std::string;
int main() {
string str = "MDCXIV";
char test = 'D';
if (test == str[4]) { // This line causes NO problems
cout << test << endl;
}
return 0;
}
I think you might be mixing python with c++. In c++ 'g' refers to a single character g not a string of length 1. "g" refers to an array (string) which is 1 character long and looks like ['g']. So as you can see, if you compare a single character to an array of characters no matter if the array is a single character long, this operation is not defined.
This will work if define it yourself by building a class which is able to compare string of one character long to a single character. Or just overload the == operator for doing just that
Example:
#include <iostream>
#include <string>
using std::cout;
using std::string;
using std::endl;
bool operator == ( const string &lh, const char &rh) {
if (lh.length() == 1) return lh[0] == rh;
return 0;
}
int main() {
string str = "MDCXIV";
string test = "D";
if (test == str[4]) {
cout << test << endl;
}
else cout << "Not a match\n";
return 0;
}