User Input Validation Using Character Array in C++ - c++

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;
}

Related

prevent input for double spaces in character arrayin C++

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;
}

character array validation in C++

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.

Retrieve each token from a file according to specific criteria

I'm trying to create a lexer for a functional language, one of the methods of which should allow, on each call, to return the next token of a file.
For example :
func main() {
var MyVar : integer = 3+2;
}
So I would like every time the next method is called, the next token in that sequence is returned; in that case, it would look like this :
func
main
(
)
{
var
MyVar
:
integer
=
3
+
2
;
}
Except that the result I get is not what I expected:
func
main(
)
{
var
MyVar
:
integer
=
3+
2
}
Here is my method:
token_t Lexer::next() {
token_t ret;
std::string token_tmp;
bool IsSimpleQuote = false; // check string --> "..."
bool IsDoubleQuote = false; // check char --> '...'
bool IsComment = false; // check comments --> `...`
bool IterWhile = true;
while (IterWhile) {
bool IsInStc = (IsDoubleQuote || IsSimpleQuote || IsComment);
std::ifstream file_tmp(this->CurrentFilename);
if (this->eof) break;
char chr = this->File.get();
char next = file_tmp.seekg(this->CurrentCharIndex + 1).get();
++this->CurrentCharInCurrentLineIndex;
++this->CurrentCharIndex;
{
if (!IsInStc && !IsComment && chr == '`') IsComment = true; else if (!IsInStc && IsComment && chr == '`') { IsComment = false; continue; }
if (IsComment) continue;
if (!IsInStc && chr == '"') IsDoubleQuote = true;
else if (!IsInStc && chr == '\'') IsSimpleQuote = true;
else if (IsDoubleQuote && chr == '"') IsDoubleQuote = false;
else if (IsSimpleQuote && chr == '\'') IsSimpleQuote = false;
}
if (chr == '\n') {
++this->CurrentLineIndex;
this->CurrentCharInCurrentLineIndex = -1;
}
token_tmp += chr;
if (!IsInStc && IsLangDelim(chr)) IterWhile = false;
}
if (token_tmp.size() > 1 && System::Text::EndsWith(token_tmp, ";") || System::Text::EndsWith(token_tmp, " ")) token_tmp.pop_back();
++this->NbrOfTokens;
location_t pos;
pos.char_pos = this->CurrentCharInCurrentLineIndex;
pos.filename = this->CurrentFilename;
pos.line = this->CurrentLineIndex;
SetToken_t(&ret, token_tmp, TokenList::ToToken(token_tmp), pos);
return ret;
}
Here is the function IsLangDelim :
bool IsLangDelim(char chr) {
return (chr == ' ' || chr == '\t' || TokenList::IsSymbol(CharToString(chr)));
}
TokenList is a namespace that contains the list of tokens, as well as some functions (like IsSymbol in this case).
I have already tried other versions of this method, but the result is almost always the same.
Do you have any idea how to improve this method?
The solution for your problem is using a std::regex. Understanding the syntax is, in the beginning, a little bit difficult, but after you understand it, you will always use it.
And, it is designed to find tokens.
The specific critera can be expressed in the regex string.
For your case I will use: std::regex re(R"#((\w+|\d+|[;:\(\)\{\}\+\-\*\/\%\=]))#");
This means:
Look for one or more characters (That is a word)
Look for one or more digits (That is a integer number)
Or look for all kind of meaningful operators (Like '+', '-', '{' and so on)
You can extend the regex for all the other stuff that you are searching. You can also regex a regex result.
Please see example below. That will create your shown output from your provided input.
And, your described task is only one statement in main.
#include <iostream>
#include <string>
#include <algorithm>
#include <regex>
// Our test data (raw string) .
std::string testData(
R"#(func main() {
var MyVar : integer = 3+2;
}
)#");
std::regex re(R"#((\w+|\d+|[;:\(\)\{\}\+\-\*\/\%\=]))#");
int main(void)
{
std::copy(
std::sregex_token_iterator(testData.begin(), testData.end(), re, 1),
std::sregex_token_iterator(),
std::ostream_iterator<std::string>(std::cout, "\n")
);
return 0;
}
You try to parse using single loop, which makes the code very complicated. Instead i suggest something like this:
struct token { ... };
struct lexer {
vector<token> tokens;
string source;
unsigned int pos;
bool parse_ident() {
if (!is_alpha(source[pos])) return false;
auto start = pos;
while(pos < source.size() && is_alnum(source[pos])) ++pos;
tokens.push_back({ token_type::ident, source.substr(start, pos - start) });
return true;
}
bool parse_num() { ... }
bool parse_comment() { ... }
...
bool parse_whitespace() { ... }
void parse() {
while(pos < source.size()) {
if (!parse_comment() && !parse_ident() && !parse_num() && ... && !parse_comment()) {
throw error{ "unexpected character at position " + std::to_string(pos) };
}
}
}
This is standard structure i use, when lexing my files in any scripting language i've written. Lexing is usually greedy, so you don't need to bother with regex (which is effective, but slower, unless some crazy template based implementation). Just define your parse_* functions, make sure they return false, if they didn't parsed a token and make sure they are called in correct order.
Order itself doesn't matter usually, but:
operators needs to be checked from longest to shortest
number in style .123 might be incorrectly recognized as . operator (so you need to make sure, that after . there is no digit.
numbers and identifiers are very lookalike, except that identifiers starts with non-number.

Code checking the result of std::unordered_set::find won't compile

I am writing a program to determine whether all characters in a string are unique or not. I am trying to do this using an unordered_set. Here is my code:
#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
bool uniqueChars(string word) {
unordered_set<char> set;
for (int i = 0; i < word.length(); i++) {
auto character = set.find(word[i]);
// if word[i] is found in set then not all chars are unique
if (character == word[i]) {
return false;
}
//else add word[i] to set
else {
set.insert(word[i]);
}
}
return true;
}
int main() {
string word;
getline(cin, word);
bool result = uniqueChars(word);
return 0;
}
It is giving me this error:
|15|error: no match for 'operator==' (operand types are 'std::__detail::_Node_iterator' and 'char')|
I believe that means that character is not comparable to word[i], but I'm not sure.
How do I make this work?
Note that std::unordered_set::find returns an iterator, not the element. It can't be compared to the element directly.
You could check whether the element was found or not by comparing the iterator with std::unordered_set::end. e.g.
auto character = set.find(word[i]);
// if word[i] is found in set then not all chars are unique
if (character != set.end()) {
return false;
}
//else add word[i] to set
else {
set.insert(word[i]);
}
BTW: Better not to use set as the name of variable, which is the name of another STL container.
Take advantage of the return value of insert. It tells you whether a duplicate was found during insertion (in which case nothing is inserted).
bool uniqueChars(string word) {
unordered_set<char> set;
for ( char c : word ) {
if ( ! set.insert( c ).second ) {
return false; // set didn't insert c because of a duplicate.
}
}
return true; // No duplicates.
}
However, this isn't as efficient as it might look. unordered_set is a heap-based hash table and its implementation is fairly heavyweight. A lightweight bit-vector works well for classifying characters.
#include <bitset>
constexpr int char_values = numeric_limits< char >::max()
- numeric_limits< char >::min() + 1;
bool uniqueChars(string word) {
bitset< char_values > set;
for ( char c : word ) {
int value_index = c - numeric_limits< char >::min();
if ( set[ value_index ] ) {
return false;
} else {
set[ value_index ] = true;
}
}
return true; // No duplicates.
}
*character == word[i]
( This is the way to access the characters but it is not needed and it
should be guided by a check whether it points to the past to the last
element)
The *charcater is basically referencing the already inserted charcater.
if(character != set1.end() )
return false; // as we are sure that it is not unique character string
You have to dereference it. but in that case you also need to do the whether it return iterator pointing to `set::end``.
By the way there is a really a simple way to do what you are trying to do.
bool uniqueChars(string word) {
unordered_set<char> set1;
for (int i = 0; i < word.length(); i++)
auto character = set1.insert(word[i]);
return set1.size()==word.length();
}
"set" is a key word in c++

Nested for-loop solution

I made this program just out of interest and wanted to make it better. My problem is that I want to make a nested for-loop to carry out the iterations but I can't get my head around it, I have tried many times but my head is melting. Any help would be greatly appreciated. Also for some reason on windows and openSuse (from what I have seen) the program prints out some random characters after the expected output, a solution to this would be a great bonus. Thanks !
Sorry I didn't make it clearer, the point of the code is to be able to theoretically generate every combination of letters from AAAAAAAA to ZZZZZZZZ.
1) No it's not homework
#include <iostream>
using namespace std;
int main()
{
char pass [] = {'A','A','A','A','A','A','A','A'};
while(pass[0] != '[')
{
pass[7]++;
if(pass[7]=='[')
{
pass[6]++;
pass[7] = 'A';
}
if(pass[6] == '[')
{
pass[6] = 'A';
pass[5]++;
}
if(pass[5] == '[')
{
pass[5] = 'A';
pass[4]++;
}
if(pass[4] == '[')
{
pass[4] = 'A';
pass[3]++;
}
if(pass[3] == '[')
{
pass[3] = 'A';
pass[2]++;
}
if(pass[2] == '[')
{
pass[2] = 'A';
pass[1]++;
}
if(pass[1] == '[')
{
pass[1] = 'A';
pass[0]++;
}
cout << pass << endl;
}
return 0;
}
Maybe like this:
const char char_first = 'A';
const char char_last = '[';
const unsigned int passlen = 8;
while (pass[0] != char_last)
{
++pass[passlen - 1];
for (unsigned int i = passlen - 1; i != 0; --i)
{
if (pass[i] == char_last)
{
++pass[i - 1]; // OK, i is always > 0
pass[i] = char_first;
}
}
}
For printing, include <string> and say:
std::cout << std::string(pass, passlen) << std::endl;
I took the liberty of making a few of the magic numbers into constants. If you're ever going to refactor this into a separate function, you'll see the merit of this.
Since (to output it) you use pass as a C string, it should be null terminated. Since it is not, garbage is printed. So you could define it as:
char pass [] = {'A','A','A','A','A','A','A','A','\0'};
or simpler
char pass[] = "AAAAAAAAA";
I'd forget about carrying on my own and just convert to/from numbers. What you're doing here is basically printing a numbers whose digits range from 'A' to ']', mappable to 0-28 via the magic of ASCII (why no ^ in passwords?)
Printing the number of anything then really boils down to
#include <iostream>
#include <cmath>
using namespace std;
std::string format(long num, int ndigits) {
if(ndigits == 0) {
return "";
} else {
char digit = 'A' + num % 28;
return format(num / 28, ndigits - 1) + digit;
}
}
int main()
{
for(int i = 0 ; i < powl(28,8) ; ++i) {
cout << format(i, 8) << endl;
}
}
You may still want to work in a char array instead of producing a billion temporary strings if you're serious about the loop, but the principle stays the same.
First try to find the common parts in the expressions looking like
if(pass[7]=='[')
{
pass[6]++;
pass[7] = 'A';
}
You should think along a line like "There's always the same number here, and a one-lower number there". Then, you replace that notion of a number with a variable and find out which range the variable has. KerrekSB gave you a solution, try to arrive at similar code from your own reasoning.
You just have to play a bit with your while and make it fit a for-loop.
while(pass[0] != '[') becomes for (i=0; pass[0] != '['; i++)
then you can replace all ifs with only one:
if(pass[i+1] == '[')
{
pass[i+1] = 'A';
pass[i]++;
}
How did we come to that conclusion? Well if you check all your if-statements all that changes between them is the indices. You can see clearly that pattern so you just replace the indices with a variable.
For starters, this is definitely not a case for a nested loop. In fact,
your entire code boils down to:
pass = initialPattern();
while ( isValidPattern( pass ) ) {
nextPattern( pass );
std::cout << pass << std::endl;
}
(But I wonder if you don't really mean to do the output before the
increment.)
Now all you have to do is define the type of pass and relevant
functions; you might even consider
putting everything in a class, since all of the functions operate on the
same data instance.
Judging from your code, pass should be an std::string with 8
characters; the initialization could be written:
std::string pass( 8, 'A' );
isValidPattern apparently only looks at the first character. (I'm not
sure that's correct, but that's what your code does.) Something like:
bool
isValidPattern( std::string const& pattern )
{
return pattern[0] != '[';
}
according to your code, but something like:
struct NotIsUpper
{
bool operator()( char ch ) const
{
return ! ::isupper( static_cast<unsigned char>( ch ) );
}
};
bool
isValidPattern( std::string const& pattern )
{
return pattern.size() == 8
&& std::find_if( pattern.begin(), pattern.end(), NotIsUpper() )
== pattern.end();
}
would seem more appropriate. (Of course, if you're doing any sort of
coding with text, you'd already have NotIsUpper and its siblings in
your tool kit.)
Finally, nextPattern seems to be nothing more than a multi-digit
increment, where the data is stored in big-endian order. So the
following (classical) algorithm would seem appropriate:
void
nextPattern( std::string& pattern )
{
static char const firstDigit = 'A';
static char const lastDigit = 'Z';
static std::string const invalidPattern( 1, '[' );
std::string::reverse_iterator current = pattern.rbegin();
std::string::reverse_iterator end = pattern.rend();
while ( current != end && *current == lastDigit ) {
*current = firstDigit;
++ current;
}
if ( current != end ) {
++ *current;
} else {
pattern = invalidPattern;
}
}
Formally, there is no guarantee in the standard that the letters will
be encoded in sequential ascending order, so for maximum portability,
you probably should in fact use an std::vector<int> with values in the
range [0, 26), and map those to letters just befor output. This
would be trivial if you put all of these operations in a class, since
the internal representation wouldn't be visible to the client code.
Something like:
class PatternGenerator
{
std::vector<int> myData;
public:
explicit PatternGenerator()
: myData( 8, 0 )
{
}
void next()
{
static int const lastDigit = 26;
std::vector<int>::reverse_iterator current = pattern.rbegin();
std::vector<int>::reverse_iterator end = pattern.rend();
while ( current != end && *current == lastDigit - 1 ) {
*current = 0;
++ current;
}
if ( current != end ) {
++ *current;
} else {
myData.front() = lastDigit;
}
}
bool isValid() const
{
return myData.front() < lastDigit;
}
friend std::ostream& operator<<(
std::ostream& dest, PatternGenerator const& obj )
{
static char const characterMap[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ( std::vector<int>::iterator current = obj.myData.current();
current != obj.myData.end():
++ current ) {
dest << characterMap[*current];
}
return dest;
}
};
(Note that things like isValid become simpler, because they can depend on the class invariants.)
Given this, all you have to write is:
int
main()
{
PatternGenerator pass;
while ( pass.isValid() ) {
std::cout << pass << std::endl;
pass.next();
}
return 0;
}
To do nested loops, you need to turn it inside-out.
You've written the code thinking as follows: go through all the possibilities for the last symbol, then change the second-last once and go back, etc. That's like counting up from 1, getting to 10 and putting a 1 in the tens column, etc.
Nested loops work the other way: go through the possibilities for the first symbol, allowing the inner loops to take care of possibilities for the other symbols each time. i.e., "list all those numbers, in order, that start with 0 in the millions place, then the ones that start with 1 etc.". In the outermost loop, you just set that value for the first digit, and the nested loops take care of the rest.