(集体内容详见ChinaUnix社区)
现在要在Linux下实现一个计算器程序, 它有如下的要求:
1. 能识别英文数字:比如 three hundred and ninety two, 要能转换为392.
2. 能识别英文的加减乘除: Add, minus, multiply, divide.
3. 能识别符号的加减乘除: +, -, *, /
例子如下:
输入:three hundred and ninety two multiply with 7
结果:2744
输入:12 minus 3
结果: 9
/* eng_cac.c */
/* english caculator */
#include
#include
#include
int skip_space(const char *p, int *io_index)
{
int index;
if (p == NULL || io_index == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
if (!isspace(p[index])) {
return -1;
}
do {
++index;
}while (isspace(p[index]));
*io_index = index;
return 0;
}
int get_word(const char *p, int *io_index, int *o_start, int *o_len)
{
int index;
if (p == NULL || io_index == NULL || o_start == NULL || o_len == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
if (isalpha(p[index])) {
do {
++index;
}while (isalpha(p[index]));
*o_start = *io_index;
*o_len = index - *io_index;
*io_index = index;
return 0;
}else if (ispunct(p[index])) {
++index;
*o_start = *io_index;
*o_len = 1;
*io_index = index;
return 0;
}else {
return -1;
}
}
int get_token(const char *tok, int len, const char *tok_tab[], int tab_len)
{
int i;
if (tok == NULL || tok_tab == NULL) {
return -1;
}
if (len <= 0 || tab_len <= 0) {
return -1;
}
for (i = 0; i < tab_len; ++i) {
if (strncmp(tok, tok_tab[i], len) == 0) {
if (tok_tab[i][len] == '\0') {
return i;
}
}
}
return -1;
}
int skip_word(const char *p, int *io_index, const char *s)
{
int index;
int start, len;
if (p == NULL || io_index == NULL || s == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (get_word(p, &index, &start, &len) == 0) {
if (strncmp(p + start, s, len) == 0) {
*io_index = index;
return 0;
}
}
return -1;
}
int get_english_number_bellow_twenty(const char *p, int *io_index, int *o_value)
{
static const char *digit20[20] = {
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen",
};
int index;
int start;
int len;
int tok;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (get_word(p, &index, &start, &len) == 0) {
tok = get_token(p + start, len, digit20, 20);
if (tok != -1) {
*o_value = tok;
*io_index = index;
return 0;
}
}
return -1;
}
int get_english_number_decade(const char *p, int *io_index, int *o_value)
{
static const char *decades[8] = {
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety",
};
int index;
int start, len;
int value;
int retn;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (get_word(p, &index, &start, &len) == 0) {
retn = get_token(p + start, len, decades, 8);
if (retn != -1) {
value = retn * 10 + 20;
*o_value = value;
*io_index = index;
return 0;
}
}
return -1;
}
int get_english_number_bellow_hundred(const char *p, int *io_index, int *o_value)
{
int index;
int value;
int digit;
int decade_yes;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
decade_yes = 0;
if (get_english_number_decade(p, &index, &value) == -1) {
value = 0;
}else {
decade_yes = 1;
}
if (get_english_number_bellow_twenty(p, &index, &digit) == 0) {
if (decade_yes && digit == 0) {
return -1;
}
value += digit;
}else {
if (!decade_yes) {
return -1;
}
}
*o_value = value;
*io_index = index;
return 0;
}
int get_english_number_base(const char *p, int *io_index, int *o_base)
{
static const char *more[] = {
"thousand",
"million",
"billion",
};
static const int base[] = {
1000,
1000000,
1000000000,
};
int index;
int start,len;
int retn;
if (p == NULL || io_index == NULL || o_base == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (get_word(p, &index, &start, &len) == 0) {
retn = get_token(p + start, len, more, 4);
if (retn != -1) {
*o_base = base[retn];
*io_index = index;
return 0;
}
}
return -1;
}
int get_english_number_bellow_thousand(const char *p, int *io_index, int *o_value)
{
int index;
int value;
int sum;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
if (get_english_number_bellow_hundred(p, &index, &sum) == 0) {
if (sum > 0 && sum < 10) {
if (skip_word(p, &index, "hundred") == 0) {
sum *= 100;
if (skip_word(p, &index, "and") == 0) {
if (get_english_number_bellow_hundred(p, &index, &value) == -1) {
return -1;
}
if (value == 0) {
return -1;
}
sum += value;
}
}
}
*o_value = sum;
*io_index = index;
return 0;
}else if (skip_word(p, &index, "a") == 0) {
sum = 1;
if (skip_word(p, &index, "hundred") == 0) {
sum *= 100;
if (skip_word(p, &index, "and") == 0) {
if (get_english_number_bellow_hundred(p, &index, &value) == -1) {
return -1;
}
if (value == 0) {
return -1;
}
sum += + value;
}
}
*o_value = sum;
*io_index = index;
return 0;
}
return -1;
}
int get_english_number(const char *p, int *io_index, int *o_value)
{
int index;
int value;
int sum;
int base;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
sum = 0;
for (;;) {
if (get_english_number_bellow_thousand(p, &index, &value) == 0) {
if (value == 0) {
break;
}
if (get_english_number_base(p, &index, &base) == 0) {
value *= base;
if (sum > 0 && value >= sum) {
return -1;
}
sum += value;
continue;
}
sum += value;
}
if (sum == 0) {
return -1;
}
break;
}
*o_value = sum;
*io_index = index;
return 0;
}
int get_operator1(const char *p, int *io_index, int *o_op)
{
static const char *op_list[] = {
"positive",
"+",
"negative",
"-",
};
int index;
int start, len;
int retn;
if (p == NULL || io_index == NULL || o_op == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (get_word(p, &index, &start, &len) == 0) {
retn = get_token(p + start, len, op_list, 8);
if (retn != -1) {
*o_op = retn / 2;
*io_index = index;
return 0;
}
}
return -1;
}
int get_operator2(const char *p, int *io_index, int *o_op)
{
static const char *op_list[] = {
"plus",
"+",
"minus",
"-",
"multiply",
"*",
"divide",
"/",
};
int index;
int start, len;
int retn;
if (p == NULL || io_index == NULL || o_op == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (get_word(p, &index, &start, &len) == 0) {
retn = get_token(p + start, len, op_list, 8);
if (retn != -1) {
if ((retn & 1) == 0) {
skip_word(p, &index, "with");
}
*o_op = retn / 2;
*io_index = index;
return 0;
}
}
return -1;
}
int get_arab_number(const char *p, int *io_index, int *o_value)
{
int index;
int value;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (isdigit(p[index])) {
value = 0;
do {
value = value * 10 + p[index] - '0';
index++;
}while (isdigit(p[index]));
*o_value = value;
*io_index = index;
return 0;
}
return -1;
}
int get_number(const char *p, int *io_index, int *o_value)
{
int index;
int value;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
if (get_english_number(p, &index, &value) == 0
|| get_arab_number(p, &index, &value) == 0) {
*o_value = value;
*io_index = index;
return 0;
}
return -1;
}
int get_oprand(const char *p, int *io_index, int *o_value)
{
int index;
int op;
int d1;
int result;
if (p == NULL || io_index == NULL || o_value == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
/* one oprand operation */
if (get_operator1(p, &index, &op) == 0) {
if (get_number(p, &index, &d1) != 0) {
return -1;
}
switch (op) {
case 0:
result = d1;
break;
case 1:
result = -d1;
break;
default:
return -1;
}
}else {
if (get_number(p, &index, &result) != 0) {
return -1;
}
}
*o_value = result;
*io_index = index;
return 0;
}
int get_end(const char *p, int *io_index)
{
int index;
if (p == NULL || io_index == NULL) {
return -1;
}
index = *io_index;
if (index < 0) {
return -1;
}
skip_space(p, &index);
if (p[index] == '\0') {
*io_index = index;
return 0;
}
return -1;
}
int caculate(const char *exp, int *o_value)
{
int op; /* operator */
int d1, d2; /* oprand number */
int result; /* store the expression result */
int index;
if (exp == NULL || o_value == NULL) {
return -1;
}
index = 0;
if (get_oprand(exp, &index, &result) == -1) {
return -1;
}
/* conjunction operation */
while (get_end(exp, &index) == -1) {
/* two oprands operation */
d1 = result;
if (get_operator2(exp, &index, &op) == -1) {
return -1;
}
if (get_oprand(exp, &index, &d2) == -1) {
return -1;
}
switch (op) {
case 0:
result = d1 + d2;
break;
case 1:
result = d1 - d2;
break;
case 2:
result = d1 * d2;
break;
case 3:
if (d2 == 0) {
return -1;
}
result = d1 / d2;
break;
default:
return -1;
}
}
*o_value = result;
return 0; /* end of conjunction */
}
int read_line(char *buff, int buf_len)
{
int len;
if (buff == NULL || buf_len <= 0) {
return -1;
}
for (;;) {
if (fgets(buff, buf_len, stdin) == NULL) {
return -1;
}
len = strlen(buff) - 1;
if (buff[len] == '\n') {
buff[len] = '\0';
return 0;
}
/* check for last not terminated line in file */
if (fgets(buff, buf_len, stdin) == NULL) {
return 0;
}
/* line length is greater than buffer */
/* skip rest line content */
for (;;) {
len = strlen(buff) - 1;
if (buff[len] == '\n') {
return -2; /* buffer overflow, skip the whole line */
}
if (fgets(buff, buf_len, stdin) == NULL) {
return -1;
}
}
}
}
int main(void)
{
char cmd_line[1024];
int result;
puts("English Caculator");
puts("Enter an expression");
puts("Ctrl-Z to exit\n");
for (;;) {
printf(": ");
switch (read_line(cmd_line, sizeof(cmd_line))) {
case 0:
break;
default:
case -1:
puts("");
return 0;
case -2:
puts("Buffer Overflow");
continue;
}
if (caculate(cmd_line, &result) == 0) {
printf("'%s' = %d\n", cmd_line, result);
}else {
printf("'%s' = E, Invalid Expression\n", cmd_line);
}
}
}