MFC newbie: how to determine if a character is hexadecimal using "FindOneOf()" - c++

I'm new at MFC and I need to do something which sounds extremely simple: determining if a string only contains hexadecimal characters.
For that, I browse through the string (it's a CString) and I verify all characters using the FindOneOf() method, as follows:
int iTest = CString(pszText[i]).FindOneOf((LPCWSTR)"0123456789ABCDEFabcdef");
For some bizarre reason, I always get -1 as a result.
What am I doing wrong?
P.s.1 I prefer not to use the SpanIncluding() method, I find the FindOneOf() quite readable but I don't know how to use it.
P.s.2 Also simple STL seems not to be working: I tried to work with std::isxdigit(pszText[i]) but in order to get this to work, I need to include <locale> and then this function is asking for a second parameter, next to the character I want to check, and a null-pointer is not allowed there (std::isxdigit(pszText[i], nullptr) does not work).

There are several problems in your code:
This is wrong:
int iTest = CString(pszText[i]).FindOneOf(LPCWSTR)"0123456789ABCDEFabcdef");
It should be:
int iTest = CString(pszText[i]).FindOneOf(L"0123456789ABCDEFabcdef");
The cast will simply make the compiler believe that "0123..." is a wide string but it isn't. You need to use the L prefix to indicate that the string is a wide string.
But even then your algorithm won't work because FindOneOf will simply find the first occurrence of any of the characters in the parameter.
Example:
int iTest = CString(L"Z223Zbc").FindOneOf(L"0123456789ABCDEFabcdef");
"Z223Zbc" is obviously not a hexadecimal string, but iTest will contain 1 because the first character of "Z223Zbc" being part of "0123456789ABCDEFabcdef" is '2' and that's at position 1.
iTest will only contain -1 if the string to be tested doesn't contain any hexadecimal characters as for example "xyz".
Therefore this solution is appropriate:
#include <cwctype>
...
WCHAR string[] = L"123abcX";
bool ishexstring = true; // assume the string is a hex string
for (int i = 0; ; i++)
{
WCHAR c = string[i];
if (c == 0)
break; // end of string => we exit the looop
if (!std::iswxdigit(c))
{
ishexstring = false; // c is no hex digit
break; // exit loop
}
}
This algorithm should be put into a function, but I'm leaving this as an exercise for the reader.
Solution using SpanIncluding (less efficient because we need tot construct a temporary CString):
bool ishexstring = CString(string).SpanIncluding(L"0123456789ABCDEFabcdef") == str;

Related

Why a "no matching function" error for call by reference with literal number?

The problem asks to create a program that asks the user to enter some text and that text will be surrounded by asterisks depending on the width of the screen for example if the user inputs "Hello world" the output should be:
****************
* Hello World! *
****************
I've tried to create the functions but I'm stuck becaus of a compiler error with the shown minimal code.
Question: Why does it tell me no matching function for within_width(text, 80)?
Some of the code I have is below:
#include <iostream>
#include <string>
void display_header (std::string &header) {
std::string text;
header = text;
}
bool within_width (std::string& text, unsigned short int& max_width) {
}
int main() {
std::string text;
std::cout << "Please enter header text: ";
std::getline(std::cin, text);
if (within_width(text, 80)) {
// call the display_header function and pass in the text
// inputted by the user
} else {
std::cout << text;
}
return 0;
}
This declaration of the function
bool within_width (std::string& text, unsigned short int& max_width)
asks for an unsigned short int variable, because it has a reference parameter, see the second &.
To satisfy it, you need to put the value 80 into a variable and give the variable as parameter.
unsigned short int MyWidth=80;
if (within_width(text, MyWidth))
Alternatively (but I assume you are not allowed) you can use a call by value parameter
bool within_width (std::string& text, unsigned short int max_width)
Then you could call as shown.
I won't give a full answer to the exercise here, just some clues.
the display_header() and within_width() functions need to know the string given in parameters but may not modify it ; thus the type of this parameter should be const std::string & (the const was missing).
the second parameter of the within_width() function is just an integer that will be compared to the length of the string ; you don't need to pass it by reference (or at least const), rather by value. Here, the (non-const) reference prevents from passing the literal constant 80.
(it seems to be the main concern of the question after edition)
You need to reason step by step.
all of this depends on the size of the string (12 for Hello World!) ; this information is available via size(text) (or text.size())
(https://en.cppreference.com/w/cpp/iterator/size)
(https://en.cppreference.com/w/cpp/string/basic_string/size)
This size will have to be compared to max_width
Displaying the line with header will require 4 more characters because * will be prepended and * will be appended.
Thus the two surrounding lines will have the length size(header)+4 too.
In order to create such a string made of *, you could use a constructor of std::string taking two parameters : the count of characters and the character to be repeated.
(https://en.cppreference.com/w/cpp/string/basic_string/basic_string)
Send all of this to std::cout in the correct order.
Edit: Just noticing that this answer probably goes far beyond the scope of the task you have been given (just filling in some skeleton that has been provided by your teacher).
I'll still leave it here to illustrate what could be done with arbitrary input. Maybe you want to experiment a little further than what you have been asked...
bool within_width(...)
Pretty simple: string.length() <= max – just wait a second, you need to consider asterisks and spaces at beginning and end of output, so: max - 4
But you can do better, you can split the string, best at word boundaries. That's a bit difficult more difficult, though:
std::vector<std::string> lines;
// we'll be starting with an initially empty line:
auto lineBegin = text.begin();
auto lineEnd = text.begin();
for(auto i = text.begin(); i != text.end(); ++)
// stop condition empty: we'll stop from inside the loop...
{
// ok, we need to find next whitespace...
// we might try using text.find_first_of("..."), but then we
// need to know any whitespace characters ourselves, so I personally
// would rather iterate manually and use isspace function to determine;
// advantage: we can do other checks at the same time, too
auto distance = std::distance(lineBegin, i);
if(std::distance(lineBegin, i) > maxLineLength)
{
if(lineEnd == lineBegin)
{
// OK, now we have a problem: the word itself is too long
// decide yourself, do you want to cut the word somewhere in the
// middle (you even might implement syllable division...)
// or just refuse to print (i. e. throw an exception you catch
// elsewhere) - decide yourself...
}
else
{
lines.emplace_back(lineBegin, lineEnd);
lineBegin = lineEnd; // start next line...
}
}
// OK, now handle current character appropriately
// note: no else: we need to handle the character in ANY case,
// if we terminated the previous line or not
if(std::isspace(static_cast<unsigned char>(*i)))
{
lineEnd = i;
}
// otherwise, we're inside a word and just go on
}
// last line hasn't been added!
lines.emplace_back(lineBegin, lineEnd);
Now you can calculate maximum length over all the strings contained. Best: Do this right when adding a new line to the vector, then you don't need a separate loop...
You might have noticed that I didn't remove whitespace at the end of the strings, so you wouldn't need to add you own one, apart, possibly, from the very last string (so you might add a lines.back() += ' ';).
The ugly part, so far, is that I left multiple subsequent whitespace. Best is removing before splitting into lines, but be aware that you need to leave at least one. So:
auto end = text.begin();
bool isInWord = false; // will remove leading whitespace, if there is
for(auto c : text)
{
if(std::isspace(static_cast<unsigned char>(c)))
{
if(isInWord)
{
*end++ = ' '; // add a single space
isInWord = false;
}
}
else
{
*end++ = c;
isInWord = true;
}
}
This would have moved all words towards the beginning of the string, but we yet to drop the surplus part of the string yet contained:
text.erase(end, text.end());
Fine, the rest is pretty simple:
iterate over maximum length, printing a single asterisk in every loop
iterate over all of your strings in the vector: std::cout << "* " << line << "*\n";
repeat the initial loop to print second line of asterisks
Finally: You introduced a fix line limit of 80 characters. If console is larger, you just won't be using the entire available width, which yet might be acceptable, if it is smaller, you will get lines broken at the wrong places.
You now could (but that's optional) try to detect the width of the console – which has been asked before, so I won't go any deeper into.
Final note: The code presented above is untested, so no guarantee to be bugfree!

How do I remove only the first character of a string that is not a digit? (MFC, C++)

I want to remove only the first character in a string that is NOT a digit. The first character can be anything from ‘A’ to ‘Z’ or it may be a special character like ‘&’ or ‘#’. This legacy code is written in MFC. I've looked at the CString class but cannot figure out how to make this work.
I have strings that may look like any of the following:
J22008943452GF or 22008943452GF or K33423333333IF or 23000526987IF or #12000895236GF. You get the idea by now.
My dilemma is I need to remove the character in the first position of all the strings, but not the strings that starts with a digit. For the strings that begin with a digit, I need to leave them alone. Also, none of the other characters in the string should not be altered. For example the ‘G’, ‘I’ or ‘F’ in the later part of the string should not be changed. The length of the string will always be 13 or 14 digits.
Here is what I have so far.
CString GAbsMeterCalibration::TrimMeterSNString (CString meterSN)
{
meterSN.MakeUpper();
CString TrimmedMeterSNString = meterSN;
int strlength = strlen(TrimmedMeterSNString);
if (strlength == 13)
{
// Check the first character anyway, even though it’s
// probably okay. If it is a digit, life’s good.
// Return unaltered TrimmedMeterSNString;
}
if (strlength == 14))
{
//Check the first character, it’s probably going
// to be wrong and is a character, not a digit.
// if I find a char in the first postion of the
// string, delete it and shift everything to the
// left. Make this my new TrimmedMeterSNString
// return altered TrimmedMeterSNString;
}
}
The string lengths are checked and validated before the calls.
From my investigations, I’ve found that MFC does not have a regular expression
class. Nor does it have the substring methods.
How about:
CString GAbsMeterCalibration::TrimMeterSNString (CString meterSN)
{
meterSN.MakeUpper();
CString TrimmedMeterSNString = meterSN;
int strlength = strlen(TrimmedMeterSNString);
if (std::isdigit(TrimmedMeterSNString.GetAt(0)) )
{
// Check the first character anyway, even though it’s
// probably okay. If it is a digit, life’s good.
// Return unaltered TrimmedMeterSNString;
}
}
From what I understand, you want to remove the first letter if it is not a digit. So you may make this function simpler:
CString GAbsMeterCalibration::TrimMeterSNString(CString meterSN)
{
meterSN.MakeUpper();
int length = meterSN.GetLength();
// just check the first character is always a digit else remove it
if (length > 0 && unsigned(meterSN[0] - TCHAR('0')) > unsigned('9'))
{
return meterSN.Right(length - 1);
}
return meterSN;
}
I am not using function isdigit instead of the conditional trick with unsigned because CString uses TCHAR which can be either char or wchar_t.
The solution is fairly straight forward:
CString GAbsMeterCalibration::TrimMeterSNString(CString meterSN) {
meterSN.MakeUpper();
return _istdigit(meterSN.GetAt(0)) ? meterSN :
meterSN.Mid(1);
}
The implementation can be compiled for both ANSI and Unicode project settings by using _istdigit. This is required since you are using CString, which stores either MBCS or Unicode character strings. The desired substring is extracted using CStringT::Mid.
(Note that CString is a typedef for a specific CStringT template instantiation, depending on your project settings.)
CString test="12355adaddfca";
if((test.GetAt(0)>=48)&&(test.GetAt(0)<=57))
{
//48 and 57 are ascii values of 0&9, hence this is a digit
//do your stuff
//CString::GetBuffer may help here??
}
else
{
//it is not a digit, do your stuff
}
Compare the ascii value of the first position in the string and you know if it's a digit or not..
I don't know if you've tried this, but, it should work.
CString str = _T("#12000895236GF");
// check string to see if it starts with digit.
CString result = str.SpanIncluding(_T("0123456789"));
// if result is empty, string does not start with a number
// and we can remove the first character. Otherwise, string
// remains intact.
if (result.IsEmpty())
str = str.Mid(1);
Seems a little easier than what's been proposed.

How to use strtok in string?

Edit: Sorry, it should be c++. how to use strtok in string?
FQ_ID_line[0]="1,26665;TUK.006.8955.FQ;TUK;400 BB 2 FQ;400 BB 2;899;FQ;Z_SCCFG1;Z_BSCFG1;333";
FQ_ID_line[1]="2,26223;TUK.002.8955.FQ;TUK;400 BB 2 FQ;400 BB 2;;FQ;Z_SCCFG1;Z_BSCFG1;333";
for(int FQ_i=0;FQ_i<FQ_Number;FQ_i++)
{
printf( "FQ_ID_line[FQ_i]=%u\n", FQ_ID_line[FQ_i] );
char * FQ_array=strdup(FQ_ID_line[FQ_i].c_str());
char *chars_array=strtok(FQ_array,seps);
chars_array=strtok(NULL,seps);
strcpy(DataLine[FQ_i].analog_comp_id,chars_array);
chars_array=strtok(NULL,seps);
strcpy(DataLine[FQ_i].RTU_abbr,chars_array);
chars_array=strtok(NULL,seps);
chars_array=strtok(NULL,seps);
chars_array=strtok(NULL,seps);
chars_array=strtok(NULL,seps);
chars_array=strtok(NULL,seps);
strcpy(DataLine[FQ_i].analog_scc_fep_group,chars_array);
chars_array=strtok(NULL,seps);
strcpy(DataLine[FQ_i].analog_bsc_fep_group,chars_array);
chars_array=strtok(NULL,seps);
strcpy(DataLine[FQ_i].RTU_number,chars_array);
DataLine[FQ_i].float_RTU_number=atof(chars_array);
free(FQ_array);
}
the ouput is :
DataLine[0].analog_comp_id=TUK.006.8955.FQ
DataLine[0].RTU_abbr=TUK
DataLine[0].analog_scc_fep_group=Z_SCCFG1
DataLine[0].analog_bsc_fep_group=Z_BSCFG1
DataLine[0].float_RTU_number=333
DataLine[1].analog_comp_id=TUK.002.8955.FQ
DataLine[1].RTU_abbr=TUK
DataLine[1].analog_scc_fep_group=Z_BSCFG1
DataLine[1].analog_bsc_fep_group=333
DataLine[1].float_RTU_number=
I want to the ouput:
DataLine[0].analog_comp_id=TUK.006.8955.FQ
DataLine[0].RTU_abbr=TUK
DataLine[0].analog_scc_fep_group=Z_SCCFG1
DataLine[0].analog_bsc_fep_group=Z_BSCFG1
DataLine[0].float_RTU_number=333
DataLine[1].analog_comp_id=TUK.002.8955.FQ
DataLine[1].RTU_abbr=TUK
DataLine[1].analog_scc_fep_group=Z_SCCFG1
DataLine[1].analog_bsc_fep_group=Z_BSCFG1
DataLine[1].float_RTU_number=333
The cause of the problem:
The function strtok() has many problems, due to the fact that subsequent calls depend on previous calls, and this dependency is managed in an unsafe manner:
it's not thread safe (see Robert's comment, and C++ standard section 21.8 pt 14)
if one function you call would use strtok() without you knowing, your next call to strtok() would return a lot of surprises.
Now your problem comes from the input string part: ...400 BB 2;;FQ;..., and the definition of strtok() : In subsequent calls, the function (...) uses the position right after the end of last token as the new starting location for scanning. To determine the beginning and the end of a token, the function first scans from the starting location for the first character not contained in delimiters (which becomes the beginning of the token)
So everything works well until it returns "400 BB 2". The next ";" will according to this algorithm be skipped and your code will jump over the empty field (;;) as if it didn't exist. Not ony do you have a shift in the following fields, but your last call to strtok() may even cause segmentation fault.
Solution:
Best avoid strtok(). If you like c-style, you may consider instead the use of strpbrk() with some adaptation in your code. For example:
char* get_field(char*p, char*& next, const char* s) // by ref as it's c++
{
if ((next = strpbrk(p, s)) != NULL)
*next++ = '\0';
return p;
}
with the following usage to replace strtok():
char* next;
char *chars_array = get_field(FQ_array, next, seps);
...
chars_array = get_field(next, next, seps); // instead of strtok(NULL, seps)
...
My personal recommendation, with C++, would be to consider regex expressions provided in the standard (or in boost), which would also allow for consistency check on you input data.
The full code would then look like:
regex fmt("([0-9]*,[0-9]*);(.*);(.*);(.*);(.*);(.*);(.*);(.*);(.*);([0-9]*\.*[0-9]*)");
for (int FQ_i = 0; ...)
{
smatch sm;
printf("FQ_ID_line[FQ_i]=%u\n", FQ_ID_line[FQ_i]); // ok, a cout would be better
if (regex_match(FQ_ID_line[FQ_i], sm, fmt)) {
DataLine[FQ_i].analog_comp_id = sm[2];
DataLine[FQ_i].RTU_abbr = sm[3];
DataLine[FQ_i].analog_scc_fep_group = sm[8];
DataLine[FQ_i].analog_bsc_fep_group = sm[9];
DataLine[FQ_i].RTU_number = sm[10];
DataLine[FQ_i].float_RTU_number = stof(sm[10]);
}
else
cout << " ** Non matching line ignored !!\n";
}
By fine tuning the regex, you could then check even more for consistency before assigning (Here I just did the minimum for the sake of the example).

Cannot get second while to loop properly

I'm making a function that removes elements from a string. However, I cant seem to get both of my loops to work together. The first while loop works flawlessly. I looked into it and I believe it might be because when "find_last_of" isn't found, it still returns a value (which is throwing off my loop). I haven't been able to figure out how I can fix it. Thank you.
#include <iostream>
#include <string>
using namespace std;
string foo(string word) {
string compare = "!##$";
string alphabet = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
while(word.find_first_of(compare) < word.find_first_of(alphabet)) {
int position = word.find_first_of(compare);
word = word.substr(++position);
}
while(word.find_last_of(compare) > word.find_last_of(alphabet)){
int size = word.length();
word = word.substr(0, --size);
}
return word;
}
int main() {
cout << foo("!!hi!!");
return 0;
}
I wrote it like this so compound words would not be affected. Desired result: "hi"
It's not entirely clear what you're trying to do, but how about replacing the second loop with this:
string::size_type p = word.find_last_not_of(compare);
if(p != string::npos)
word = word.substr(0, ++p);
It's not clear if you just want to trim certain characters from the front and back of word or if you want to remove every one of a certain set of characters from word no matter where they are. Based on the first sentence of your question, I'll assume you want to do the latter: remove all characters in compare from word.
A better strategy would be to more directly examine each character to see if it needs to be removed, and if so, do so, all in one pass through word. Since compare is quite short, something like this is probably good enough:
// Rewrite word by removing all characters in compare (and then erasing the
// leftover space, if any, at the end). See std::remove_if() docs.
word.erase(std::remove_if(word.begin(),
word.end(),
// Returns true if a character is to be removed.
[&](const char ch) {
return compare.find(ch) != compare.npos;
}),
word.end());
BTW, I'm not sure why there is both a compare and alphabet string in your example. It seems you would only need to define one or the other, and not both. A character is either one to keep or one to remove.

vector subscript out of range C++ (substring)

So I'm having this problem with substrings and converting them into integers. This will probably be an easy-fix but I'm not managing to find the answer.
So I receive this string "12-12-2012" and i want to split it, convert into integers and call the modifications methods like this:
string d = (data.substr(0,data.find("-")));
setDia(atoi(d.c_str()));
But it gives me the error mentioned in the title when I try to comvert into an integer.
EDIT:
Turns out that the string doesn't actually contain a '-' but this is really confusing since the string in the parameter results from this : to_char(s.diaInicio,'dd-mm-yyyy')
More information: I used the debugger and it's making the split correctly since the value that atoi receives is 12 (the first split). But I don't know why the VS can't convert into an integer even though the string passed is "12".
This code is not save in the sense that it fails when data does not contain a -.
Try this:
std::size_t p = data.find("-");
if(p == std::string::npos) {
// ERROR no - in string!
}
else {
std::string d = data.substr(0,p);
setDia(atoi(d.c_str()));
}
Please duplicate the problem with a very simple program. If what you say is correct, then the following program should also fail (taken from Danvil's example, and without calling the unknown (to us) setDia() function):
#include <string>
#include <cstdlib>
using namespace std;
int main()
{
string data = "12-12-2012";
std::size_t p = data.find("-");
if(p == std::string::npos) {
// ERROR no - in string!
}
else {
std::string d = data.substr(0,p);
atoi(d.c_str());
}
}