Link to home
Start Free TrialLog in
Avatar of atomicgs12
atomicgs12Flag for United States of America

asked on

Find last occurrence of any valid character in a string C++

I have a C++ project and I read a line from a file, I'm using fgets but I can use anything. With that line I would like to find the last position, in the string, that contains a valid character or number, i.e. 0-9 or a-Z. I don't really what to create a string that holds all the numbers and characters to use as a compare I would like the most eligant was to find the last vaild character/number in the string so I can extract a variable size word from the string.
Thanks
ASKER CERTIFIED SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
::isalnum in combination with phoffric's comment above would probably be the way to go.
Avatar of phoffric
phoffric

Since the comparison string is user defined, and only alphanumerics are included, then there should be no need to further test for alphanumerics.

However, you can write your own loop searching from the end of the string testing for alphanumerics using isalnum and not using the functions in the first post.
Avatar of atomicgs12

ASKER

Doesn't all these examples mean I have to create a comparison string of "0-9, a-z, A-Z", which is what I did NOT want to do?
Thanks
OP stated I don't really what to create a string that holds all the numbers and characters to use as a compare, and unless I'm misunderstanding something strpbrk, strcspn, strtok, etc all require a string to hold all the numbers and characters against which to compare, which is why I suggested isalnum - maybe I'm being too literal. ;)

string test("one two three---");

string::iterator it = find_if(test.rbegin(), test.rend(), isalnum).base();
	
int indexOfLastAlphaNumeric = it - test.begin() - 1;

Open in new window

Using isalnum is likely to be a performance improvement over the other mentioned functions since it doesn't necessarily have to compare each char in your string against the 26*2+10 valid chars. Instead depending upon the locale, it will do just a few tests. If ASCII chars, it would be
( 'a' <= ch && ch <= 'z')  || ( 'A' <= ch && ch <= 'Z') for the alphas, and similar for the digits.
>> which is what I did NOT want to do?
I misunderstood the OP.
I guess you don't necessarily need to know the index of the last non-alpha-numeric, if, for example, you just want the last word in a given string:

http://www.cplusplus.com/reference/algorithm/find_if/
http://www.cplusplus.com/reference/std/functional/not1/
http://www.cplusplus.com/reference/string/string/

Enter a string: one---two---three---
The last word in the line is: three

Open in new window


#include <iostream>
#include <string>
#include <algorithm>
#include <functional>

using namespace std;

void GetLastWord(string &source, string &dest)
{
	string::reverse_iterator end = find_if(source.rbegin(), source.rend(), ::isalnum);
	string::iterator start = find_if(end, source.rend(), not1(ptr_fun(isalnum))).base();

	dest = string(start, end.base());
}

int main(int argc, char *argv[])
{
	string input, lastWord;

	cout << "Enter a string: ";
	getline(cin, input);
	
	GetLastWord(input, lastWord);

	cout << "The last word in the line is: " << lastWord << endl;

	cin.get();
}

Open in new window

While perhaps not the most elegant solution, I would think the most strait forward solution would be to use strlen to find the end of the string, position a char* to point to that last char of the string, then use the logic like what phoffric shows two posts up to determine if what char* points to is a valid charactor or not.  If the character is invalid (i.e. not a-z, A-Z, or 0-9) then back the char* up one time until a valid character is reached (or the start of the string is reached).

char Read[100];
char* pLast;
fgets( Read );
pLast = Read + strlen( Read );
while( pLast > Read )
{
    char ch = *pLast;
    if( 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '0' <= ch && ch <= '9')
        break;
    pLast--;
}
if( pLast == Read )
    pLast = NULL;  //Flags that there were no valid characters in the string
why not just use isalnum(ch) instead of the if statement (which assumes ASCII chars)? You could just use:
    if( isalnum(ch) )
          break;
>> Doesn't all these examples mean I have to create a comparison string of "0-9, a-z, A-Z", which is what I did NOT want to do?
To which I replied, "I misunderstood the OP."
So, I think you may have accepted my answer which is not what you wanted.
Actually I should have divided it up, I ended up using your hint of find_last_of and tgerbert's std::string .begin, .end
I'm not concerned about the points, so long as the correct answer is selected so someone  else reading this thread in the future might find it useful, and that appears to be the case so I say leave it as-is.