中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --
联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum
游客 | 登录 | 注册 | 会员 | 搜索 | 中国DOS联盟
中国DOS联盟论坛
现在时间是 2026-09-14 13:37
47,812 主题排行 / 349,917 发帖 / 今日 0 篇 / 48,268 会员排行
DOS批处理 & 脚本技术(批处理室) » 一个可以计算简单表达式的批处理
可打印版本  13,106 / 22
第1楼 brglng 发表于 2005-08-15 15:53
银牌会员 发帖 466 积分 1,200 来自 上海
一个可以计算简单表达式的批处理
我编了一个有趣的批处理,用于2K/XP等NT类的系统,可以计算简单的表达式。只能算作入门级的吧,所以就贴在这里了。
@echo off
echo 欢迎使用2K/XP表达式计算批处理
echo Made by Brglng 2005.8.15
echo 下载地址:http://brglng.ys168.com
echo 有bug请及时汇报!Email:sky-0310@sohu.com
echo.
echo 注意:本批处理只能用于NT类的系统,不能用于Win9x/Me/DOS!
echo 如果在命令行下使用,请用附件里的“命令提示符”或者使用
echo cmd.exe,而不要使用command.com!
echo This batch file can only be used in Chinese version of
echo Windows 2K/XP/2003...!
echo Do not use command.com instead of cmd.exe!
pause

:inputExpr
set Expr=
set EvalExpr=
echo.
echo 请输入表达式(输入"?"查看帮助):
set /p Expr=
if "%Expr%"=="" (
goto inputExpr
)
if "%Expr%"=="?" (
echo 本表达式计算批处理很简单并以递减的优先权顺序支持下列操作:
echo ^(^) ^- 分组
echo ^! ^~ ^- ^- 一元运算符
echo ^* ^/ ^% ^- 算数运算符
echo ^+ ^- ^- 算数运算符
echo ^<^< ^>^> ^- 逻辑移位
echo ^- 按位“与”
echo ^^ ^- 按位“异”
echo ^| ^- 按位“或”
echo ^= ^*^= ^/^= ^%^= ^+^= ^-^= ^- 赋值
echo ^&^= ^^^= ^|^= ^<^<^= ^>^>^=
echo ^, ^- 表达式分隔符
echo 如果您使用任何逻辑或取余操作符, 您需要将表达式字符串用
echo 引号扩起来。在表达式中的任何非数字字符串键作为环境变量
echo 名称,这些环境变量名称的值已在使用前转换成数字。如果指定
echo 了一个环境变量名称,但未在当前环境中定义,那么值将被定为
echo 零。这使您可以使用环境变量值做计算而不用键入那些 % 符号
echo 来得到它们的值。除十六进制有 0x 前缀,八进制有 0 前缀的,
echo 数字值为十进位数字。因此, 0x12 与 18 和 022 相同。请注意
echo 八进制公式可能很容易搞混: 08 和 09 是无效的数字,因为 8
echo 和 9 不是有效的八进制位数。
goto inputExpr
)
set /a EvalExpr="%Expr%"
if "%EvalExpr%"=="" (
echo 输入错误!
goto inputExpr
)
echo 计算结果为:%EvalExpr%

:if_save
set SaveEval=
echo.
echo 是否将表达式和计算结果存入文件?
set /p SaveEval=
if "%SaveEval%"=="Y" goto save
if "%SaveEval%"=="y" goto save
if "%SaveEval%"=="N" goto next
if "%SaveEval%"=="n" goto next
goto if_save

:save
echo 请输入文件路径及文件名(如C:\Windows\file.txt。若不输入路径则表示当前文件夹,留空则为上一次输入的路径):
set /p SaveEvalPath=
echo %Expr%=%EvalExpr% >>%SaveEvalPath%
echo 表达式以及计算结果已存入%SaveEvalPath%!

:next
set EvalNext=
echo 1.继续计算 2.退出
set /p EvalNext=
if "%EvalNext%"=="1" goto inputExpr
if "%EvalNext%"=="2" goto end
goto next

:end
echo 谢谢使用!按任意键退出……
pause >nul
set Expr=
set EvalExpr=
set SaveEval=
set SaveEvalPath=
set EvalNext=


这个批处理的最新版可以在我的E盘的“DIY”目录里下载到。

[ Last edited by namejm on 2007-2-3 at 09:49 PM ]
第2楼 venchia 发表于 2005-08-15 18:02
初级用户 发帖 11 积分 125
不错,收藏
第3楼 merlin 发表于 2005-08-15 19:00
初级用户 发帖 18 积分 143
怎么不写一个Win9x/me/DOS的啊!
第4楼 Michael 发表于 2005-08-15 19:44
钻石会员 发帖 3,041 积分 10,056
也许只有2k/xp才有现成的表达式求值语句吧。
第5楼 brglng 发表于 2005-08-16 09:41
银牌会员 发帖 466 积分 1,200 来自 上海
Originally posted by Michael at 2005-8-15 07:44 PM:
也许只有2k/xp才有现成的表达式求值语句吧。

嗯,没错
其实,要真正实现更好的表达式计算,只要用那些命令行计算器就可以了。它们的使用都很方便。NT的set命令的/a参数也有很多限制的,功能并不强,比如不支持浮点数,不支持复杂的数学函数(如sin、cos)等。所以,这个批处理实际上没什么实用价值,只能作为学习学习吧。我自认为这个批处理对2K/XP新的命令开关和语法的综合运用还是不错的。

[ Last edited by brglng on 2005-8-16 at 10:18 ]
第6楼 Michael 发表于 2005-08-16 21:36
钻石会员 发帖 3,041 积分 10,056
开始还真吓了我一跳,我还以为表达式计算也是批处理完成的呢。

C++编一个类似程序还得需要这么多行呢:

#include<iostream>
#include<string>
#include<map>
#include<cctype>
#include<sstream>

using namespace std;

namespace Parser
{
double prim(bool);
double term(bool);
double expr(bool);
}

namespace Lexer
{
enum Token_value
{
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};

Token_value curr_tok;
double number_value;
string string_value;
map<string,double>table;

Token_value get_token();
}

namespace Driver
{
int no_of_errors;
istream* input;
void skip();
void sta();
}

namespace Error
{
struct Syntax_error
{
const char* p;
Syntax_error(const char* q) {p=q;}
};
struct Zero_divide{};
}


using namespace Driver;
using namespace Parser;
using namespace Lexer;
using namespace Error;

double Parser::expr(bool get)
{
double left=term(get);
for(;;)
switch(curr_tok)
{
case PLUS:
left+=term(true);
break;
case MINUS:
left-=term(true);
break;
default:
return left;
}
}

double Parser::term(bool get)
{
double left=prim(get);
for(;;)
switch(curr_tok)
{
case MUL:
left*=prim(true);
break;
case DIV:
if(double d=prim(true))
{
left/=d;
break;
}
throw Zero_divide();
default:
return left;
}
}



double Parser::prim(bool get)
{
if (get) get_token();
switch(curr_tok)
{
case NUMBER:
{
double v=number_value;
get_token();
return v;
}
case NAME:
{
double& v=table;
if(get_token()==ASSIGN)v=expr(true);
return v;
}
case MINUS:
return -prim(true);
case LP:
{
double e=expr(true);
if(curr_tok!=RP) throw Syntax_error("')'expected");
get_token();
return e;
}

default:
throw Syntax_error("primary expected");
}

}
Token_value Lexer::get_token()
{
char ch;
do
{
if(!input->get(ch))return curr_tok=END;
}while(ch!='\n'&&isspace(ch));
switch(ch)
{
case '\n':
return curr_tok=PRINT;
case ';':
case '*':
case '/':
case '+':
case '-':
case '(':
case ')':
case '=':
return curr_tok=Token_value(ch);
case '0':case '1':case '2':case '3':case '4':
case '5':case '6':case '7':case '8':case '9':
input->putback(ch);
*input>>number_value;
return curr_tok=NUMBER;
default:
if(isalpha(ch))
{
string_value=ch;
while(input->get(ch)&&isalnum(ch))string_value.push_back(ch);
input->putback(ch);
return curr_tok=NAME;
}
throw Syntax_error("bad token");
}
}
void Driver::sta()
{
while(*input){
try
{
get_token();
if(curr_tok==END)break;
if(curr_tok==PRINT)continue;
cout<<expr(false)<<endl;
}
catch(Zero_divide)
{
cerr<<"attempt to divide by zero\n";
if(curr_tok!=PRINT)skip();
}
catch(Syntax_error e)
{
cerr<<"syntax error:"<<e.p<<endl;
if(curr_tok!=PRINT)skip();
}
}
}



void Driver::skip()
{
no_of_errors++;

while(*input)
{
char ch;
input->get(ch);

switch(ch)
{
case '\n':
case ';':
return;
}
}
}

int main(int argc, char*argv)
{
switch(argc)
{
case 1:
input=&cin;
break;
case 2:
input=new istringstream(argv);
break;
default:
cerr<<"too many arguments\n";
return 1;
}
sta();
if(input!=&cin)delete input;
return no_of_errors;
}

[ Last edited by Michael on 2005-8-16 at 21:37 ]
第7楼 brglng 发表于 2005-08-17 15:46
银牌会员 发帖 466 积分 1,200 来自 上海
呵呵,我用VB编过一个。
第8楼 willsort 发表于 2005-08-19 20:31
元老会员 发帖 1,512 积分 4,432
Re brglng:

很好的代码,提三个建议:

1、加入命令行参数支持,即在允许在命令行参数中写入表达式或者/?;

2、set /p 的提示字符串可以写在 set 语句中,比如
set /p SaveEval=是否将表达式和计算结果存入文件?

3、if 语句中可以使用开关 /i 进行忽略大小写判断,比如:
if /i "%SaveEval%"=="Y" goto save
if /i "%SaveEval%"=="N" goto next

另外,待代码完善后,我有意将它收入我的批处理收集帖 {8905}《批处理编程的异类》,但是因为版面限制,可能会删除大部分的说明性文档代码,不知 brglng 兄是否同意?
第9楼 agboy888 发表于 2005-08-20 01:20
新手上路 发帖 1 积分 1
不错,谢谢
第10楼 brglng 发表于 2005-08-23 14:56
银牌会员 发帖 466 积分 1,200 来自 上海
Originally posted by willsort at 2005-8-19 08:31 PM:
Re brglng:

很好的代码,提三个建议:

1、加入命令行参数支持,即在允许在命令行参数中写入表达式或者/?;

2、set /p 的提示字笠...


谢谢Willsort高手的建议。关于收录,我没什么意见,反正本来就是随便写写的。
第11楼 brglng 发表于 2005-08-25 11:39
银牌会员 发帖 466 积分 1,200 来自 上海
以下是根据willsort建议改进后的代码(增加了很多功能):
第12楼 defrag 发表于 2005-08-26 00:54
中级用户 发帖 570 积分 456
用GW-BASIC编计算器并不是很难。。。
这个没测试过
没有错误处理
main.bas:

用QB7.1也可以(要用QBX运行,不能编译)
有错误处理
也没测试
main.bas:


[ Last edited by defrag on 2005-8-30 at 19:41 ]
第13楼 willsort 发表于 2005-08-26 11:28
元老会员 发帖 1,512 积分 4,432
Re Brglng:

在结构上作了一些变化,让程序执行流程更加清晰,核心代码并为做出更改,请 Brglng 兄做一下测试吧。关于表达式用法帮助,我起初使用了 for 的语句以节省代码,但后来考虑到可读性的问题,最后舍弃了,你也可以一起测试一下。



[ Last edited by willsort on 2005-9-11 at 13:19 ]
第14楼 pfox 发表于 2005-08-26 15:01
银牌会员 发帖 446 积分 1,451
几位用其他语言编写的朋友,能不能随便编译成exe上传?
第15楼 defrag 发表于 2005-08-27 16:43
中级用户 发帖 570 积分 456
说过,我那个只能在IDE(急成开发环境)里运行。。。
1 2  下一页
[ 联系联盟系统管理团队 - 中国DOS联盟 - 标准版 ]
Sponsored by ifanr Inc | © 2001–2023