第一次个人编程作业-中文编程

这个作业属于哪个课程 软件工程
这个作业要求在哪里 第一次个人编程作业
这个作业的目标 实现功能、熟练使用git和GitHub
作业正文 如下
其他参考文献 如下

GitHub仓库地址:learn-programming

看到题目零碎的想法

零碎的想法对问题的解决并没有什么帮助,所以开始搜相关案例

搜索

搜索关键字“中文编程”,意外看到了一个Github项目

中文编程的历史, 现状和展望

Java采用Unicode字符集,所以变量、方法名、类名等都可以定义为中文,所以有一种实现“中文编程”的方式就是把类名、变量名都定义为中文,但是这种方式还是不能脱离Java的关键字,所以这种方方式不符合这次的要求

开心一下-实现基于Java一个中文编程语言

没有找到相关案例,最后就从语义的角度开始分析:可以通过截取字符串来获取关键词、变量和常量,再进行逻辑操作

分析

需求分析

  • 整数变量定义、赋值
  • 加减运算
  • 输出变量或字符串
  • 三目运算:如果 则 否则

需要解决的问题

  • 字符串截取
  • 字符串语义判断
  • 异常处理
  • 负数,多位数

遇到的一些坑

  • 字符串split的时候左右边的空格会被加入到split返回的数组

功能实现情况

  • 数字输入输出(支持中文习惯和机械式的转化)

    • 符合中国人习惯的输入输出,支持-999到999(例如:九百九十九、九百一、九百一十,九百零一,九百,九十,十九,十,零,负九百九十九)
    • 机械式的转化(例如:一零零)支持的范围:-2147483647到2147483647
    • 传奇地加入了负零(效用等同于零)
  • 支持去除语句中多余的空格

  • 变量定义:整数 钱包 等于 零

    • 支持定义多个变量
  • 运算操作:钱包 增加 十

    • 支持增加、减少、乘以、除以、模除
  • 输出:看看 钱包 、 看看 “字符串”

    • 支持输出字符串或者变量
  • 三目运算符:如果 钱包 大于 十 则 看看 “钱太多了” 否则 看看 “我穷死了”

    • 条件判断两边都可以是整数变量或者整数常量
  • 异常处理

    • 关键字不存在
    • 运算时变量未定义
    • 输出时变量未定义
    • 除数不能为0
    • 非法输入

完整代码

主函数

public class DemoMain {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while (true) {
			Utils.runMain(sc.nextLine());
		}
	}

}

Utils类


public class Utils {
	private static String numWords = "零一二三四五六七八九十";
	private static Map<String, Integer> map = new HashMap<String, Integer>();

	/**
	 * 运行语句并处理异常
	 * @param str
	 */
	public static void runMain(String str) {
		try {
			Utils.callFunction(str);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	/**
	 * 调用方法
	 * @param str
	 */
	public static void callFunction(String str) {
		String[] split = str.trim().split("\\s+");
		String keyword = split[0];
		if (keyword.equals("无")) {
			return;
		}
		if (isManipulate(str) || isVar(keyword)) {
			Utils.manipulateNum(str);
		} else {
			// isManipulate 已经判断了len == 1情况 不会越界
			switch (keyword) {
			case "整数":
				// System.out.println(map.get("钱包"));// 不存在返回null
				Utils.assignInt(str);
				break;
			case "看看":
				Utils.printOut(str);
				break;
			case "如果":
				Utils.ternaryOperator(str);
				break;
			default:
				throw new IllegalArgumentException("没有关键字:" + keyword + " 请使用关键字:整数、看看、如果");
			}
		}
	}

	/**
	 *  赋值整数变量
	 * @param array
	 * @return
	 */
	public static void assignInt(String str) {
		String[] strArr = str.trim().split("\\s+");
		// 短路 所以不会越界
		if (strArr.length != 4 || !strArr[2].equals("等于")) {
			throw new DemoException("语法有错,请检查语法");
		}

		setVar(strArr[1], toNum(strArr[3]));
	}

	/**
	 * 输出字符串或者变量
	 * @param array
	 */
	public static void printOut(String str) {
		String[] strArr = str.trim().split("\\s+");

		// 输出变量
		if (strArr.length == 2) {
			String varStr = strArr[1];
			if (isVar(varStr)) {
				System.out.println(toChStr(getVar(varStr)));
				return;
			} else if (!varStr.contains("“") && !varStr.contains("”")) {
				throw new DemoException("变量:" + varStr + " 未定义,请定义变量");
			}
			
		}
		// 去除字符串中的“看看”
		String newStr = str.trim().substring(2).trim();		
	
		if (newStr.indexOf("“") == 0 && newStr.lastIndexOf("”") == (newStr.length() - 1)) {
			System.out.println(newStr.substring(1, ( newStr.length()-1)  ));
		} else {
			throw new DemoException("语法有错,请检查语法");
		}

//		if (strArr.length != 2) {
//			throw new DemoException("语法有错,请检查语法");
//		}
//		String printStr = strArr[1];
//		if (printStr.contains("“") && printStr.contains("”")) {
//			System.out.println(printStr.replace("“", "").replace("”", "")); // 看看 “字符串”
//		} else {
//			System.out.println(toChStr(getVar(printStr)));
//		}
	}


	/**
	 * 三目运算
	 * @param str
	 */
	public static void ternaryOperator(String str) {
		// 如果 钱包 大于 十 则 看看 “钱太多了” 否则 看看 “我穷死了”
		// 先不考虑三目嵌套三目的情况
		String statement1 = null;
		String statement2 = null;
		String statement3 = null;
		try {
			statement1 = str.substring(0, str.indexOf("则")).replace("如果", "");
			statement2 = str.substring(str.indexOf("则"), str.indexOf("否则")).replace("则", "");
			statement3 = str.substring(str.indexOf("否则")).replace("否则", "");
		} catch (Exception e) {
			throw new DemoException("语法有错,请检查语法");
		}

		boolean judge = judgeOperator(statement1);
		// System.out.println(judge);

		if (judge) {
			callFunction(statement2);
		} else {
			callFunction(statement3);
		}
	}

	/**
	 * 判断语句,传入如果语句 例如:零 等于 零
	 * @param str
	 * @return 
	 */
	public static boolean judgeOperator(String str) {
		String[] strArr = str.trim().split("\\s+");// 不去除左右空格,空格会被加入到分割数组
		if (strArr.length != 3) {
			throw new DemoException("语法有错,请检查语法");
		}
		String leftStr = strArr[0];
		String rightStr = strArr[2];
		String middle = strArr[1];
		int leftInt = 0;
		int rightInt = 0;

		// 如果都不是变量,那么toNum来异常处理
		if (isVar(leftStr)) {
			leftInt = getVar(leftStr);
		} else {
			try {
				leftInt = toNum(leftStr);
			} catch (Exception e) {
				throw new DemoException("变量:" + leftStr + " 未定义,请定义变量");
			}
		}

		if (isVar(rightStr)) {
			rightInt = getVar(rightStr);
		} else {
			try {
				rightInt = toNum(rightStr);
			} catch (Exception e) {
				throw new DemoException("变量:" + rightStr + " 未定义,请定义变量");
			}

		}

		switch (middle) {
		case "等于":
			return leftInt == rightInt;
		case "大于":
			return leftInt > rightInt;
		case "小于":
			return leftInt < rightInt;

		default:
			throw new IllegalArgumentException("没有关键字:" + middle + " 请使用关键字:大于、等于、小于");
		}
	}

	/**
	 * 汉字单个转化为单个数字
	 * @param str
	 * @return
	 */

	public static int toSingleNum(String str) {
		int num = numWords.indexOf(str);
		if (num == -1) {
			throw new DemoException("语法有错,请检查语法,字符转化错误");
		}
		return num;// -1不存在
	}

	/**
	 * 单个数字转化为单个汉字
	 * @param num
	 * @return
	 */
	public static String toSingleChStr(int num) {
		if (num < 0 || num > 10) {
			throw new DemoException("语法有错,请检查语法,字符转化错误");
		}
		return numWords.substring(num, num + 1);
	}

	/**
	 * 汉字转化为数字
	 * @return
	 */
	public static int toNum(String str) {
		int flag = 1;// 负数标记
		int var = 0;
		char[] arr = str.toCharArray();
		if (arr[0] == '负') {
			str = str.replace("负", "");

			flag = -1;
		}
		if (str.length() == 1 &&str.equals("百")) {
			return flag*100;
		}
		// 数字机械式的转化
		if (str.length() > 1 && !str.contains("百") && !str.contains("十")) {
			char[] chArr = str.toCharArray();
			int temp = 1;
			for (int i = chArr.length - 1; i >= 0; i--) {
				String s = "" + chArr[i];
				var += temp * toSingleNum(s);
				temp *= 10;
			}
			return var * flag;
		}

		if (str.contains("百")) {
			var = 100 * toSingleNum(str.substring(0, str.indexOf('百')));// 百位
			str = str.substring(str.indexOf('百') + 1);
			if (str.contains("零")) {
				var += toSingleNum(str.substring(str.indexOf("零") + 1));// 几百零几
				return var * flag;
			}
		}

		if (str.length() == 1 && var >= 100) { // 判断var 不然十 可能会输出100
			var += 10 * toSingleNum(str); // 几百几:九百九
			return var * flag;
		}

		if (str.contains("十")) {
			if (str.indexOf('十') == 0) {
				var += 10; // 十几
			}
			var += 10 * toSingleNum(str.substring(0, str.indexOf('十')));// 十
			str = str.substring(str.indexOf('十') + 1);
		}

		if (str.length() != 0) {
			var += toSingleNum(str);
		}

		return var * flag;
	}

	@Test
	public void test2() {
		// Java int Max :2147483647 二一四七四八三六四七 Min:-2147483648
		// System.out.println(toNum("负二一四七四八三六四八"));
		System.out.println(toNum("负九九九"));
	}

	/**
	 * 数字转化为汉字
	 * @param num
	 * @return
	 */
	public static String toChStr(int num) {

		if (num == -2147483648) {
			throw new DemoException("超出支持的输入输出范围(-2147483647到2147483647)");
		}
		String varStr = "";

		if (num < 0) {
			varStr += "负";
			num = num * -1; // 转化为正数
		}

		// 数字机械式的转化
		if (num > 999) {
			// 1234
			char[] CharArr = Integer.toString(num).toCharArray();
			for (char c : CharArr) {
				String s = "" + c;
				varStr += toSingleChStr(Integer.parseInt(s));
			}
			return varStr;
		}
		if (num == 0) { // 零
			return "零";
		}

		if (num / 10 == 1) { // 用十几 改变一十几的写法
			varStr += "十";
			if (num % 10 != 0) {
				varStr += toSingleChStr(num % 10);
			}
			return varStr;
		}

		if (num / 100 > 0) {
			varStr += toSingleChStr(num / 100) + "百";
			if (num % 100 == 0) {// 几百
				return varStr;
			} else if (num % 100 < 10) { // 几百零几
				return varStr += "零" + toSingleChStr(num % 100);
			}
		}

		num %= 100;
		if (num / 10 != 0) {
			varStr += toSingleChStr(num / 10) + "十";// 十
		}

		if (num % 10 != 0) {
			varStr += toSingleChStr(num % 10); // 几
		}

		return varStr;
	}

	/**
	 * 运算操作
	 * @param str
	 */
	public static void manipulateNum(String str) {
		String[] strArray = str.trim().split("\\s+");
		if (strArray.length != 3) {
			throw new DemoException("语法有错,请检查语法");
		}

		String operator = strArray[1];
		String varStr = strArray[0];
		String numStr = strArray[2];

		int var = getVar(varStr);
		int num = toNum(numStr);

		switch (operator) {
		case "减少":
			var -= num;
			break;
		case "增加":
			var += num;
			break;
		case "乘以":
			var *= num;
			break;
		case "除以":
			if (num == 0) {
				throw new DemoException("除数不能等于零哦");
			} else {
				var /= num;
			}
			break;
		case "模除":
			var %= num;
			break;
		default:
			throw new IllegalArgumentException("没有关键字:" + operator + " 请使用关键字:增加、减少、乘以、除以、模除 ");
		}

		setVar(varStr, var);
	}

	/**
	 * 判断是否为运算操作语句
	 * @param str
	 * @return
	 */
	public static boolean isManipulate(String str) {
		String[] array = str.trim().split("\\s+");

		String[] keywords = { "增加", "减少", "乘以", "除以", "模除" };

		// 下面会用到array[1]
		if (array.length == 1) {
			throw new DemoException("语法有错,请检查语法");
		}
		String symbol = array[1];

		for (String s : keywords) {
			if (symbol.equals(s)) {
				return true;
			}
		}
		return false;
	}

	@Test
	public void testSymbol() {
		System.out.println(isManipulate("钱包 模除  十"));
	}

	/**
	 * 从Map中获取变量,不存在抛出异常
	 * @param str 需要获取数据的变量名
	 * @return
	 */
	private static int getVar(String varStr) {
		if (isVar(varStr)) {
			return map.get(varStr);
		} else {
			throw new DemoException("变量:" + varStr + " 未定义,请定义变量");
		}
	}

	/**
	 * 存入变量到map中
	 * @param varStr 键
	 * @param var 值
	 */
	private static void setVar(String varStr, int var) {
		map.put(varStr, var);
	}

	/**
	 * 判断是否为已存在的变量
	 * @param varStr 键
	 * @return
	 */
	private static boolean isVar(String varStr) {
		return map.get(varStr) != null ? true : false;
	}
}



Exception类

public class DemoException extends RuntimeException {

package Demo1;


public class DemoException extends RuntimeException {

	public DemoException() {
		super();
	}

	public DemoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public DemoException(String message, Throwable cause) {
		super(message, cause);
	}

	public DemoException(String message) {
		super(message);
	}

	public DemoException(Throwable cause) {
		super(cause);
	}

}


}

数据测试

测试数据1
test1
测试数据2
test2
测试数据3
test3
测试数据4
test4

迭代记录

第一次commit

基本功能:一位数的收入输出、整数赋值、变量增减、三目运算

第二次commit

添加负数和多位数(符合习惯的多位数,例如一百九十九)

第三次commit

优化运算模块、输出模块、赋值模块的实现,添加乘以、除以、模除

第四次commit

实现数字机械式的转化(100 <-> 一零零)

第五次commit

优化callFunction,增加isManipulate,增加异常处理,优化细节问题

第六次commit

添加removeSpaces处理语句多余的空格 Eclipse默认GBK编码更换为UTF-8

第七次commit

添加了getVar,删除了removeSpaces,用正则改进了split,优化了代码细节,代码又清爽了不少,修复了非法输入导致的越界问题
修改了测试程序和测试样本

第八次commit

添加setVar和isVar,封装了变量的赋值,优化了代码结构

第九次commit

添加了runMain,添加了一个关于三目运算的Exception

第十次commit

修复了printOut 百的bug和pirntOut 字符串中包含空格的bug

编程记录

第一次提交的代码行数(包括文档注释和单元测试):260+
查资料+需求分析时间:2小时左右
第一次完成编码时间:4小时左右

思路

代码有三个类

  • DemoMain 存放主函数(处理传入的字符串,分割成行)
  • Utils 字符处理的工具类
  • DemoException (Demo的异常处理)

Utils类

  • callFunction 传入一行字符串,判断逻辑关键字并调用对应的方法
  • assignInt 整数赋值
  • printOut 输出字符串或者整数
  • ternaryOperator 三目操作
  • judgeOperator 判断语句(举例:零 等于 零)
  • toNum 把中文数字转化为数字
  • toChStr 把数字转化为中文数字
  • manipulateNum 增减运算
  • toSingleNum 单个数字转化为单个汉字
  • toSingleChStr 单个汉字转化为单个数字
  • isManipulate 判断是否为运算操作语句
  • getVar从Map中获取变量,不存在抛出异常

主要是根据功能的不同来分类,对于一些经被其他方法调用到的方法就单独抽取出来如:toNum()和toChStr()和judgeOperator()

把toNum()和toChStr()划分出来有利于代码的迭代更新,目前仅实现了一位数字的运算,通过修改这两个方法就可以实现负数和多位数字的运算

public static String numWords = "零一二三四五六七八九十";
public static int toNum(String str) {
    return numWords.indexOf(str);
}

public static String toChStr(int num) {
    
    return numWords.substring(num,num+1);
}

代码持续改进中,欢迎讨论交流

posted @ 2020-02-05 20:18  AaronLin  阅读(718)  评论(2编辑  收藏  举报