Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 9 years ago.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
please, how I can convert this:
char infix[] = "123+354*87/156=" (can be variable)
How to separate number's from this string (to integers, like 123 354 87 156, no to 1 2 3 3 5 4...) and char (chars + * / ).
I suppose you need to build a simple calculator... If you're planning to do this from scratch, you gonna need some background from Compiling Theory, and use concepts like Finite State Machine, Parsing, etc.
But there are a lot of tools that can make this task easier: lex/yacc (C), flex/bison (C++) or COCO/R (many languages).
This is a SIMPLE example in C that splits the string in numbers (state=NUM) and symbols (state=SYM):
#include <string.h>
#include <ctype.h>
#define NONE 0
#define NUM 1
#define SYM 2
int _tmain(int argc, char* argv[])
{
char infix[] = "123+354*87/156=";
char buffer[10];
int i, j;
int state = NONE;
char c;
i = 0;
j = 0;
while(i < strlen(infix)) {
c = infix[i];
switch(state) {
case NUM:
if ( isdigit(c) ) {
buffer[j++] = c;
buffer[j] = 0;
i++;
}
else {
printf("%s\n", buffer);
j = 0;
state = NONE;
}
break;
case SYM:
i++;
printf("%c\n", c);
state = NONE;
break;
case NONE:
if ( isdigit(c) ) state = NUM;
else state = SYM;
break;
}
}
getchar();
return 0;
}
When you use C++ and each operator has a length of one char, and string has the form "number operator number operator ... number operator" (that means begins with number, ends with operator and switches always between both) then use istringstream:
#include <sstream>
using namespace std;
void main()
{
char infix[] = "123+345*87/156=";
istringstream is(infix);
double nums[999]; // maybe you need more than 999
char chars[999];
int nums_pos = 0;
int chars_pos = 0;
bool number = true; // begin with number
while (!is.eof())
{
if (number)
{
is >> nums[nums_pos];
nums_pos++;
number = false;
}
else
{
is >> chars[chars_pos];
chars_pos++;
number = true;
}
}
// you got nums_pos-1 numbers and chars_pos chars
}
Here's yet another possible way but in C as you asked...
#include <stdio.h>
main()
{
char infix[] = "123+354*87/156=";
int curVal = 0;
// For each character in infix
for(char *p = infix; *p != '\0'; ++p)
{
// If it is not a ascii numeral
if(*p > '9' || *p < '0')
{
// Output value
printf("%d\n", curVal/10);
// Output char
printf("%c\n", *p);
curVal = 0;
}
else
{
// Accumulate the individual digits
curVal += (*p) - '0';
curVal *= 10;
}
}
}
It will output:
123
+
354
*
87
/
156
=
You can do this as
#include<stdio.h>
int main()
{
char infix[] = "123+354*87/156=";
char *p = infix;
while(1)
{
if(*p == '\0')
break;
if(*p == '+' || *p == '*' ||*p == '/' ||*p == '=' || *p == '-')
{
printf(" ");
}
else
printf("%c", *p);
p++;
}
printf("\n");
return 0;
}
Output:
123 354 87 156
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I need to count how many strings there is in a "string list". Each string ends as usual with a NUL char ('\0'), and the list ends with two NUL chars in succession.
I wrote a function but I keep getting a segmentation fault:
int numStrsInList(const char* strList) {
int count = 0;
int flag = 0;
if(!(*strList))
return -1;
while (flag != 2) {
if (!(*strList)) {
count++;
flag++;
}
else
flag = 0;
strList++;
}
return count;
}
for example:
const char* empty = "\0";
const char* one = "Hell0 \t\n\v\f\rw0r1d\0";
const char* two = "Hello\0 \t\0";
const char* simple = "Hello\0world\0!\0";
the invocation for example:
numStrsInList(empty)
numStrsInList(one)
numStrsInList(two)
numStrsInList(simple)
for this strings the output should be:
0
1
2
3
There are several issues with your code....
int numStrsInList(const char* strList) {
int count = 0;
int flag = 0;
if(!(*strList)) // this is not right, numStrsInList("\0") returns -1 instead of 0
return -1; // did you mean if (!strlist) ??
while (flag != 2) {
if (!(*strList)) { // maybe using this notation if (!strlist[0])
count++; // would help in avoiding the error above
flag++; // c library has strlen() functions
} // that are much faster and will make your code more readable
else
flag = 0;
strList++;
}
return count;
}
}
Compare to, overall length added per request :)
int numStrsInList(const char* strList, int maxlen)
{
// returns the number of strings in a null terminated array of
// contiguous null-terminated strings.
// maxlen is the maximum overall length of the buffer,
// can be 0 to defeat length checking
const char* s;
int result = 0;
if (!strList) return -1;
for (s = strlist;
s > (char*)1 && s[0] != 0;
s = (maxlen) ? (memchr(s, 0, maxlen - (s - strlist)) + 1)
: (s + strlen(s) + 1) )
{
if ((s - strlist) > maxlen) return -1;
++result;
}
return result;
}
Just use standard C function strchr declared in the header <string.h>.
For example
#include <stdio.h>
#include <string.h>
size_t numStrsInList(const char *s)
{
size_t n = 0;
if (!(s[0] == '\0' && s[1] == '\0'))
{
do
{
s = strchr(s, '\0');
++n;
} while (*++s);
}
return n;
}
int main( void )
{
printf("The number of substrings is %zu\n", numStrsInList("\0"));
printf("The number of substrings is %zu\n", numStrsInList("Hell0 \t\n\v\f\rw0r1d\0")) ;
printf("The number of substrings is %zu\n", numStrsInList("Hello\0 \t\0"));
printf("The number of substrings is %zu\n", numStrsInList("Hello\0world\0!\0"));
}
The program output is
The number of substrings is 0
The number of substrings is 1
The number of substrings is 2
The number of substrings is 3
Without using the standard function strchr the function can be implemented the following way
size_t numStrsInList(const char *s)
{
size_t n = 0;
if (!(s[0] == '\0' && s[1] == '\0'))
{
do
{
while (*s) ++s;
++n;
} while (*++s);
}
return n;
}
Take into account that for example this string
"\0A\0"
contains two substrings: "" and "A". While this string "\0" contains neither substring.
As for your code then already this statement
if(!(*strList))
return -1;
does not make sense.
It seems you mean
if(!strList)
return -1;
that is that the pointer strList is not equal to NULL. However by analogy with standard string functions it is better when the caller checks whether the pointer is equal to NULL.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
my first time here, im a very novice student in programming and I have a problem I cant find any solution for.
I’m writing a code to do vigenere cipher but I have problems with it:
First: Key is to be entered, let’s say that the key is; “aaa”
Second: Text to be encrypted, let’s say that the text is:”alligator”
The cipher should be:
alligator
+
aaa|aaa|aaa (key to rerun each extra letter in the text vs key)
a
+
a
ciphered first letter;
b
all the text:
bmmjhbups
My problem is how to loop through alligator with the shorter abc? In all my attempts the abc becomes zero when looping pass it instead of starting from the beginning when the loop for the text is passed the length of the abc.
I have also tried with strcpy and concatenate so that the abc becomes the same strlength at alligator but i meet problems in the strcpy and cat metodes due to strange symbols in the begging of the string.
Do any have a easy solution regarding how a loop can work through a bigger loop ?
Although I probably shouldn't post this (as no code sample was provided), here's a piece of code that does what you want. However, a couple of ideas:
According to Vigenere wiki, each letter has a number associated with it:
a .. 0
b .. 1
...
z .. 25
and when adding 2 letters their values are added and then the % 26 (modulo) is applied to the result. E.g. 'a' + 'a' = (0 + 0) % 26 = 0 which is also a (which is different than b that you are expecting), that's why I had to add the CUSTOM_OFFSET shifting every result from the table (forward) by 1.
The cipher works either with upper or lower case letters, not with both (or any other characters). Probably some text and key validation routines could be added. Anyway in this case, lowercase are used.
Code:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#define BASE_CHR 'a' //Change it to 'A' if working with uppercases (can't have both)
#define ALPHABET_LEN ('z' - 'a' + 1)
#define CUSTOM_OFFSET 1 //According to specs should be 0, but it wouldn't match the current requirements
int main() {
char *text = "alligator", *key = "aaa", *new_text = NULL;
size_t text_len = strlen(text), key_len = strlen(key), i = 0;
if ((new_text = (char*)calloc(text_len + 1, sizeof(char))) == NULL) {
printf("Malloc error\n");
return 1;
}
for (i = 0; i < text_len; i++)
new_text[i] = ((text[i] - BASE_CHR + key[i % key_len] - BASE_CHR + CUSTOM_OFFSET) % ALPHABET_LEN) + BASE_CHR;
printf("Text: %s, Key: %s, Crypted: %s\n", text, key, new_text);
free(new_text);
new_text = NULL;
return 0;
}
Output:
Text: alligator, Key: aaa, Crypted: bmmjhbups
Please have a look at this program. I think it does what you want.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define NUMLETTERS 26
#define BUFSIZE 4096
char *get_input(void);
int main(int argc, char *argv[])
{
char sign = 1;
char const plainmsg[] = "Plain text: ";
// Convert argument into array of shifts
char const *const restrict key = "bbb";
size_t const keylen = strlen(key);
char shifts[keylen];
char const *restrict plaintext = NULL;
for (size_t i = 0; i < keylen; i++) {
if (!(isalpha(key[i]))) {
fprintf(stderr, "Invalid key\n");
return 2;
}
char const charcase = (char) (isupper(key[i])) ? 'A' : 'a';
// If decrypting, shifts will be negative.
// This line would turn "bacon" into {1, 0, 2, 14, 13}
shifts[i] = (key[i] - charcase) * sign;
}
do {
fflush(stdout);
// Print "Plain text: " if encrypting and "Cipher text: " if
// decrypting
printf("%s", plainmsg);
plaintext = get_input();
if (plaintext == NULL) {
fprintf(stderr, "Error getting input\n");
return 4;
}
} while (strcmp(plaintext, "") == 0); // Reprompt if entry is empty
size_t const plainlen = strlen(plaintext);
char* const restrict ciphertext = calloc(plainlen + 1, sizeof *ciphertext);
if (ciphertext == NULL) {
fprintf(stderr, "Memory error\n");
return 5;
}
for (size_t i = 0, j = 0; i < plainlen; i++) {
// Skip non-alphabetical characters
if (!(isalpha(plaintext[i]))) {
ciphertext[i] = plaintext[i];
continue;
}
// Check case
char const charcase = (isupper(plaintext[i])) ? 'A' : 'a';
// Wrapping conversion algorithm
ciphertext[i] = ((plaintext[i] + shifts[j] - charcase + NUMLETTERS) % NUMLETTERS) + charcase;
j = (j+1) % keylen;
}
ciphertext[plainlen] = '\0';
printf("%s\n", ciphertext);
free(ciphertext);
// Silence warnings about const not being maintained in cast to void*
free((char*) plaintext);
return 0;
}
char *get_input(void) {
char *const restrict buf = malloc(BUFSIZE * sizeof (char));
if (buf == NULL) {
return NULL;
}
fgets(buf, BUFSIZE, stdin);
// Get rid of newline
size_t const len = strlen(buf);
if (buf[len - 1] == '\n') buf[len - 1] = '\0';
return buf;
}
Test
Plain text: alligator
bmmjhbups
However, I think that you might have misunderstood the key, the key aaa will leave the plain text unchanged, if I understood correctly, but the key bbb will shift the positions one step down in the alphabet. Watch for corner cases such as shifting Z down or shifting A up.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I have no idea how to go further with this program that I'm about to create. This is the idea:
Validate the password input to check if the password has at least one
uppercase letter, lowercase letter and a number.
Some parts of it is broken at the moment. For instance the false, true statements. And the "undynamic" char array in the main function. I don't know how to make that at the moment either. But it explains what I'm looking for.
So how can I validate this without writing too much code?
This is my current code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int passval(char pw[])
{
int x;
for (x = 0; x < sizeof(pw); x++) {
if ( (isalnum(pw[x])) || (ispunct(pw[x])) ) {
return 0;
} else {
return 1;
}
}
return 0;
}
int main()
{
char password[20];
printf("Enter password: ");
scanf("%s", password);
if (passval(password) == TRUE) {
printf("Password is TRUE");
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int
password_validate(const char *pass)
{
int upper = 0, lower = 0, digit = 0;
if (pass == NULL || strlen(pass) == 0)
return -1;
do
{
if (isupper(*pass))
upper = 1;
if (islower(*pass))
lower = 1;
if (isdigit(*pass))
digit = 1;
} while ((!lower || !upper || !digit) && *(++pass));
return (*pass != '\0' ? 0 : (upper == 0 ? -2 : (lower == 0 ? -3 : -4)));
}
Please view the below link to a code sample to be sure to understand some of the corner cases (thank you Alex Pogue for highlighting additional cases) and how this function handles them.
https://ideone.com/GiOGkj
Scan for the string.
If the characters to be found exists, raise flags.
Say "valid" iff all of the required flags are raised.
Example implementation:
#include <ctype.h>
int passval(const char pw[])
{
size_t x;
unsigned char c; /* making this unsigned is important:
char may be negative and passing it to isupper(), etc. may invoke undefined behavior */
int upperExists = 0, lowerExists = 0, numberExists = 0;
for (x = 0; pw[x] != '\0'; x++) {
/* fetch the character */
c = pw[x];
/* raise flags when the character is required kind */
upperExists = upperExists || isupper(c);
lowerExists = lowerExists || islower(c);
numberExists = numberExists || isdigit(c);
}
/* check if all of required flags are raised */
return upperExists && lowerExists && numberExists;
}
Arrays that are passed to a function decay into pointers so you cannot do sizeof() in your function, instead pass the string length strlen()
I would do something like this
#include <ctype.h>
#include <stdbool.h>
bool validatePassword(const char* pw, const int len)
{
int x;
bool upperCase = false;
bool lowerCase = false;
bool number = false;
for (x = 0; x < len; x++)
{
if ( pw[x] == toupper(pw[x]) )
{
upperCase = true;
}
else if ( pw[x] == tolower(pw[x]) )
{
lowerCase = true;
}
else if ( isdigit(pw[x]) )
{
number = true;
}
}
return upperCase && lowerCase && number;
}
My solution is based on parsing a string that ends with a \0 character, check for at least one capital char, small char, and a digit like how an OR gate functions..
Try Online
int passval(char * p)
{
int capital=0, small=0, digit=0;
while (*p && !(capital && small && digit))
capital = (*p>='A' && *p<='Z' ? 1 : capital),
small = (*p>='a' && *p<='z' ? 1 : small ),
digit = (*p>='0' && *p<='9' ? 1 : digit ),
p++ ;
return capital && small && digit;
}
I'm trying to create a calculator in c, which can calculate with priority and get right results for examples like these:
((5+5)/3)*3) -- > 9
((1+2) * 3) -- > 9
These examples my code below can calculate. But for something like this
(2+5) * (2+5), my program gives wrong answer.
I'm using 2 stacks. One for operators and one for numbers. It works on this principle:
follows:
((4 - 2) * 5) + 3 --> normal infix expression:
+ * - 4 2 5 3
Pseudo code:
Read + (an operation), push it onto the stack,
Read * (an operation), push it onto the stack,
Read - (an operation), push it onto the stack,
Read 4 (a number), the top of the stack is not a number, so push it onto the stack.
Read 2 (a number), the top of the stack is a number, so pop from the stack twice, you get 4 - 2, calculate it (2), and push the result (2) onto the stack.
Read 5 (a number), the top of the stack is a number, so pop from the stack twice, you get 2 * 5, push the result (10) onto the stack.
Read 3 (a number), the top of the stack is a number, so pop from the stack twice, you get 3 + 10, push the result (13) onto the stack.
Nothing left to read, pop from the stack and return the result (13).
Actual code:
#include <stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 102
typedef struct
{
char stk[MAXSIZE];
int top;
}STACK;
typedef struct stack
{
int stk[MAXSIZE];
int itop;
}INT_STACK;
STACK s;
INT_STACK a;
void push(char);
char pop(void);
void display(void);
int main()
{
a.itop = 0;
char string[MAXSIZE],vyb,vyb2;
int cislo1,cislo2,vysledok;
while (gets(string) != NULL){
for(int j = strlen(string); j > 0; j--){
if(string[j] == '*' || string[j] == '/' || string[j] == '+' || string[j] == '-')
push(string[j]);
}
//display();
for(int j = 0; j < strlen(string); j++){
if(isdigit(string[j])&&!(a.itop)){
//display();
char pomoc[2];
pomoc[0] = string[j];
pomoc[1] = '\0';
int_push(atoi(pomoc));
}
else if(isdigit(string[j])&&(a.itop)){
cislo1 = int_pop();
vyb2 = pop();
char pomoc[2];
pomoc[0] = string[j];
pomoc[1] = '\0';
cislo2 = atoi(pomoc);
if(vyb2 == '+')
vysledok = cislo1+cislo2;
else if(vyb2 == '-')
vysledok = cislo1-cislo2;
else if(vyb2 == '*')
vysledok = cislo1*cislo2;
else if(vyb2 == '/')
vysledok = cislo1 / cislo2;
//printf(" v %d",vysledok);
int_push(vysledok);
}
}
printf("%d\n",int_pop());
}
}
/* Function to add an element to the stack */
void push (char c)
{
s.top++;
s.stk[s.top] = c;
//printf ("pushed element is = %c \n", s.stk[s.top]);
}
/* Function to delete an element from the stack */
char pop ()
{
char num = s.stk[s.top];
// printf ("poped element is = %c\n", s.stk[s.top]);
s.top--;
return(num);
}
int empty()
{
if (s.top == - 1)
{
printf ("Stack is Empty\n");
return (s.top);
}
return 1;
}
void display ()
{
int i;
if (!empty)
{
printf ("Stack is empty\n");
return;
}
else
{
printf ("\n The status of the stack is \n");
for (i = s.top; i >= 0; i--)
{
printf ("%c\n", s.stk[i]);
}
}
printf ("\n");
}
void int_push (int c)
{
a.itop++;
a.stk[a.itop] = c;
//printf ("pushed element is = %d \n", a.stk[a.itop]);
}
/* Function to delete an element from the stack */
int int_pop ()
{
int num = a.stk[a.itop];
// printf ("poped element is = %d\n", a.stk[a.itop]);
a.itop--;
return(num);
}
Is there any other way to create a calculator with priority, which can give good answers?
Thanks for your respond
Put breakpoints - you'll get the following expression:
+ + * 2 5 2 5. The problem with that, is your interpreter is interpeting this as (2+5+2)*5 instead of (2+5) * (2+5).
Well then, you might be wondering how to solve this. There's no simple single solution - you could either fix your own interpreter or build a whole new mechanic, because the way you build expressions just can't handle more then one pair of parthesises.
For example, you may want to calculate all the values in parnthesises before even building the expression seperatley, possibly using recursion in the case of parenthesiseception - however if you actually choose to use that method, you might want to change the way you work with the expressions entirely, because that's a different approach.
If you need me to show actual code examples to explain this further using parts of the code you made, just ask for it and i'll edit and provide what you need.
Either way, I really advise you to look up working with interpreters in general - you could really learn a lot about analysing strings and working with different inputs, and people even did similar stuff to yours with calculators before
EDIT: you asked for examples, so here you go - this is an example of a completely different method using recursion. This way, you handle a single pair of parenthesises at a time, and thus you won't have the problem you currently do. Note - the source i'm basing this on ( pretty much copy-pasted with edits from the thread and some personal comments ) is from codereview on stack exchange, you can see it here
if you're intrested.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getInput(char * in) {
printf("> ");
fgets(in, 256, stdin);
}
int isLeftParantheses(char p) {
if (p == '(') return 1;
else return 0;
}
int isRightParantheses(char p) {
if (p == ')') return 1;
else return 0;
}
int isOperator(char p) {
if (p == '+' || p == '-' || p == '*' || p == '/') return p;
else return 0;
}
int performOperator(int a, int b, char p) {
switch(p) {
case '+': return a+b;
case '-': return a-b;
case '*': return a*b;
case '/':
if (b == 0) { printf("Can't divide by 0, aborting...\n"); exit(1); } // now we dont want the world to expload here do we.
return a/b;
default:
puts("Bad value in switch.\n"); // A replacement which was mentioned in the thread- better have a default response just in case something goes wrong.
break;
}
return 0;
}
char isDigit(char p) {
if (p >= '0' && p <= '9') return 1;
else return 0;
}
int charToDigit(char p) {
if (p >= '0' && p <= '9') return p - '0';
else return 0;
}
int isNumber(char * p) {
while(*p) {
if (!isDigit(*p)) return 0;
p++;
}
return 1;
}
int len(char * p)
{
return (int) strlen(p); // This was bugged in the source, so I fixed it like the thread advised.
}
int numOfOperands(char * p) {
int total = 0;
while(*p) {
if (isOperator(*p)) total++;
p++;
}
return total+1;
}
int isMDGRoup(char *p)
{
for(; *p; p++) // used to be a while loop in the source, but this is better imho. more readable, also mentioned on the thread itself.
{
if (!isDigit(*p) && *p != '/' && *p != '*') return 0;
}
return 1;
}
int getLeftOperand(char * p, char * l) {
// Grab the left operand in p, put it in l,
//and return the index where it ends.
int i = 0;
// Operand is part of multi-*/ group
if (isMDGRoup(p)) {
while(1) {
if (*p == '*' || *p == '/') break;
l[i++] = *p++;
}
return i;
}
// Operand is in parantheses (so that's how you write it! sorry for my bad english :)
if(isLeftParantheses(*p)) {
int LeftParantheses = 1;
int RightParantheses= 0;
p++;
while(1) {
if (isLeftParantheses(*p)) LeftParantheses++;
if (isRightParantheses(*p)) RightParantheses++;
if (isRightParantheses(*p) && LeftParantheses == RightParantheses)
break;
l[i++] = *p++;
}
// while (!isRightParantheses(*p)) {
// l[i++] = *p++;
// }
l[i] = '\0';
return i+2;
}
// Operand is a number
while (1) {
if (!isDigit(*p)) break;
l[i++] = *p++;
}
l[i] = '\0';
return i;
}
int getOperator(char * p, int index, char * op) {
*op = p[index];
return index + 1;
}
int getRightOperand(char * p, char * l) {
// Grab the left operand in p, put it in l,
//and return the index where it ends.
while(*p && (isDigit(*p) || isOperator(*p) ||
isLeftParantheses(*p) || isRightParantheses(*p))) {
*l++ = *p++;
}
*l = '\0';
return 0;
}
int isEmpty(char * p) {
// Check if string/char is empty
if (len(p) == 0) return 1;
else return 0;
}
int calcExpression(char * p) {
// if p = #: return atoi(p)
//
// else:
// L = P.LeftSide
// O = P.Op
// R = P.RightSide
// return PerformOp(calcExpression(L), calcExpression(R), O)
// ACTUAL FUNCTION
// if p is a number, return it
if (isNumber(p)) return atoi(p);
// Get Left, Right and Op from p.
char leftOperand[256] = ""; char rightOperand[256]= "";
char op;
int leftOpIndex = getLeftOperand(p, leftOperand);
int operatorIndex = getOperator(p, leftOpIndex, &op);
int rightOpIndex = getRightOperand(p+operatorIndex, rightOperand);
printf("%s, %c, %s", leftOperand, op, rightOperand);
getchar();
if (isEmpty(rightOperand)) return calcExpression(leftOperand);
return performOperator(
calcExpression(leftOperand),
calcExpression(rightOperand),
op
);
}
int main()
{
char in[256];
while(1) {
// Read input from user
getInput(in);
if (strncmp(in, "quit", 4) == 0) break;
// Perform calculations
int result = calcExpression(in);
printf("%d\n", result);
}
}
Hi:) what i'm trying to do is write a simple program to expand from shortest entry
for example
a-z or 0-9 or a-b-c or a-z0-9
to longest write
for example
abc...xyz or 0123456789 or abc or abcdefghijklmnouprstwxyz0123456789
1-st examle shortest entry = 1-st example result which should give:)
so far i write something like this and it's work only for letters from a to z:
expand(char s[])
{
int i,n,c;
n=c=0;
int len = strlen(s);
for(i = 1;s[i] > '0' && s[i]<= '9' || s[i] >= 'a' && s[i] <= 'z' || s[i]=='-';i++)
{
/*c = s[i-1];
g = s[i];
n = s[i+1];*/
if( s[0] == '-')
printf("%c",s[0]);
else if(s[i] == '-')
{
if(s[i-1]<s[i+1])
{
while(s[i-1] <= s[i+1])
{
printf("%c", s[i-1]);
s[i-1]++;
}
}
else if(s[i-1] == s[i+1])
printf("%c",s[i]);
else if(s[i+1] != '-')
printf("%c",s[i]);
else if(s[i-1] != '-')
printf("%c",s[i]);
}
else if(s[i] == s[i+1])
{
while(s[i] == s[i+1])
{
printf("%c",s[i]);
s[i]++;
}
}
else if( s[len] == '-')
printf("%c",s[len]);
}
}
but now i'm stuck:(
any ideas what should i check to my program work correctly?
Edit1: #Andrew Kozak (1) abcd (2) 01234
Thanks for advance:)
Here is a C version (in about 38 effective lines) that satisfies the same test as my earlier C++ version.
The full test program including your test cases, mine and some torture test can be seen live on http://ideone.com/sXM7b#info_3915048
Rationale
I'm pretty sure I'm overstating the requirements, but
this should be an excellent example of how to do parsing in a robust fashion
use states in an explicit fashion
validate input (!)
this version doesn't assume a-c-b can't happen
It also doesn't choke or even fail on simple input like 'Hello World' (or (char*) 0)
it shows how you can avoid printf("%c", c) each char without using extraneous functions.
I put in some comments as to explain what happens why, but overall you'll find that the code is much more legible anyways, by
staying away from too many short-named variables
avoiding complicated conditionals with un-transparent indexers
avoiding the whole string length business: We only need max lookahead of 2 characters, and *it=='-' or predicate(*it) will just return false if it is the null character. Shortcut evaluation prevents us from accessing past-the-end input characters
ONE caveat: I haven't implemented a proper check for output buffer overrun (the capacity is hardcoded at 2048 chars). I'll leave it as the proverbial exercise for the reader
Last but not least, the reason I did this:
It will allow me to compare raw performance of the C++ version and this C version, now that they perform equivalent functions. Right now, I fully expect the C version to outperform the C++ by some factor (let's guess: 4x?) but, again, let's just see what suprises the GNU compilers have in store for us. More later Update turns out I wasn't far off: github (code + results)
Pure C Implementation
Without further ado, the implementation, including the testcase:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int alpha_range(char c) { return (c>='a') && (c<='z'); }
int digit_range(char c) { return (c>='0') && (c<='9'); }
char* expand(const char* s)
{
char buf[2048];
const char* in = s;
char* out = buf;
// parser state
int (*predicate)(char) = 0; // either: NULL (free state), alpha_range (in alphabetic range), digit_range (in digit range)
char lower=0,upper=0; // tracks lower and upper bound of character ranges in the range parsing states
// init
*out = 0;
while (*in)
{
if (!predicate)
{
// free parsing state
if (alpha_range(*in) && (in[1] == '-') && alpha_range(in[2]))
{
lower = upper = *in++;
predicate = &alpha_range;
}
else if (digit_range(*in) && (in[1] == '-') && digit_range(in[2]))
{
lower = upper = *in++;
predicate = &digit_range;
}
else *out++ = *in;
} else
{
// in a range
if (*in < lower) lower = *in;
if (*in > upper) upper = *in;
if (in[1] == '-' && predicate(in[2]))
in++; // more coming
else
{
// end of range mode, dump expansion
char c;
for (c=lower; c<=upper; *out++ = c++);
predicate = 0;
}
}
in++;
}
*out = 0; // null-terminate buf
return strdup(buf);
}
void dotest(const char* const input)
{
char* ex = expand(input);
printf("input : '%s'\noutput: '%s'\n\n", input, ex);
if (ex)
free(ex);
}
int main (int argc, char *argv[])
{
dotest("a-z or 0-9 or a-b-c or a-z0-9"); // from the original post
dotest("This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6"); // from my C++ answer
dotest("-x-s a-9 9- a-k-9 9-a-c-7-3"); // assorted torture tests
return 0;
}
Test output:
input : 'a-z or 0-9 or a-b-c or a-z0-9'
output: 'abcdefghijklmnopqrstuvwxyz or 0123456789 or abc or abcdefghijklmnopqrstuvwxyz0123456789'
input : 'This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6'
output: 'This is some efghijklmnopqrstuvwxyz test in 567 steps; this works: abc. This works too: bcdefghijk. Likewise 45678'
input : '-x-s a-9 9- a-k-9 9-a-c-7-3'
output: '-stuvwx a-9 9- abcdefghijk-9 9-abc-34567'
Ok I tested your program out and it seems to be working for nearly every case. It correctly expands a-z and other expansions with only two letters/numbers. It fails when there are more letters and numbers. The fix is easy, just make a new char to keep the last printed character, if the currently printed character matches the last one skip it. The a-z0-9 scenario didn't work because you forgot a s[i] >= '0' instead of s[i] > '0'. the code is:
#include <stdio.h>
#include <string.h>
void expand(char s[])
{
int i,g,n,c,l;
n=c=0;
int len = strlen(s);
for(i = 1;s[i] >= '0' && s[i]<= '9' || s[i] >= 'a' && s[i] <= 'z' || s[i]=='-';i++)
{
c = s[i-1];
g = s[i];
n = s[i+1];
//printf("\nc = %c g = %c n = %c\n", c,g,n);
if(s[0] == '-')
printf("%c",s[0]);
else if(g == '-')
{
if(c<n)
{
if (c != l){
while(c <= n)
{
printf("%c", c);
c++;
}
l = c - 1;
//printf("\nl is %c\n", l);
}
else
{
c++;
while(c <= n)
{
printf("%c", c);
c++;
}
l = c - 1;
//printf("\nl is %c\n", l);
}
}
else if(c == n)
printf("%c",g);
else if(n != '-')
printf("%c",g);
else if(c != '-')
printf("%c",g);
}
else if(g == n)
{
while(g == n)
{
printf("%c",s[i]);
g++;
}
}
else if( s[len] == '-')
printf("%c",s[len]);
}
printf("\n");
}
int main (int argc, char *argv[])
{
expand(argv[1]);
}
Isn't this problem from K&R? I think I saw it there. Anyway I hope I helped.
Based on the fact that the existing function addresses "a-z" and "0-9" sequences just fine, separately, we should explore what happens when they meet. Trace your code (try printing each variable's value at each step -- yes it will be cluttered, so use line breaks), and I believe you will find a logical short-circuit when iterating, for example, from "current token is 'y' and next token is 'z'" to "current token is 'z' and next token is '0'". Explore the if() condition and you will find that it does not cover all possibilities, i.e. you have covered yourself if you are within a<-->z, within 0<-->9, or exactly equal to '-', but you have not considered being at the end of one (a-z or 0-9) with your next character at the start of the next.
Just for fun, I decided to demonstrate to myself that C++ is really just as suited to this kind of thing.
Test-first, please
First, let me define the requirements a little more strictly: I assumed it needs to handle these cases:
int main()
{
const std::string in("This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6");
std::cout << "input : " << in << std::endl;
std::cout << "output: " << expand(in) << std::endl;
}
input : This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6
output: This is some efghijklmnopqrstuvwxyz test in 567 steps; this works: abc. This works too: bcdefghijk. Likewise 45678
C++0x Implementation
Here is an implementation (actually a few variants) in 14 lines (23 including whitespace, comments) of C++0x code1
static std::string expand(const std::string& in)
{
static const regex re(R"([a-z](?:-[a-z])+|[0-9](?:-[0-9])+)");
std::string out;
auto tail = in.begin();
for (auto match : make_iterator_range(sregex_iterator(in.begin(), in.end(), re), sregex_iterator()))
{
out.append(tail, match[0].first);
// char range bounds: the cost of accepting unordered ranges...
char a=127, b=0;
for (auto x=match[0].first; x<match[0].second; x+=2)
{ a = std::min(*x,a); b = std::max(*x,b); }
for (char c=a; c<=b; out.push_back(c++));
tail = match.suffix().first;
}
out.append(tail, in.end());
return out;
}
Of course I'm cheating a little because I'm using regex iterators from Boost. I will do some timings comparing to the C version for performance. I rather expect the C++ version to compete within a 50% margin. But, let's see what kind of surprises the GNU compiler ahs in store for us :)
Here is a complete program that demonstrates the sample input. _It also contains some benchmark timings and a few variations that trade-off
functional flexibility
legibility / performance
#include <set> // only needed for the 'slow variant'
#include <boost/regex.hpp>
#include <boost/range.hpp>
using namespace boost;
using namespace boost::range;
static std::string expand(const std::string& in)
{
// static const regex re(R"([a-z]-[a-z]|[0-9]-[0-9])"); // "a-c-d" --> "abc-d", "a-c-e-g" --> "abc-efg"
static const regex re(R"([a-z](?:-[a-z])+|[0-9](?:-[0-9])+)");
std::string out;
out.reserve(in.size() + 12); // heuristic
auto tail = in.begin();
for (auto match : make_iterator_range(sregex_iterator(in.begin(), in.end(), re), sregex_iterator()))
{
out.append(tail, match[0].first);
// char range bounds: the cost of accepting unordered ranges...
#if !SIMPLE_BUT_SLOWER
// debug 15.149s / release 8.258s (at 1024k iterations)
char a=127, b=0;
for (auto x=match[0].first; x<match[0].second; x+=2)
{ a = std::min(*x,a); b = std::max(*x,b); }
for (char c=a; c<=b; out.push_back(c++));
#else // simpler but slower
// debug 24.962s / release 10.270s (at 1024k iterations)
std::set<char> bounds(match[0].first, match[0].second);
bounds.erase('-');
for (char c=*bounds.begin(); c<=*bounds.rbegin(); out.push_back(c++));
#endif
tail = match.suffix().first;
}
out.append(tail, in.end());
return out;
}
int main()
{
const std::string in("This is some e-z test in 5-7 steps; this works: a-b-c. This works too: b-k-c-e. Likewise 8-4-6");
std::cout << "input : " << in << std::endl;
std::cout << "output: " << expand(in) << std::endl;
}
1 Compiled with g++-4.6 -std=c++0x
This is a Java implementation. It expands the character ranges similar to 0-9, a-z and A-Z. Maybe someone will need it someday and Google will bring them to this page.
package your.package;
public class CharacterRange {
/**
* Expands character ranges similar to 0-9, a-z and A-Z.
*
* #param string a string to be expanded
* #return a string
*/
public static String expand(String string) {
StringBuilder buffer = new StringBuilder();
int i = 1;
while (i <= string.length()) {
final char a = string.charAt(i - 1); // previous char
if ((i < string.length() - 1) && (string.charAt(i) == '-')) {
final char b = string.charAt(i + 1); // next char
char[] expanded = expand(a, b);
if (expanded.length != 0) {
i += 2; // skip
buffer.append(expanded);
} else {
buffer.append(a);
}
} else {
buffer.append(a);
}
i++;
}
return buffer.toString();
}
private static char[] expand(char a, char b) {
char[] expanded = expand(a, b, '0', '9'); // digits (0-9)
if (expanded.length == 0) {
expanded = expand(a, b, 'a', 'z'); // lower case letters (a-z)
}
if (expanded.length == 0) {
expanded = expand(a, b, 'A', 'Z'); // upper case letters (A-Z)
}
return expanded;
}
private static char[] expand(char a, char b, char min, char max) {
if ((a > b) || !(a >= min && a <= max && b >= min && b <= max)) {
return new char[0];
}
char[] buffer = new char[(b - a) + 1];
for (int i = 0; i < buffer.length; i++) {
buffer[i] = (char) (a + i);
}
return buffer;
}
public static void main(String[] args) {
String[] ranges = { //
"0-9", "a-z", "A-Z", "0-9a-f", "a-z2-7", "0-9a-v", //
"0-9a-hj-kmnp-tv-z", "0-9a-z", "1-9A-HJ-NP-Za-km-z", //
"A-Za-z0-9", "A-Za-z0-9+/", "A-Za-z0-9-_" };
for (int i = 0; i < ranges.length; i++) {
String input = ranges[i];
String output = CharacterRange.expand(ranges[i]);
System.out.println("input: " + input);
System.out.println("output: " + output);
System.out.println();
}
}
}
Output:
input: 0-9
output: 0123456789
input: a-z
output: abcdefghijklmnopqrstuvwxyz
input: A-Z
output: ABCDEFGHIJKLMNOPQRSTUVWXYZ
input: 0-9a-f
output: 0123456789abcdef
input: a-z2-7
output: abcdefghijklmnopqrstuvwxyz234567
input: 0-9a-v
output: 0123456789abcdefghijklmnopqrstuv
input: 0-9a-hj-kmnp-tv-z
output: 0123456789abcdefghjkmnpqrstvwxyz
input: 0-9a-z
output: 0123456789abcdefghijklmnopqrstuvwxyz
input: 1-9A-HJ-NP-Za-km-z
output: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
input: A-Za-z0-9
output: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
input: A-Za-z0-9+/
output: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
input: A-Za-z0-9-_
output: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_