前言:

  本次博客包含的内容有pta题目集6-8(电信计费)三次题目集。这次的难点主要是理解老师给的类图的内容,明白各部分的作用,以及如何使各类之间联系起来。只要能够理解老师给的类图结构之后就可以比较轻松的完成本次的作业。

设计与分析:

  题目6-7-1 :电信计费系列1-座机计费

实现一个简单的电信计费程序:
假设南昌市电信分公司针对市内座机用户采用的计费方式:
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
南昌市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。

输入格式:

输入信息包括两种类型
1、逐行输入南昌市用户开户的信息,每行一个用户,
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐)
例如:u-079186300001 0
座机号码除区号外由是7-8位数字组成。
本题只考虑计费类型0-座机计费,电信系列2、3题会逐步增加计费类型。
2、逐行输入本月某些用户的通讯信息,通讯信息格式:
座机呼叫座机:t-主叫号码 接听号码 起始时间 结束时间
t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
以上四项内容之间以一个英文空格分隔,
时间必须符合"yyyy.MM.dd HH:mm:ss"格式。提示:使用SimpleDateFormat类。
以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
注意:
本题非法输入只做格式非法的判断,不做内容是否合理的判断(时间除外,否则无法计算),比如:
1、输入的所有通讯信息均认为是同一个月的通讯信息,不做日期是否在同一个月还是多个月的判定,直接将通讯费用累加,因此月租只计算一次。
2、记录中如果同一电话号码的多条通话记录时间出现重合,这种情况也不做判断,直接 计算每条记录的费用并累加。
3、用户区号不为南昌市的区号也作为正常用户处理。

输出格式:

根据输入的详细通讯信息,计算所有已开户的用户的当月费用(精确到小数点后2位,
单位元)。假设每个用户初始余额是100元。
每条通讯信息单独计费后累加,不是将所有时间累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。

错误处理:
输入数据中出现的不符合格式要求的行一律忽略。

输入样例:

在这里给出一组输入。例如:

u-079186300001 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:25
end
 

输出样例:

在这里给出相应的输出。例如:

079186300001 3.0 77.0

题目分析:

  这题老师给了相关的类图建议,我们来分析一下老师给的类图建议。

建议类图:
参见图1、2、3,可根据理解自行调整:

                                    图1
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。 ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。 UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。
 
 

                                     图2
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。 CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。
 
 

                                        图3
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList<CallRecord> callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。 LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是
座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。

我的分析:

这里的User类是用户类,用户开户时就要new一个User类,new User时要注意设置他的计费方式,用setChargeMode函数设置。
用户记录类UserRecords是用来记录户主所有的通信记录的。后面的计费就是通过这些记录来收费的。
ChargeMode是计费方式类,对不同的用户设置不同的计费规则,座机用户就是座机用户的计费规则,计费时传入用户通信记录的对象,对每一条记录进行计费。
CommunicationRecord类是所有通信记录的父类,短信记录和通话记录是具体的子类。记录了主叫电话号码和接收的电话号码,UserRecord类记录的通信记录就是这个类。
ChargeRule就是计费规则类,CallChargeRule就是打电话的计费规则,下面子类分别为座机在市内的呼叫计费,座机在省内的呼叫计费,座机在省外的呼叫计费。以后还需要添加手机在省内呼叫,手机在省外呼叫,手机的漫游接听计费的内容。还要添加一个Message的ChargeRule,里面的计算收费方法calCost传入的就要变成MessageRecord类的动态数组了,底下也跟之前一样,添加各类收费方式。
 
以上各类按照老师给的类图逐一实现各类里面的方法就可以了,大家的代码估计大同小异,主要的工作就是计费规则的设置,我就主要展示我的主类的处理和计费规则的设置。
这是主类

public class Main{
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
ArrayList<String> a=new ArrayList<String>();
String s=in.nextLine();
ArrayList<User> users=new ArrayList<User>();
while (!s.equals("end")){
if (s.matches("^u-[0-9]{11,12} 0$"))//开户匹配成功
{
String[] m=s.split("-");
User user=new User();
addU(m[1],user);
int count=0;
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().equals(user.getNumber()))
count++;
}
if (count==0)
users.add(user); } else if (s.matches("^t-\\d{10,12}\\s\\d{10,12}\\s([1-9][0-9]*\\.[1-9][0-9]?\\.[1-9][0-9]? ([0|1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}$")) {//输入信息匹配成功
String[] m=s.split("-");
String[] n=m[1].split(" ");
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码
addT(m[1],users.get(i));
} }
}
s=in.nextLine();
}
DecimalFormat d=new DecimalFormat();
String style="#0.0#";
d.applyPattern(style);
sort(users);
for (int i = 0; i < users.size(); i++) {
double balance=users.get(i).calBalance();
double cost=users.get(i).calCost();
System.out.println(users.get(i).getNumber()+" "+d.format(cost)+" "+d.format(balance));
} }
static ArrayList<User> sort(ArrayList<User> users){
for (int i = 0; i < users.size(); i++) {
for (int j = i+1; j < users.size(); j++) {
if (Long.parseLong(users.get(i).getNumber())>Long.parseLong(users.get(j).getNumber())){
Collections.swap(users,i,j);
}
}
}
return users;
}
static void addU(String s, User user){//开户
String[] n=s.split(" ");
if (n[1].equals("0")){//座机用户
LandlinePhoneCharge landlinePhoneCharge=new LandlinePhoneCharge();
ArrayList<ChargeRule> chargeRules=new ArrayList<ChargeRule>();
landlinePhoneCharge.setChargeRules(chargeRules);
user.setChargeMode(landlinePhoneCharge);
user.setNumber(n[0]);
}
}
static void addT(String s, User user){//找到是主叫号码后添加记录
String[] n=s.split(" ");
CallRecord callRecord =new CallRecord();
callRecord.setCallingNumber(n[0]);
callRecord.setAnswerNumber(n[1]);
SimpleDateFormat time=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start=n[2]+" "+n[3];
String end=n[4]+" "+n[5];
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(n[0].substring(0,4));
callRecord.setAnswerAddressAreaCode(n[1].substring(0,4));
if (callRecord.getCallingAddressAreaCode().equals("0791")){
if (callRecord.getAnswerAddressAreaCode().equals("0791")){//市内通话
user.userRecords.addCallingInCityRecords(callRecord);
} else if (callRecord.getAnswerAddressAreaCode().matches("079[0-9]")||callRecord.getAnswerAddressAreaCode().equals("0701")) {//省内通话
user.userRecords.addCallingInProvinceRecords(callRecord);
} else{//国内通话
user.userRecords.addCallingInLandRecords(callRecord);
}
} }
}

这是计费规则的设置:

class LandPhoneInCityRule extends CallingChargeRule{//市内计费规则
@Override
double calCost(ArrayList<CallRecord> callRecords){//计算花费
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.1*minutes;
}
return cost;
} }
class LandPhoneInProvinceRule extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
} class LandPhoneInLandRule extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.6*minutes;
}
return cost; }
}

这是座机的计费方式设置:


class LandlinePhoneCharge extends ChargeMode{//座机计费规则
double monthlyRent=20;
@Override
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {//设置座机计费规则
chargeRules.add(new LandPhoneInCityRule());
chargeRules.add(new LandPhoneInProvinceRule());
chargeRules.add(new LandPhoneInLandRule());
this.chargeRules = chargeRules;
} @Override
double calCost(UserRecords userRecords){//计算花费
double cost=0;
cost+=chargeRules.get(0).calCost(userRecords.getCallingInCityRecords());
cost+=chargeRules.get(1).calCost(userRecords.getCallingInProvinceRecords());
cost+=chargeRules.get(2).calCost(userRecords.getCallingInLandRecords());
return cost;
}
@Override
double getMonthlyRent(){//得到月租
return monthlyRent;
}
}

这是我的各类之间的联系图

这是SourceMonitor生成的报表内容

踩坑心得:

  主要难点就是理解老师的类图,接下来一步一步实现就可以了。注意日期使用dateformat的数据类型来记录。开户时要注意把计费规则设置好。

题目7-7-1: 电信计费系列2-手机+座机计费

实现南昌市电信分公司的计费程序,假设该公司针对手机和座机用户分别采取了两种计费方案,分别如下:
1、针对市内座机用户采用的计费方式(与电信计费系列1内容相同):
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
假设本市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。
2、针对手机用户采用实时计费方式:
月租15元,市内省内接电话均免费,市内拨打市内电话0.1元/分钟,市内拨打省内电话0.2元/分钟,市内拨打省外电话0.3元/分钟,省内漫游打电话0.3元/分钟,省外漫游接听0.3元/分钟,省外漫游拨打0.6元/分钟;
注:被叫电话属于市内、省内还是国内由被叫电话的接听地点区号决定,比如以下案例中,南昌市手机用户13307912264在区号为020的广州接听了电话,主叫号码应被计算为拨打了一个省外长途,同时,手机用户13307912264也要被计算省外接听漫游费:
u-13307912264 1
t-079186330022 13307912264 020 2022.1.3 10:00:25 2022.1.3 10:05:11

输入:
输入信息包括两种类型
1、逐行输入南昌市用户开户的信息,每行一个用户,含手机和座机用户
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐)
例如:u-079186300001 0
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题在电信计费系列1基础上增加类型1-手机实时计费。
手机设置0或者座机设置成1,此种错误可不做判断。
2、逐行输入本月某些用户的通讯信息,通讯信息格式:
座机呼叫座机:t-主叫号码 接听号码 起始时间 结束时间
t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
以上四项内容之间以一个英文空格分隔,
时间必须符合"yyyy.MM.dd HH:mm:ss"格式。提示:使用SimpleDateFormat类。
输入格式增加手机接打电话以及收发短信的格式,手机接打电话的信息除了号码之外需要额外记录拨打/接听的地点的区号,比如:
座机打手机
t-主叫号码 接听号码 接听地点区号 起始时间 结束时间
t-079186330022 13305862264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
手机互打
t-主叫号码 拨号地点 接听号码 接听地点区号 起始时间 结束时间
t-18907910010 0791 13305862264 0371 2022.1.3 10:00:25 2022.1.3 10:05:11

注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。

输出:
根据输入的详细通讯信息,计算所有已开户的用户的当月费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条通讯、短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。

输入样例:

在这里给出一组输入。例如:

u-13811111111 1
t-13811111111 0791 13811111110 020 2022.1.3 08:00:00 2022.1.3 08:09:20
end
 

输出样例:

在这里给出相应的输出。例如:

13811111111 3.0 82.0

题目分析:

主要的类图分析在上一题已经给大家分析过了,这题只需要在上一题的基础上添加手机的计费规则和主类中开户和记录的处理。接下来给大家展示我写的代码。

这是主类:


public class Main {
public static void main(String[] args) {
//座机呼叫座机
String tPattern = "t-0\\d{9,11}\\s" + "0\\d{9,11}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//座机打手机
String tPattern1 = "t-\\d{10,12}\\s" + "1\\d{10}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机互打
String tPattern2 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{11}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机打座机
String tPattern3 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{10,12}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}"; Scanner in=new Scanner(System.in); String s=in.nextLine();
ArrayList<User> users=new ArrayList<User>();
while (!s.equals("end")){
if (s.matches("^u-[0-9]{11,12} 0$")||s.matches("[u]-1[0-9]{10}\\s[1]"))//开户匹配成功
{
String[] m=s.split("-");
User user=new User();
addU(m[1],user);
int count=0;
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().equals(user.getNumber()))
count++;
}
if (count==0)
users.add(user);
} else if (s.matches(tPattern)) {//输入信息匹配成功//座机互打
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[0].substring(0,4);
String answerNumber = n[1];
String answerAddress = n[1].substring(0,4);
String beginTime = n[2]+" "+n[3];
String endTime = n[4]+" "+n[5];
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addT(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}if (user.getNumber().equals(n[1])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
}
} else if (s.matches(tPattern1)) {//座机打手机
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[0].substring(0,4);
String answerNumber = n[1];
String answerAddress = n[2];
String beginTime = n[3]+" "+n[4];
String endTime = n[5]+" "+n[6]; for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addT(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
if (user.getNumber().equals(n[1])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
}
} else if (s.matches(tPattern2)) {//手机互打
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[1];
String answerNumber = n[2];
String answerAddress = n[3];
String beginTime = n[4]+" "+n[5];
String endTime = n[6]+" "+n[7]; for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addTMCalling(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
if (user.getNumber().equals(n[2])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
} } else if (s.matches(tPattern3)) {//手机打座机
String[] m=s.split("-");
String[] n=m[1].split(" ");
String callNumber = n[0];
String callAddress = n[1];
String answerNumber = n[2];
String answerAddress = n[2].substring(0,4);
String beginTime = n[3]+" "+n[4];
String endTime = n[5]+" "+n[6];
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(n[0])){//找到该号码主叫
addTMCalling(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
if (user.getNumber().equals(n[2])){//接听
addTAnswer(callNumber,callAddress,answerNumber,answerAddress,beginTime,endTime,users.get(i));
}
}
} s=in.nextLine();
}
DecimalFormat d=new DecimalFormat();
String style="#0.0#";
d.applyPattern(style);
sort(users);
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
}
for (int i = 0; i < users.size(); i++) {
if (!users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
} }
static ArrayList<User> sort(ArrayList<User> users){ for (int i = 0; i < users.size(); i++) {
for (int j = i+1; j < users.size(); j++) {
if (Long.parseLong(users.get(i).getNumber())>Long.parseLong(users.get(j).getNumber())){
Collections.swap(users,i,j);
}
}
}
return users;
}
public static void addU(String s, User user){//开户
String[] n=s.split(" ");
if (n[1].equals("0")){//座机用户
LandlinePhoneCharge landlinePhoneCharge=new LandlinePhoneCharge();
ArrayList<ChargeRule> chargeRules=new ArrayList<ChargeRule>();
landlinePhoneCharge.setChargeRules(chargeRules);
user.setChargeMode(landlinePhoneCharge);
user.setNumber(n[0]);
}
if (n[1].equals("1")){//手机
MobilePhoneCharge mobilePhoneCharge= new MobilePhoneCharge();
ArrayList<ChargeRule> chargeRules = new ArrayList<>();
mobilePhoneCharge.setChargeRules(chargeRules);
user.setChargeMode(mobilePhoneCharge);
user.setNumber(n[0]); }
}
static void addT(String callNumber, String callAddress,String answerNumber,String answerAddress,String beginTime, String endTime, User user){//找到是主叫号码后添加记录
CallRecord callRecord =new CallRecord();
callRecord.setCallingNumber(callNumber);
callRecord.setAnswerNumber(answerNumber);
SimpleDateFormat time=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start=beginTime;
String end=endTime;
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(callAddress);
callRecord.setAnswerAddressAreaCode(answerAddress);
if (callRecord.getCallingAddressAreaCode().equals("0791")){
if (callRecord.getAnswerAddressAreaCode().equals("0791")){//市内通话
user.userRecords.addCallingInCityRecords(callRecord);
} else if (callRecord.getAnswerAddressAreaCode().matches("079[0-9]")||callRecord.getAnswerAddressAreaCode().equals("0701")) {//省内通话
user.userRecords.addCallingInProvinceRecords(callRecord);
} else{//国内通话
user.userRecords.addCallingInLandRecords(callRecord);
}
}
}
static void addTAnswer(String callNumber, String callAddress,String answerNumber,String answerAddress,String beginTime, String endTime, User user){//找到是接听号码后添加记录 CallRecord callRecord =new CallRecord();
callRecord.setCallingNumber(callNumber);
callRecord.setAnswerNumber(answerNumber); SimpleDateFormat time=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start=beginTime;
String end=endTime;
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(callAddress);
callRecord.setAnswerAddressAreaCode(answerAddress);
if (callRecord.getAnswerAddressAreaCode().equals("0791")){//市内接听电话
user.userRecords.addAnswerInCityRecords(callRecord);
}
else if (callRecord.getAnswerAddressAreaCode().matches("079[0-9]")||callRecord.getAnswerAddressAreaCode().equals("0701")){//省内接听电话
user.userRecords.addAnswerInProvinceRecords(callRecord);
}else {//省外接听电话
user.userRecords.addAnswerInLandRecords(callRecord);
}
} static void addTMCalling(String callNumber, String callAddress, String answerNumber, String answerAddress, String beginTime, String endTime, User user) {//找到是手机主叫号码后添加记录 CallRecord callRecord = new CallRecord();
callRecord.setCallingNumber(callNumber);
callRecord.setAnswerNumber(answerNumber);
SimpleDateFormat time = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String start = beginTime;
String end = endTime;
try {
callRecord.setStartTime(time.parse(start));
callRecord.setEndTime(time.parse(end));
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(callAddress);
callRecord.setAnswerAddressAreaCode(answerAddress);
if (callRecord.getCallingAddressAreaCode().equals("0791")) {//市内拨打电话
user.userRecords.addCallingInCityRecords(callRecord);
} else if (callRecord.getCallingAddressAreaCode().matches("079[0-9]") || callRecord.getCallingAddressAreaCode().equals("0701")) {//省内拨打电话
user.userRecords.addCallingInProvinceRecords(callRecord);
} else {//省外接听电话
user.userRecords.addCallingInLandRecords(callRecord);
}
}
}

这是手机的各种计费规则的设置


class MobilePhoneCallCityToCity extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.1*minutes;
}
return cost;
}
} class MobilePhoneCallCityToProvince extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.2*minutes;
}
return cost;
}
} class MobilePhoneCallCityToLand extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
}
class MobilePhoneCallInProvince extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
} class MobilePhoneAnswerInLand extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.3*minutes;
}
return cost;
}
} class MobilePhoneCallInLand extends CallingChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords){
double cost=0;
for (int i = 0; i < callRecords.size(); i++) {
Date start=callRecords.get(i).startTime;
Date end=callRecords.get(i).endTime;
int minutes=(int)((end.getTime()-start.getTime())/(1000*60));
if (((end.getTime()-start.getTime())/1000)%60>0)
minutes++;
cost+=0.6*minutes;
}
return cost;
}
}

这是手机的计费方式的设置


class MobilePhoneCharge extends ChargeMode{//移动手机的计费规则
double monthlyRent=15;
@Override
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {//设置座机计费规则
chargeRules.add(new MobilePhoneCallCityToCity());
chargeRules.add(new MobilePhoneCallCityToProvince());
chargeRules.add(new MobilePhoneCallCityToLand());
chargeRules.add(new MobilePhoneCallInLand());
chargeRules.add(new MobilePhoneCallInProvince());
chargeRules.add(new MobilePhoneAnswerInLand()); this.chargeRules = chargeRules;
} @Override
double calCost(UserRecords userRecords){//计算花费
double cost=0;
cost+=chargeRules.get(0).calCost(userRecords.getCallingInCityToCityRecords());
cost+=chargeRules.get(1).calCost(userRecords.getCallingInCityToProvinceRecords());
cost+=chargeRules.get(2).calCost(userRecords.getCallingInCityToLandRecords());
cost+=chargeRules.get(3).calCost(userRecords.getCallingInLandRecords());
cost+=chargeRules.get(4).calCost(userRecords.getCallingInProvinceRecords());
cost+=chargeRules.get(5).calCost(userRecords.getAnswerInLandRecords()); return cost;
}
@Override
double getMonthlyRent(){//得到月租
return monthlyRent;
}
}

代码的类图和sourceMonitor报表已经在上题放出。

踩坑心得:

  本题主要是在上题的基础上添加了手机的计费规则,手机的计费规则比座机计费规则复杂度提升了许多,所以在计费时也有不同的计费规则。注意要对不同的拨打方式设置不同的函数来记录。还有在展示时要将座机的优先展示再按号码大小排序。

题目8-7-1: 电信计费系列3-短信计费

实现一个简单的电信计费程序,针对手机的短信采用如下计费方式
1、接收短信免费,发送短信0.1元/条,超过3条0.2元/条,超过5条0.3元/条。
2、如果一次发送短信的字符数量超过10个,按每10个字符一条短信进行计算。

输入:
输入信息包括两种类型
1、逐行输入南昌市手机用户开户的信息,每行一个用户。
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐 3-手机短信计费)
例如:u-13305862264 3
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题只针对类型3-手机短信计费。
2、逐行输入本月某些用户的短信信息,短信的格式:
m-主叫号码,接收号码,短信内容 (短信内容只能由数字、字母、空格、英文逗号、英文句号组成)
m-18907910010 13305862264 welcome to jiangxi.
m-13305862264 18907910010 thank you.

注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
输出:
根据输入的详细短信信息,计算所有已开户的用户的当月短信费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
本题只做格式的错误判断,无需做内容上不合理的判断,比如同一个电话两条通讯记录的时间有重合、开户号码非南昌市的号码、自己给自己打电话等,此类情况都当成正确的输入计算。但时间的输入必须符合要求,比如不能输入2022.13.61 28:72:65。

本题只考虑短信计费,不考虑通信费用以及月租费。

输入样例:

在这里给出一组输入。例如:

u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaaaaaaaaaaaaa
end
 

输出样例:

在这里给出相应的输出。例如:

18907910010 0.3 99.7
 
### 输入样例1:
 

在这里给出一组输入。例如:

u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaa
m-18907910010 13305862264 aaaaaaa.
m-18907910010 13305862264 bb,bbbb
end
 
 

输出样例1:

在这里给出相应的输出。例如:

18907910010 0.5 99.5

题目分析:

  本题的要求是只记录短信的相较于前面两题较为简单,接下来直接给出相应的代码。

这是主类:

public class Main {
public static void main(String[] args) {
//座机呼叫座机
String tPattern = "t-0\\d{9,11}\\s" + "0\\d{9,11}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//座机打手机
String tPattern1 = "t-\\d{10,12}\\s" + "1\\d{10}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机互打
String tPattern2 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{11}\\s\\d{3,4}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
//手机打座机
String tPattern3 = "t-\\d{11}\\s\\d{3,4}\\s" + "\\d{10,12}\\s"
+ "([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3}\\.(1[0-2]|[1-9])\\.([1-9]|(1|2)[0-9]|(30|31)) ([0|1][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9] ?){2}";
String message_regex_3 = "[m]-1[0-9]{10}\\s" + "1[0-9]{10}\\s" + "[0-9a-zA-Z\\s\\.,]+";
Scanner in=new Scanner(System.in); String s=in.nextLine();
ArrayList<User> users=new ArrayList<User>();
while (!s.equals("end")){
if (s.matches("[u]-1[0-9]{10}\\s[3]")||s.matches("^u-[0-9]{11,12} 0$")||s.matches("[u]-1[0-9]{10}\\s[1]"))//开户匹配成功
{
String[] m=s.split("-");
User user=new User();
addU(m[1],user);
int count=0;
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().equals(user.getNumber()))
count++;
}
if (count==0)
users.add(user);
}
else if (s.matches(message_regex_3)){
String m=s.substring(2,26);
String[] n = m.split(" ");
String sendNumber = n[0];
String receiveNumber = n[1];
String message = s.substring(26);
for (int i = 0; i < users.size(); i++) {
User user=users.get(i);
if (user.getNumber().equals(sendNumber)){//找到该号码主叫
addMSend(sendNumber,receiveNumber,message,user);
}
if (user.getNumber().equals(receiveNumber)){
addMReceive(sendNumber,receiveNumber,message,user);
}
}
} s=in.nextLine();
}
DecimalFormat d=new DecimalFormat();
String style="#0.0#";
d.applyPattern(style);
sort(users);
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
}
for (int i = 0; i < users.size(); i++) {
if (!users.get(i).getNumber().substring(0,4).equals("0791")) {
double balance = users.get(i).calBalance();
double cost = users.get(i).calCost();
System.out.println(users.get(i).getNumber() + " " + d.format(cost) + " " + d.format(balance));
}
} } static ArrayList<User> sort(ArrayList<User> users){ for (int i = 0; i < users.size(); i++) {
for (int j = i+1; j < users.size(); j++) {
if (Long.parseLong(users.get(i).getNumber())>Long.parseLong(users.get(j).getNumber())){
Collections.swap(users,i,j);
}
}
}
return users;
}
public static void addU(String s, User user){//开户
String[] n=s.split(" ");
if (n[1].equals("3")){//短信
MessageCharge messageCharge = new MessageCharge();
ArrayList<ChargeRule> chargeRules = new ArrayList<>();
messageCharge.setChargeRules(chargeRules);
user.setChargeMode(messageCharge);
user.setNumber(n[0]);
}
}
static void addMSend(String sendNumber,String receiveNumber,String message,User user){
MessageRecord messageRecord = new MessageRecord();
messageRecord.setMessage(message);
messageRecord.setCallingNumber(sendNumber);
messageRecord.setAnswerNumber(receiveNumber);
user.userRecords.addSendMessageRecords(messageRecord);
}
static void addMReceive(String sendNumber, String receiveNumber, String message, User user){
MessageRecord messageRecord = new MessageRecord();
messageRecord.setMessage(message);
messageRecord.setCallingNumber(sendNumber);
messageRecord.setAnswerNumber(receiveNumber);
user.userRecords.addReceiveMessageRecords(messageRecord);
} }

这是message的计费规则的设置


class MessageChargeRule extends ChargeRule{
@Override
double calCost(ArrayList<CallRecord> callRecords) {
return 0;
} double mesCost(ArrayList<MessageRecord> messageRecords){
double cost=0;
int num=0;
for (int i = 0; i < messageRecords.size(); i++) {
int count=messageRecords.get(i).getMessage().length()/10;
if (messageRecords.get(i).getMessage().length()%10>0)
count++;
num+=count;
}
if (num<=3){
cost=0.1*num;
} else if (num > 3 && num <= 5) {
cost = 0.3+0.2*(num-3);
} else if (num > 5) {
cost = 0.3+0.4+0.3*(num-5);
}
return cost;
}
}

这是message计费方式的设置

class MessageCharge extends ChargeMode{
@Override
public void setChargeRules(ArrayList<ChargeRule> chargeRules){
chargeRules.add(new MessageChargeRule());
this.chargeRules=chargeRules;
} @Override
double calCost(UserRecords userRecords) {
return chargeRules.get(0).mesCost(userRecords.getSendMessageRecords());
} @Override
double getMonthlyRent() {
return 0;
}

各类的联系图和sourceMonitor生成的报表内容再第一题给出了。

踩坑心得:

  本题的难度相较于前面两题要简单一点。只需要考虑短信这一点就可以了,不过在设置MessageChargeRule时不能使用CallRecord的方法,可以选择使用下转型或者单独添加一个计算message的方法。

总结:

  本次的三个题目主要的难点就是能过够读懂老师的类图并将它实现,总体难度并不高,比较容易能够完成,这三题还是要求比较熟练的使用继承和多态以及动态数组的使用,算是对以前的知识的一次比较完整的总结测试了。经过这几次的作业以后,确实使我对java有了更深的理解,也让我能够更加熟练的使用java来编写程序了。感谢您的阅读!

最新文章

  1. 移动端1px边框
  2. SpringMVC 模拟登陆
  3. ZOJ 3819 Average Score(平均分)
  4. mokoid android open source HAL hacking in a picture
  5. Citrix 服务器虚拟化之十八 桌面虚拟化之部署MCS随机桌面池
  6. FILTER的执行次数和驱动表问题
  7. SessionState的配置 [转载]
  8. iphone/ipad前端开发技巧
  9. [LeetCode]题解(python):029-Divide Two Integers
  10. Effective Java 之-----静态工厂与构造器
  11. bzoj 3166 [Heoi2013]Alo 可持久化Trie
  12. FFmpeg 结构体学习(一): AVFormatContext 分析
  13. 「造个轮子」——cicada 源码分析
  14. 流式大数据计算实践(5)----HBase使用&amp;SpringBoot集成
  15. Android/Linux Thermal框架分析及其Governor对比
  16. (netty宝贵知识)
  17. [1]windows 内核情景分析---说明
  18. 高性能kv存储之Redis、Redis Cluster、Pika:如何应对4000亿的日访问量?
  19. Vue滚动加载自定义指令
  20. 阿里路由框架ARouter的使用步骤

热门文章

  1. 关于Java中数组的简单使用
  2. js获取当天时间,凌晨0点
  3. nodejs查询
  4. 2023-03-01 Error: Invalid hook call.Hooks can only be called inside of the body of a function component.
  5. python3.7与python3.6,python2.7 pyc文件头部差异
  6. go开发框架推荐
  7. CB9328是一颗PA+LNA+Switch三合一的FEM 兼容S*8112Q
  8. python中如果获取对象的内存地址,及字典的地址
  9. linux dma
  10. C2驾驶车型