问题描述
中国有句俗语叫“三天打鱼两天晒网”。某人从2010年1月1日起开始“三天打鱼两天晒网”,问这个人在以后的某一天中是“打鱼”还是“晒网”。用C或C++语言实现程序解决问题。
问题分析加算法设计
本题需要求出此人在某一天是“打鱼”还是“晒网”的状态,因此,需首先求出从2010年1月1日起到所输入日期的时间间隔天数。因为此人打鱼晒网的周期为5天,因此对天数求5的余数,根据所得余数判断他的状态。我们默认他前三天“打鱼”后两天“晒网”,因此,当余数为1、2、3时,可知他在“打鱼”,当余数为4、0时,则表示他在“晒网”。
输入检查
先判断输入字符串长度是否符合规则,然后拆分字符串并转化为int型数据,看是否能够成功,其次,检查所输入年份是否在初始年份之后,最后检查输入的月日是否符合规范。
求时间间隔天数
应考虑闰年因素,即2月为29天的特殊情况,在求解时,在遇到闰年时应该加一天。而闰年的判断条件是:
年份能够被4整除不能被100整除、或仅能够被400整除
存储数据
使用Dates类进行存储日期,分别存储年year,月mouth,日day,并在初始化时进行闰年判断,为下一步计算日期间隔做准备。
计算日期间隔,将该方法封装为Dates的成员方法,考虑到不确定哪个日期在前的情况,在计算前进行日期的比较,将较早的日期作为减数,解决运算中频繁使用abs()方法求绝对值的问题。
日期间隔分为以下几个部分:闰年数量、平年数量和零散天数(即计算输入日期中的月日为该年中的第几天),最后日期间隔的计算公式即:
总天数 = 闰年数(leapYearNum)*366 + 平年数(commonYearNum)*365 + 零散天数(daysNum)
Utils工具类
闰年数计算、平年数计算、零散日期计算等方法封装在Utils工具类中作为静态方法,方便调用。通过传入两个Dates对象,计算所需要的值。
流程图
代码块
Dates.java
package pm01;
/**
* @description 记录日期数据,具有计算日期间隔天数的方法
* @see #year 年份
* @see #month 月份
* @see #day 天数
* @see #getDifference(Dates) 计算两个日期间的时间间隔
* @author Wang Rongqiang
* @date 2021-03-20 15:53
* @version 1.0
**/
public class Dates {
//记录日期的年份,月份,日期以及是否为闰年
private final int year;
private final int month;
private final int day;
private final boolean isLeapY;
public Dates(String dt) {
//根据输入字符串中年份月份等数据所在位置,截取出数值
this.year = Integer.parseInt(dt.substring(0, 4));
this.month = Integer.parseInt(dt.substring(4, 6));
this.day = Integer.parseInt(dt.substring(6));
//判断是否为闰年
isLeapY = Utils.isLeap(year);
}
/**
*
* @params [d]
* @author Wang Rongqiang
* @Description //TODO 计算两个日期相差天数
* @date 2021/3/20 17:38
* @return int
*/
public int getDifference(Dates d) {
//记录d日期的年份、月份、日期
int year = d.getYear();
int month = d.getMonth();
int day = d.getDay();
//记录两年间闰年和平年的数量
int leapYearNum, commonYearNum;
//计算两个年间闰年个数
leapYearNum = Utils.leapYearCount(this, d);
//两年间平年个数,总年数减去闰年数
commonYearNum = Utils.commonYearCount(this, d,
leapYearNum);
//计算到一年的零散天数,即month月day天为year年第几天
int daysNum;
//判断哪个年份为结束年份,并计算该年零散天数
if(this.year <= year)
daysNum = Utils.dayNumInYear(d);
else
daysNum = Utils.dayNumInYear(this);
//计算总天数,闰年*366+平年*365+零散日期
return leapYearNum *365 + commonYearNum * 366 +
daysNum;
}
public int getYear() {
return year;
}
public int getMonth() {
return month;
}
public int getDay() {
return day;
}
public boolean isLeapY() {
return isLeapY;
}
@Override
public String toString() {
return year + "年" + month + "月" + day + "日";
}
}
Utils.java
package pm01;
import java.util.Arrays;
/**
* @description 工具类,实现输入检查,闰年判断、月份合法检查等操作
* @see #defaultDate 起始日期
* @see #inputCheck(String) 输入检查
* @see #dateIsOK(int, int, int) 判断日期是否合法
* @see #isLeap(int) 判断是否为闰年
* @see #leapYearCount(Dates, Dates) 计算闰年数
* @see #commonYearCount(Dates, Dates, int) 计算平年数
* @see #dayNumInYear(Dates) 计算这天是这年的第几天
* @see #dayNumInMonth(int) 计算这个月有多少天
* @author Wang Rongqiang
* @date 2021-03-20 17:06
* @version 1.0
**/
public class Utils {
public static String defaultDate = "20100101";
/**
*
* @params [input]
* @author Wang Rongqiang
* @Description //TODO 判断输入是否合法,日期格式以及日期是否在起始日期后
* @date 2021/3/20 17:37
* @return boolean
*/
public static boolean inputCheck(String input) {
//先根据长度判断是否合法
if(input.length()!=defaultDate.length()) {
System.out.println("输入格式有误!");
return false;
}
//用以记录input的年月日数据
int y, m, d;
//先尝试拆分字符串,获取年月日的数据,如果报错则输入非法
try{
y = Integer.parseInt(input.substring(0, 4));
m = Integer.parseInt(input.substring(4, 6));
d = Integer.parseInt(input.substring(6));
} catch (NumberFormatException e) {
System.out.println("输入的格式有误!");
return false;
}
//判断月份和日期是否合法
if (!dateIsOK(y, m, d)) {
System.out.println("输入的格式有误!");
return false;
}
//判断输入日期是否在起始日期之前
if (input.compareTo(defaultDate) < 0) {
System.out.println("输入日期在初始日期之前!");
return false;
}
return true;
}
/**
*
* @params [y, m, d]
* @author Wang Rongqiang
* @Description //TODO 判断日期是否合法
* @date 2021/3/20 17:37
* @return boolean
*/
private static boolean dateIsOK(int y, int m, int d){
//月份不会大于12
if(m > 13)
return false;
//记录大月(31天)的月份
int[] bigMonth = {1, 3, 5, 7, 8, 10, 12};
//根据月份大小,判断日期是否合法
//m月是否出现在数组中,判断m月是否为大月
if(Arrays.binarySearch(bigMonth, m) > 0)
//m月为大,日期不能大于31
return d <= 31;
//m月小,若m不为2月,则d不大于30
else if(m != 2)
return d <= 30;
//m为2月,判断该年是否为闰年
else if(isLeap(y))
//闰年2月不能大于29天
return d <= 29;
else
//平年2月不能大于28天
return d <= 28;
}
/**
*
* @params [y]
* @author Wang Rongqiang
* @Description //TODO 判断年份是否为闰年
* @date 2021/3/20 17:49
* @return boolean
*/
public static boolean isLeap(int y) {
//根据闰年判断条件书写语句
return y % 4 == 0 && y % 100 != 0 || y % 400 == 0;
}
/**
*
* @params [date1, date2]
* @author Wang Rongqiang
* @Description //TODO 计算begin和end两日期之间的闰年数
* @date 2021/3/20 18:08
* @return int
*/
public static int leapYearCount(Dates dates1, Dates dates2) {
//记录起始与结束年份
int beginYear, endYear;
//date1,date2两个年份哪一年更早设置起始与结束年份
if (dates1.getYear() <= dates2.getYear()) {
beginYear = dates1.getYear();
endYear = dates2.getYear();
} else {
beginYear = dates2.getYear();
endYear = dates1.getYear();
}
//闰年数量
int count = 0;
//遍历查找闰年
for (int i = beginYear; i < endYear; i++) {
//判断是否为闰年
if (isLeap(i))
count++;
}
return count;
}
/**
*
* @params [date1, date2, leapYNum]
* @author Wang Rongqiang
* @Description //TODO 计算两年中平年的个数
* @date 2021/3/20 19:07
* @return int
*/
public static int commonYearCount(Dates dates1, Dates dates2, int leapYNum) {
//记录平年数量
int commonYearNum;
//判断哪一年在前,则设置该年为减数,来计算总年数
//总年数减去闰年数即平年数
if (dates1.getYear() <= dates2.getYear()) {
commonYearNum = dates2.getYear() - dates1.getYear() - leapYNum;
} else {
commonYearNum = dates1.getYear() - dates2.getYear() - leapYNum;
}
return commonYearNum;
}
/**
*
* @params [dates]
* @author Wang Rongqiang
* @Description //TODO 计算某天是这年的第几天
* @date 2021/3/20 19:30
* @return int
*/
public static int dayNumInYear(Dates dates) {
//记录总天数,此时2月先设为28天
int dayNum = 0;
for (int i = 1; i <= dates.getMonth(); i++) {
if (i == dates.getMonth())
dayNum+=dates.getDay();
else
dayNum+=dayNumInMonth(i);
}
//判断是否为闰年且月份大与2,闰年天数加一
if (dates.getMonth() > 2 && dates.isLeapY())
dayNum++;
return dayNum;
}
/**
*
* @params [m]
* @author Wang Rongqiang
* @Description //TODO 计算某月有多少天
* @date 2021/3/20 19:41
* @return int
*/
private static int dayNumInMonth(int m) {
//将大月记录在数组中
int[] bigMonth = {1, 3, 5, 7, 8, 10, 12};
//m月是否出现在数组中,判断m月是否为大月
if(Arrays.binarySearch(bigMonth, m) > 0)
return 31;
//不是2月也不是大月
else if (m != 2)
return 30;
//是2月先返回28天,后续根据平闰年觉得是否加一
else
return 28;
}
}
MainClass.java
package pm01;
import java.io.*;
import java.util.Scanner;
/**
* @author Wang Rongqiang
* @version 1.0
* @description 用来运行程序,并计算这天是打鱼还是晒网
* @date 2021-03-20 19:47
**/
public class MainClass {
public static void main(String[] args) {
System.out.println("某人从2010年1月1日开始“三天打鱼两天晒网”," +
"输入一个日期,将获得该天他的状态");
Scanner sc = new Scanner(System.in);
int n;
while (true){
System.out.println("1、从键盘手动输入");
System.out.println("2、从文件获取输入");
System.out.println("3、退出");
System.out.println("请选择:");
n = sc.nextInt();
switch (n) {
case 1 -> {
fromKB();
}
case 2 -> {
fromFile();
}
case 3 -> {
System.out.println("退出");
return;
}
}
}
}
/**
*
* @params []
* @author Wang Rongqiang
* @Description //TODO 从键盘获取输入数据
* @date 2021/3/21 17:28
* @return void
*/
private static void fromKB() {
Scanner sc = new Scanner(System.in);
//记录输入的日期
String date;
//通过循环,检查输入是否合法,不合法则重新输入
do {
System.out.println("请输入一个日期(如:20200524):");
date = sc.next();
}while (!Utils.inputCheck(date));
//使用Dates类记录初始时间和输入时间
Dates beginDate = new Dates(Utils.defaultDate);
Dates endDate = new Dates(date);
//记录日期间隔天数
int days = beginDate.getDifference(endDate);
//计算这天是打鱼还是晒网
//“三天打鱼两天晒网”则应对天数求5的余数
int n = days % 5;
System.out.println("此人从" + beginDate.toString() +
"开始“三天打鱼两天晒网”");
//判断余数为1,2,3时为打鱼,其余时为晒网
if (n != 0 && n <= 3) {
System.out.println("则" + endDate.toString() + "他在“打鱼”");
} else {
System.out.println("则" + endDate.toString() + "他在“晒网”");
}
}
/**
*
* @params []
* @author Wang Rongqiang
* @Description //TODO 测试模式,即从文件中批量读取数据
* @date 2021/3/21 17:29
* @return void
*/
private static void fromFile() {
//测试代码
//输出文件及地址
String outPath = "D:/out.txt";
//输入文件地址
String inPath = "D:/in.txt";
File write = new File(outPath);
try {
write.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileWriter writer = new FileWriter(write);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
try (FileReader file = new FileReader(inPath);
BufferedReader br = new BufferedReader(file);
) {
//记录输入的日期
String date = "";
//通过循环,测试多组样例
do {
try {
date = br.readLine();
if (date == null)
break;
if (!Utils.inputCheck(date))
continue;
} catch (IOException e) {
e.printStackTrace();
}
//使用Dates类记录初始时间和输入时间
Dates beginDate = new Dates(Utils.defaultDate);
Dates endDate = new Dates(date);
//记录日期间隔天数
int days = beginDate.getDifference(endDate);
//计算这天是打鱼还是晒网
//“三天打鱼两天晒网”则应对天数求5的余数
int n = days % 5;
try {
File writeName = new File(outPath);
writeName.createNewFile();
FileWriter writer = new FileWriter(writeName, true);
BufferedWriter out = new BufferedWriter(writer);
//判断余数为1,2,3时为打鱼,其余时为晒网
if (n != 0 && n <= 3) {
out.write(endDate.toString() + "他在“打鱼”\n");
out.flush();
} else {
out.write(endDate.toString() + "他在“晒网”\n");
out.flush();
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
} while (true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果
- 通过文件读取批量数据的运行结果
- 通过手动输入的运行结果