I have to validate my data "123-AB-12345" as correct using character array. I set char array size to 13 including '\0'. The function must return false if the condition is not satisfied. ALL I done is that the program validates these 12 chracters but IT DOESN'T RETURN FALSE WHEN I PASS MORE VALUES like as "123-AB-123456789" and it is returning true. My program is follwing:
#include<iostream>
using namespace std;
bool isValidBookId(char bookId[13]);
int main()
{
char book[13];
cin.getline(book,13);
bool id = isValidBookId(book);
cout<<id;
}
bool isValidBookId( char bookId[13] ) {
/* Valid: 098-EN-98712 */
if ( bookId[12] != '\0' )
return false;
if ( bookId[3] != '-' )
return false;
if ( bookId[6] != '-' )
return false;
for ( int i = 0; i < 3; i++ ) {
if ( bookId[i] < '0' || bookId[i] > '9' ) {
return false;
}
}
for ( int i = 4; i < 6; i++ ) {
if ( bookId[i] < 'A' || bookId[i] > 'Z' ) {
return false;
}
}
for ( int i = 7; i < 12 || bookId[12]!='\0'; i++ ) {
if(bookId[13]!='\0'){
return false;
}
if ( bookId[i] < '0' || bookId[i] > '9' ) {
return false;
}
}
return true;
}
I don't know why this condition is not working.
if ( bookId[12] != '\0' )
return false;
Looking at your code, the only explanation is that the last character of your array is null. Try specifying a delimiter character like this:
cin.getline(book, 13, '\n');
I'd refer to this link:
"A null character ('\0') is automatically appended to the written sequence if n is greater than zero, even if an empty string is extracted."
Your issue is in your input function:
You only read up to 12 characters. So you cannot have more than 12 characters.
You might use std::string
bool isValidBookId(const std::string&s) {
static const std::regex r{R"(^\d{3}-[A-Z]{2}-\d{5}$)"};
return std::regex_match(std::begin(s), std::end(s), r);
}
int main()
{
std::string s;
while (std::getline(std::cin, s))
{
std::cout << s << ": " << isValidBookId(s) << std::endl;
}
}
Demo
Or bigger buffer:
bool isValidBookId(const char (&s)[14]) {
static const std::regex r{R"(^\d{3}-[A-Z]{2}-\d{5}\0$)"};
return std::regex_match(std::begin(s), std::end(s) - 1, r);
}
int main()
{
char s[14];
while (true)
{
bool b = !!std::cin.getline(s, 14);
if (s[0] == '\0') break;
std::cout << " " << s << ": " << isValidBookId(s) << std::endl;
if (!b) {
std::cin.clear();
std::cin.ignore(255, '\n');
}
}
}
Demo
All conditions are correct. Your problem is created initially when entering data.
cin.getline(book,13);
The 'getline' method accepts any number of characters (of course within reason), but allocates space only for the first 12 characters and the 13th will always be only '\ 0'. If you want to write more characters, let me enter more characters.
The correct option is:
bool isValidBookId(char bookId[100]); // now there is a restriction of not 13 characters, but 100
int main()
{
char book[100]; // now there is a restriction of not 13 characters, but 100
cin.getline(book,100); // now there is a restriction of not 13 characters, but 100
}
bool isValidBookId( char bookId[100] ) // now there is a restriction of not 13 characters, but 100
{...}
As pointed above,
cin.getline(book,13) wont save more than 12 characters in your array.
Instead replace your code with :
char book[100]; // To save upto 99 characters
cin.getline(book,100);
And change
isValidBookId(char bookId[13])
to
isValidBookId( char bookId[100] )
and remove all the checks of
bookId[12]!='\0' inside this isValidBookId function, as there can be any character at 12th index.
Related
So writing a palindrome with pointers and boolean. I have it working with a single word but then I began building it to work with a sentence. The problem is I am unsure how to keep the new modified sentence after making it lowercase and getting rid of the spaces for it to return whether it is or isn't a palindrome. It keeps returning the palindrome as false and when I went to check why I see that the program ignores the modification and kept the original string. I can't use "&" on the parameter as I tested it out. Any hints or takes on what I can do to keep the new modified string?
int main()
{
userInput();
return 0;
}
void userInput()
{
char str[90];
std::cout<<"Please enter a string to check if it is a palindrome: ";
std::cin.getline(str, 90);
modifyString(str);
}
void modifyString(char *string)
{
int count = 0;
for (int i=0; i<strlen(string); i++)
{
putchar(tolower(string[i]));
}
for (int i = 0; string[i]; i++)
{
if (string[i] != ' ')
{
string[count++] = string[i];
}
}
string[count] = '\0';
std::cout<<string<<std::endl;
results(string);
}
bool checkPalindrome(char *string)
{
char *begin;
char *end;
begin = string;
end = (string + strlen(string)-1);
while(begin != end)
{
if ((*begin) == (*end))
{
begin ++;
end--;
}
else
{
return false;
}
}
return true;
}
void results(char *string)
{
bool isItPalindrome;
isItPalindrome = checkPalindrome(string);
if( isItPalindrome == true)
{
std::cout<<"\nCongrats, the string is a palindrome!";
}
else
{
std::cout<<"\nThis string is not a palindrome.";
}
}
For starters this definition of main
int main()
{
userInput();
return 0;
}
does not make a sense. According to the function name main the function should perform the main task that is to output whether the entered sentence is a palindrome or not.
This for loop
for (int i=0; i<strlen(string); i++)
{
putchar(tolower(string[i]));
}
does nothing useful. It just outputs the string in the lower case.
This statement
end = (string + strlen(string)-1);
can invoke undefined behavior if an empty string was passed.
This while loop
while(begin != end)
{
if ((*begin) == (*end))
{
begin ++;
end--;
}
else
{
return false;
}
}
also can invoke undefined behavior for a string containing an even number ofo characters because after this if statement
if ((*begin) == (*end))
{
begin ++;
end--;
}
if the two adjacent characters are equal then begin after incrementing will be greater than end after its decrementing. And as a result the loop will continue its iteration.
In general the approach when the original string is changed is just a bad approach.
Your program has too many functions. It is enough to write one function that will determine whether the passed string is a palindrome or not.
Here is a demonstrative program.
#include <iostream>
#include <cstring>
#include <cctype>
bool checkPalindrome( const char *s )
{
const char *t = s + std::strlen( s );
do
{
while ( s != t && std::isspace( ( unsigned char )*s ) ) ++ s;
while ( s != t && std::isspace( ( unsigned char )*--t ) );
} while ( s != t &&
std::tolower( ( unsigned char )*s ) == tolower( ( unsigned char ) *t ) &&
++s != t );
return s == t;
}
int main()
{
const size_t N = 100;
char s[N] = "";
std::cout << "Please enter a string to check if it is a palindrome: ";
std::cin.getline( s, N );
std::cout << '\n';
if ( checkPalindrome( s ) )
{
std::cout << "Congrats, the string is a palindrome!\n";
}
else
{
std::cout << "This string is not a palindrome.\n";
}
return 0;
}
Its output might look like
Please enter a string to check if it is a palindrome: 1 23 456 6 54 321
Congrats, the string is a palindrome!
Okay, I solved it!
As one of the users on here brought up a point that my lowercase did not modify the string and only prints it out. I try my best to solve the problem and I think I found the solution and everything works perfectly fine. comment back to debug it if you like to see how it looks but what I did was create a for loop again for the lower case but made another pointer with it. here how it looks.
for (char *pt = string; *pt != '\0'; ++pt)
{
*pt = std::tolower(*pt);
++pt;
}
Now that definitely changes the string into a lower case and keeps it as a lower case.
so now the modified function looks like this and ready to take any sentence palindrome you give it. Example: A nUt fOr a jAr of tUNa. We make this all lowercase and take out space and boom palindrome and return true.
void modifyString(char *string)
{
int count = 0;
for (char *pt = string; *pt != '\0'; ++pt)
{
*pt = std::tolower(*pt);
++pt;
}
for (int i = 0; string[i]; i++)
{
if (string[i] != ' ')
{
string[count++] = string[i];
}
}
string[count] = '\0';
//take out the forward slash below to see how it looks after being modified
// std::cout<<std::endl<<string<<std::endl;
results(string);
}
I have to avoid double spaces, double ! and double full stops in my character array. I must have to use character array btw.
e.g Valid data: "It is raining.!" Invalid Data: "It is raining!!." (it is only example)
I tried the following way but am not getting desired result. Plz help me.
#include<iostream>
using namespace std;
bool isValidData( char data[60] );
int main()
{
char data[60];
cin.getline(data,60);
bool name = isValidData(data);
cout<<name;
}
bool isValidData( char data[60] )
{
int i=0;
while(data[i]!='\0') {
if ( data[i]==' ' && data[i]=='.' && data[i]=='!'){
if ( data[i+1]==' ' && data[i+1]=='.' && data[i+1]=='!')
return false;
}
i++;
}
return true;
}
Your code fails because no character can simultaneously be equal to ,, !, and .
Even if you fix that, you will still flag ,! as invalid.
Test the property directly instead:
bool isValidData( char data[60] )
{
int i=0;
while(data[i]!='\0' && data[i+1]!='\0') {
if ((data[i]==' ' || data[i]=='.' || data[i]=='!') && data[i+1]==data[i]) {
return false;
}
i++;
}
return true;
}
I am creating a c++ program to validate book ID using function in c++. The program must return 1 if the input is valid and 0 if the input is invalid. INPUT Pattern: "123-AB-12345" This will be considered as a valid input. The valid input is: (a) Total characters must be 12 (b) First three characters must be integers from 1 to 9 each. (c) 4th and 7th characters must be hiphen "-". (d) Last 5 characters must be integers from 1 to 9 each.
I tried the following way but I am not getting the desired answer. Need help plz
#include<iostream>
using namespace std;
bool isValidBookId(char bookId[13]);
int main()
{
char book[13];
cin.getline(book,12);
bool id = isValidBookId(book);
cout<<id;
}
bool isValidBookId(char bookId[13])
{
int i;
bool check1,check2,check3,check4,check5,check6;
check1=check2=check3=check4=check5=true;
if(bookId[12]=='\0'){
check1=true;
}
if(bookId[3]=='-')
{
check2=true;
}
if(bookId[6]=='-')
{
check3=true;
}
for(i=0; i<3;i++){
if(bookId[i]>=0 || bookId[i]<=9)
{
check4=true;
}
}
if(bookId[i]>= 'A' || bookId[i]<= 'Z')
{
check5=true;
}
for(i=7; i<12; i++)
{
if(bookId[i]>=0 || bookId[i]<=9)
{
check6=true;
}
}
if(check1==true && check2==true && check3==true && check4==true && check5==true && check6==true)
{
return true;
}
else
{
return false;
}
}
There's a few errors that you have in your code.
First, you initialize all your checks to true, and never set anything to false, so the answer will always be true. Realistically, you want to initialize them all to false, and change to true when all the conditions are met, or assume true, and set to false when the condition is not met.
Second, your check for the values 0-9 is incorrect. You cannot compare bookId[i] to 0, you want to compare it to the character '0'. Also note that the question you have also says 1-9 not 0-9
Third, your check for A-Z is incorrect (note, this issue also applies to 0-9). You're code basically says is bookId[i] greater than or equal to 'A' OR less than or equal to Z, which is always going to be true.
I've written your code below:
bool isValidBookId( char bookId[13] ) {
if ( bookId[12] != '\0' )
return false;
if ( bookId[3] != '-' )
return false;
if ( bookId[6] != '-' )
return false;
for ( int i = 0; i < 3; i++ ) {
if ( bookId[i] < '1' || bookId[i] > '9' ) {
return false;
}
}
for ( int i = 4; i < 6; i++ ) {
if ( bookId[i] < 'A' || bookId[i] > 'Z' ) {
return false;
}
}
for ( int i = 7; i < 12; i++ ) {
if ( bookId[i] < '1' || bookId[i] > '9' ) {
return false;
}
}
return true;
}
This method doesn't require any Boolean variables. Instead, I assume true (the last return statement) and instead try to prove false. As soon as something is false, you can return without doing any other checks.
Since the given code is in C-Style format, I would like to present 2 additional solutions in C++. I think the task is anyway to think about patterns and how these could be detected.
In my first solutions I just added more C++ elements. The 2nd solution should be the correct on.
Please see:
#include <iostream>
#include <regex>
#include <string>
#include <cctype>
#include <vector>
bool isValidBookId1(const std::string& bookId) {
// Lambda to detect hyphen
auto ishyphen = [](int i){ return static_cast<int>(i == '-');};
// First check size of given string
bool result{(bookId.size() == 12)};
// Define the position of the types
std::vector<size_t> digitIndex{0,1,2,7,8,9,10,11};
std::vector<size_t> letterIndex{4,5};
std::vector<size_t> hyphenIndex{3,6};
// Check types
if (result) for (size_t index : digitIndex) result = result && std::isdigit(bookId[index]);
if (result) for (size_t index : letterIndex) result = result && std::isupper(bookId[index]);
if (result) for (size_t index : hyphenIndex) result = result && ishyphen(bookId[index]);
// Return resulting value
return result;
}
bool isValidBookId2(const std::string& bookId) {
// Define pattern as a regex
std::regex re{R"(\d{3}-[A-Z]{2}-\d{5})"};
// Check, if the book id matches the pattern
return std::regex_match(bookId, re);
}
int main()
{
// Get input from user
if (std::string line{}; std::getline(std::cin, line)) {
std::cout << "Check for valid Book input 1: " << isValidBookId1(line) << "\n";
std::cout << "Check for valid Book input 2: " << isValidBookId2(line) << "\n";
}
return 0;
}
i have this line taken from a txt file (first line in the file):
#operation=1(create circle and add to picture) name X Y radius.
why does this code doesnt take the integer 1 and puts it into k?
Circle Circle::CreateCirc(Circle c){
int k;
ifstream myfile("cmd.txt");
if (!myfile.is_open())
cout<<"Unable to open the requested file "<<endl;
string line,line2="create circle";
for (int i=1;i<countrows();i++)
{
getline(myfile,line);
if (line.find(line2)!=string::npos)
{
istringstream ss(line);
ss>>k;
cout<<k<<endl;
}
}
return c;
}
instead im getting adress memory...help plz
Because the line doesn't start with a number. You'll need to skip over the #operation= part before extracting a number.
You should check the result of the extraction, and of getline, to help identify what's going wrong when these fail.
Also, if countrows() returns the expected number of rows in the file, then your loop would miss out the last one. Either loop from zero, or while i <= countrows(); or, if you want to process every line in the file, you could simply loop while (getline(myfile,line)).
If the actual text in the file you try to read starts with "#operation=1" and you want the number 1 from that, you can't use the simple input operator. It will read the character '#' first, which isn't a digit and so the parsing will fail and k will not be initialized. And if k is not initialized, it will be of indeterminate value, and reading that value will lead to undefined behavior and seemingly random output.
You need to check that the extraction worked:
if (ss >> k)
std::cout << k << '\n';
That won't solve your problem though, as like I said above, you can't use the simple input operator here. You need to parse the string using other methods. One way might be to find the equal character '=' and get a sub-string after that to try and extract the number.
try this:
Circle Circle::CreateCirc(Circle c){
const std::streamsize ALL = std::numeric_limits< std::streamsize >::max(); // #include <limits> needed
int k;
ifstream myfile("cmd.txt");
if (!myfile.is_open())
cout<<"Unable to open the requested file "<<endl;
for (int i=1;i<countrows(); ++i, myfile.ignore(ALL,'\n') ) // skip rest of the line
{
if( myfile.ignore(ALL,'=') >> k )
{
cout<<k<<endl;
}
else
break; // read error
}
return c;
}
EDIT: A way to do it not much bit a little closer to the way you were trying to do it using atoi() rather than streams.
#include <iostream>
#include <cstdlib> // for atoi()
int main(){
std::string str = "#operation=1(create circle and add to picture) name X Y radius.";
int k;
std::string line=str, line2="(create circle";
std::size_t fnd = line.find(line2);
if (fnd!=std::string::npos)
{
k = atoi(&str[fnd-1]); // int atoi(const char *str) == argument to integer
std::cout<< k << " " << str[fnd-1] << str[fnd] << " ";
}
}
There are a few ways to extract an integer from a string but i like to filter out the digit from the string;
#include <iostream>
int main(){
std::string str = "#operation=1(create circle and add to picture) name X Y radius.";
int k = 0;
// an array of our base10 digits to filter through and compare
const char digit[] = {'0','1','2','3','4','5','6','7','8','9'};
for(int s_filter = 0; s_filter<str.size(); ++s_filter){
for(int d_filter = 0; d_filter<10; ++d_filter){
// filter through each char in string and
// also filter through each digit before the next char
if(digit[d_filter] == str[s_filter]) {
// if so the char is equal to one of our digits
k = d_filter;// and d_filter is equal to our digit
break;
} else continue;
}
}
switch(k) {
case 1:
std::cout<< "k == 1";
// do stuff for operation 1..
return 0;
case 2:
std::cout<< "k != 1";
// do more stuff
break;
//case 3: ..etc.. etc..
default:
std::cout<< "not a digit";
return 1;
}
}
// find_num.cpp (cX) 2015 adolfo.dimare#gmail.com
// http://stackoverflow.com/questions/21115457/
#include <string> // std::string
#include <cctype> // isnum
/// Find the number in 'str' starting at position 'pos'.
/// Returns the position of the first digit of the number.
/// Returns std::string::npos when no further numbers appear within 'str'.
/// Returns std::string::npos when 'pos >= str.length()'.
size_t find_num( const std::string str, size_t pos ) {
size_t len = str.length();
bool isNegative = false;
while ( pos < len ) {
if ( isdigit(str[pos]) ) {
return ( isNegative ? pos-1 : pos );
}
else if ( str[pos]=='-' ) {
isNegative = true;
}
else {
isNegative = false;
}
++pos;
}
return std::string::npos;
}
#include <cassert> // assert()
#include <cstring> // strlen();
int main() {
std::string str;
str = "";
assert( std::string::npos == find_num( str, 0 ) );
assert( std::string::npos == find_num( str, 9 ) );
str = "#operation=1(create circle and add to picture) name X Y radius.";
assert( strlen("#operation=") == find_num( str, 0 ) );
str = "abcd 111 xyx 12.33 alpha 345.12e-23";
/// 0123456789.123456789.123456789.123456789.
assert( 5 == find_num( str, 0 ) );
assert( 13 == find_num( str, 5+3 ) );
assert( 25 == find_num( str, 20 ) );
str = "abcd-111 xyx-12.33 alpha-345.12e-23";
/// 0123456789.123456789.123456789.123456789.
assert( 4 == find_num( str, 0 ) );
assert( 12 == find_num( str, 5+3 ) );
assert( 24 == find_num( str, 20 ) );
str = "-1";
assert( 0 == find_num( str, 0 ) );
assert( 1 == find_num( str, 1 ) );
assert( std::string::npos == find_num( str, 2 ) );
assert( std::string::npos == find_num( str, strlen("-1") ) );
return 0;
}
I found the code to convert a hexadecimal string into a signed int using strtol, but I can't find something for a short int (2 bytes). Here' my piece of code :
while (!sCurrentFile.eof() )
{
getline (sCurrentFile,currentString);
sOutputFile<<strtol(currentString.c_str(),NULL,16)<<endl;
}
My idea is to read a file with 2 bytes wide values (like 0xFFEE), convert it to signed int and write the result in an output file. Execution speed is not an issue.
I could find some ways to avoid the problem, but I'd like to use a "one line" solution, so maybe you can help for this :)
Edit : The files look like this :
0x0400
0x03fe
0x03fe
...
Edit : I already tried with the hex operator, but I still have to convert the string to an integer before doing so.
// This won't work as currentString is not an integer
myInt << std::hex << currentString.c_str();
This should be simple:
std::ifstream file("DataFile");
int value;
while(file >> std::hex >> value) // Reads a hex string and converts it to an int.
{
std::cout << "Value: " << std::hex << value << "\n";
}
While we are talking about files:
You should NOT do this:
while (!sCurrentFile.eof() )
{
getline (sCurrentFile,currentString);
... STUFF ...
}
This is because when you read the last line it does NOT set the EOF. So when you loop around and then read the line after the last line, getline() will fail and you will be doing STUFF on what was in currentString from the last time it was set up. So in-effect you will processes the last line twice.
The correct way to loop over a file is:
while (getline(sCurrentFile,currentString))
{
// If the get fails then you have read past EOF and loop is not entered.
... STUFF ...
}
You can probably use stringtream class's >> operator with hex manipulator.
Have you considered sscanf with the "%hx" conversion qualifier?
// convert unsigned-integer to it's hexadecimal string represention
// 0x12345678 -> '12345678'
// N is BYTE/WORD/UINT/ULONGLONG
// T is char or wchar_t
template <class N, class T> inline T* UnsignedToHexStr(N n , // [i ]
T* pcStr , // [i/o] filled with string
UINT nDigits , // [i ] number of digits in output string / 0 (auto)
bool bNullTerminate ) // [i ] whether to add NULL termination
{
if ((N)-1 < (N)1) // if type of N is floating-point / signed-integer
if (::IsDebuggerPresent())
{
::OutputDebugString(_T("UnsignedToHexStr: Incorrect type passed\n"));
::DebugBreak();
}
if (!nDigits)
nDigits= GetUnsignedHexDigits(n);
if (1 == sizeof(T))
{
const char _czIntHexConv[]= "0123456789ABCDEF";
for (int i= nDigits-1; i>= 0; i--)
{
char* pLoc= (char*)&pcStr[i];
*pLoc= _czIntHexConv[n & 0x0F];
n >>= 4;
}
}
else
{
const wchar_t _czIntHexConv[]= L"0123456789ABCDEF";
for (int i= nDigits-1; i>= 0; i--)
{
wchar_t* pLoc= (wchar_t*)&pcStr[i];
*pLoc= _czIntHexConv[n & 0x0F];
n >>= 4;
}
}
if (bNullTerminate)
pcStr[nDigits]= 0;
return pcStr;
}
// --------------------------------------------------------------------------
// convert unsigned-integer in HEX string represention to it's numerical value
// '1234' -> 0x1234
// N is BYTE/WORD/UINT/ULONGLONG
// T is char or wchar_t
template <class N, class T> inline bool HexStrToUnsigned(const T* pczSrc ,
N& n ,
bool bSpecificTerminator= false, // whether string should terminate with specific terminating char
T cTerminator = 0 ) // specific terminating char
{
n= 0;
if (!pczSrc)
return false;
while ((32 == *pczSrc) || (9 == *pczSrc))
pczSrc++;
bool bLeadZeros= *pczSrc == _T('0');
while (*pczSrc == _T('0')) // skip leading zeros
pczSrc++;
BYTE nMaxDigits= 2*sizeof(N);
BYTE nDigits = 0 ;
while (true)
{
if ( (*pczSrc >= _T('0')) && (*pczSrc <= _T('9')))
{ if (nDigits==nMaxDigits) return false; n= (n<<4) + (*pczSrc-_T('0') ); pczSrc++; nDigits++; continue; }
if ( (*pczSrc >= _T('A')) && (*pczSrc <= _T('F')))
{ if (nDigits==nMaxDigits) return false; n= (n<<4) + (*pczSrc-_T('A')+10); pczSrc++; nDigits++; continue; }
if ( (*pczSrc >= _T('a')) && (*pczSrc <= _T('f')))
{ if (nDigits==nMaxDigits) return false; n= (n<<4) + (*pczSrc-_T('a')+10); pczSrc++; nDigits++; continue; }
if (bSpecificTerminator)
if (*pczSrc != cTerminator)
return false;
break;
}
return (nDigits>0) || bLeadZeros; // at least one digit
}
If you're sure the data can be trusted from currentString.c_str(), then you could also easily do
myInt << std::hex << atoi(currentString.c_str());
If you know the data is always going to be in that format, couldn't you just do something like:
myInt << std::hex << currentString.c_str() +2; // skip the leading "0x"