问题描述
编写一个Inventory.java完成以下功能(没有学过Java文件处理之前,各位同学可以使用硬编码将数据放进两个Vector变量里。等学过Java文件处理之后,再补充数据文件读取部分):
1.程序首先打开并读取Inventory.txt中记录的所有库存记录,然后读取Transactions.txt,处理这个文件中包含的事务,记录发货记录到Shipping.txt,并记录错误信息到Errors.txt中。最后更新库存到另外一个文件NewInventory.txt中。
2.文件Inventory.txt和NewInventory.txt的每行包含一个存货记录,没条记录包含下面一些字段息,这些字段之间用一个tab分开(见后面的文件格式):
字段 | 格式和含义 |
Item number | 字符串型,货物编号 |
Quantity | 整型,货物数量 |
Supplier | 字符串型,供应商编号 |
Description | 字符串型,货物描述 |
3.字段Items按照从小到大的顺序写入文件的。注意Item号不必连续,如Item号为752的后面可能是800。
4.文件Transactions.txt包含几个不同的处理记录(每行一条记录)。每条记录前面以一个大写字母开头,表示这条记录是什么类型的事务。在不同的大写字母后面是不同的信息格式。所有的字段也是以tab键分开的(见Transactions.txt文件格式)。
5.以'O'开头的事务表示这是一个发货订单,即某一种货物应该发给特定的客户。Item number和Quantity的格式如上面表格定义。Custom编号和上面的Supplier编号一致。处理一条定单记录(以'O'开头的事务)意味着从减少库存记录中相应货物的数量(减少的数量=发货单中的数量),记录发货信息到Shipping.txt中。注意:Inventory.txt中的quantity不应该小于0,如果对于某一种货物,库存的数量小于发货单的数量的话,系统应该停止处理发货单,并记录出错信息到Errors.txt。如果对于某一种货物有多个发货单,而且库存总量小于这些发货单的总和的话,系统应该按照发货单中的数量从小到大的有限原则满足客户。也就是说,对于某一种货物如果一个数量Quantity少的发货单没有处理之前,数量Quantity多的发货单永远不会被处理。(这种处理原则不受发货单记录在Transactions.txt的先后顺序影响)
6.以'R'开头的事务表示这是一个到货单记录,在'R'后面是Item number和它的数量Quanlity。处理一条到货单意味着增加库存中相应货物的数量(增加的数量=到货单中的数量)。注意:如果在Transactions.txt文件中,到货单出现在发货单之后,到货单中的货物数量可以用来填补发货单中的数量(可以理解成在Transactions.txt中,优先处理到货单)。
7.以'A'开头的事务表示向库存中增加一种新的货物(即这种货物以前库存中没有),在'A'后面是Item number,供应商supplier以及货物的描述description。处理一个新增货物记录意味着向库存中增加一个数量Quantity为0的新的Item。你可以假设在一个Transactions.txt中,新增货物记录总是出现在第一个到货单之前。
8.以'D'开头的事务表示从库存中删除一种货物,在'D'后面是Item号。删除操作总是在所有的事物处理之后才被处理,以保证对于可能出现的同一种货物的发货单的操作能在删除之前被正确处理。如果要删除的某种货物的库存量Quantity不为0的话,系统应该向Errors.txt记录出错信息。
9.文件Shipping.txt中的每一行代表给某一客户的发货信息。Shipping.txt中的每一行分别是客户编号、Item号、货物数量,它们之间用tab键分隔。如果发货单中有两条客户编号和Item编号一样的记录,在Shipping.txt中应该将这两条发货信息合并(即将它们的数量相加)。
10.Errors.txt文件包含未发送的发货记录和库存量大于0的删除记录。Errors.txt每一行包含Custom编号、Item编号以及发货单上的数量Quantity。对于删除操作,Custom编号为0,数量Quntity为库存中的Quantity.
11.实验测试数据:
Inventory.txt
Transactions.txt
如果有问题,加QQ群623914970,可以来问
希望同学们学习其中的思路,而不是直接复制粘贴代码。
设计思路
1.设计货物类Goods,包含Item_number、Quantity、Supplier、Description等属性和相应的get和set方法
2.设计库存类Inventory。该类包含一个protected ArrayList<Goods> inventory存放各种货物的信息。构造函数从文件Inventory.txt读取库存,存放于inventory中。类中还包含一个updateInventory的方法,来更新交易后的库存
3.设计交易类Transaction。该类派生自Inventory,包含4个私有属性
Vector<String[]> Output Vector<String[]> Receive
Vector<String[]> Delete private Vector<String[]> Add
分别存放从文件中读取的4种交易的信息。
由构造函数从Transaction中读取交易信息,按照首字母存放到各自容器中。
函数 add、receive、output、delete分别用于处理各自容器中的交易信息。
函数handleTransaction用于实现交易功能。该函数按题目中交易顺序调用add、receive、output、delete 4个函数,并最后调用父类函数updateInventory来更新交易后的数据。
具体代码
注意,如果要参考(copy),代码下面的代码一定要看明白,注释中写了很多的提示信息
goods(货物)类
货物类中保存货物的各项信息
package inventory;
//货物的各种属性
public class Goods {
private String Item_number;
private int Quantity;
private String Supplier;
private String Description;
public String getItem_number() {
return Item_number;
}
public void setItem_number(String item_number) {
Item_number = item_number;
}
public int getQuantity() {
return Quantity;
}
public void setQuantity(int quantity) {
Quantity = quantity;
}
public String getSupplier() {
return Supplier;
}
public void setSupplier(String supplier) {
Supplier = supplier;
}
public String getDescription() {
return Description;
}
public void setDescription(String description) {
Description = description;
}
}
Inventory类
该类主要用于从文件中读取货物信息,保存到库存中
package inventory;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
//库存信息
public class Inventory {
protected ArrayList<Goods> inventory = new ArrayList<>();//子类也要用到,故为protected
// 程序在单线程环境下运行,ArrayList是更好的选择,比Vector快。
// 如果程序在多线程环境下运行,并且需要确保数据的线程安全性,那么Vector可能是更好的选择。
public Inventory() {
String line = null;// 暂存从文件中读取的每一段字符
try {
// 读取库存记录存入inventory中
BufferedReader bfreader = new BufferedReader(new FileReader("Inventory.txt"));
while ((line = bfreader.readLine()) != null) {
// 使用正则表达式将一个长字符串分解为各个字符
String[] str = line.split("[^a-zA-Z0-9,]+");
Goods g1 = new Goods();
g1.setItem_number(str[0]);// 第一项为货物编号
g1.setQuantity(Integer.parseInt(str[1]));// 第二项为货物数量
g1.setSupplier(str[2]);// 第三项为供应商
g1.setDescription(str[3]);// 第四项为描述
inventory.add(g1);// 添加到inventory中
}
bfreader.close();// 清空读取器中的缓冲区,用于下次读取
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("Inventory.txt doesn't exist!");
} catch (IOException e) {
e.printStackTrace();
}
}
// 更新库存内容
public void updataInventory() throws IOException {
FileWriter writer = new FileWriter("NewInventory.txt");
for (int i = 0; i < inventory.size(); i++) {
//writer.write(String.format("%10s %10s %10s %20s\n", "Field1", "Field2", "Field3", "Field4"));
//这样控制文字间隔
writer.write(String.format("%10s %10s %10s %20s",
inventory.get(i).getItem_number()+"\t",
inventory.get(i).getQuantity()+"\t",
inventory.get(i).getSupplier()+"\t",
inventory.get(i).getDescription())+"\n");
}
writer.close();
}
// 显示出ArrayList中的内容
public void showInventory() {
for(Goods s:inventory) {
System.out.println(s.getItem_number()+"\t"+s.getQuantity()+"\t"+s.getSupplier()+"\t"+s.getDescription());
}
}
}
Transaction类
处理题目要求的各种交易,主要的代码
package transactions;
import java.io.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Vector;
import inventory.Goods;
import inventory.Inventory;
public class Transaction extends Inventory {
// O(发货订单) 123123(货物编号) 1000(交易货物数量) 9(客户编号)
// R (到货单) 123123 1 (到货数量)
// D (删除某种货物) 1234(删除的货物编号)
// A(添加一种货物) 5(添加的新的货物编号) 4(新增货物数量) Thingy(新货物描述)
// 第一个符号表示交易类型,其余是各种具体交易信息
// 货物编号 客户编号、物品数量、货物描述 在父类(库存)中有
// 存放多个字符串数组,每个字符串数组中存着各类交易信息
private Vector<String[]> Output = new Vector<>();
private Vector<String[]> Receive = new Vector<>();
private Vector<String[]> Delete = new Vector<>();
private Vector<String[]> Add = new Vector<>();
// 构造函数用于从文件中读取各种交易信息
public Transaction() throws IOException {
String line = null;// 用于读取每一行
FileReader reader = null;
try {
reader = new FileReader("Transactions.txt");
} catch (FileNotFoundException e1) {
System.out.println("Transactions Exists Error!");
e1.printStackTrace();
}
BufferedReader bfreader = new BufferedReader(reader);
while ((line = bfreader.readLine()) != null) {
String[] parts = line.split("[^a-zA-Z0-9]+");
switch (parts[0].charAt(0)) {
case 'O':
Output.add(parts);
break;
case 'R':
Receive.add(parts);
break;
case 'D':
Delete.add(parts);
break;
case 'A':
Add.add(parts);
break;
}
}
reader.close();
bfreader.close();// 清空读取器中的缓冲区,用于下次读取
sort_output();// 对output进行排序
}
// 按照数量对output排序,使要输出的数量少的排在前面
public void sort_output() {
Output.sort(Comparator.comparing(output -> Integer.parseInt(output[2])));
}
// 处理交易信息
public void handleTransaction() throws IOException {
this.add();// 新增货物记录总是出现在第一个到货单之前。
this.receive();// 在Transactions.txt中,优先处理到货单
this.output();
this.delete();// 删除操作总是在所有的事物处理之后才被处理
super.updataInventory();// 更新库存
}
// 以'O'开头的事务表示这是一个发货订单,即某一种货物应该发给特定的客户。
// Item number和Quantity的格式如上面表格定义。Custom编号和上面的Supplier编号一致。
// 处理一条定单记录(以'O'开头的事务)意味着从减少库存记录中相应货物的数量(减少的数量=发货单中的数量),
// 记录发货信息到Shipping.txt中。
// 注意:Inventory.txt中的quantity不应该小于0,
// 如果对于某一种货物,库存的数量小于发货单的数量的话,系统应该停止处理发货单,并记录出错信息到Errors.txt。
// 如果对于某一种货物有多个发货单,而且库存总量小于这些发货单的总和的话,
// 系统应该按照发货单中的数量从小到大的有限原则满足客户。
// 也就是说,对于某一种货物如果一个数量Quantity少的发货单没有处理之前,数量Quantity多的发货单永远不会被处理。
// (这种处理原则不受发货单记录在Transactions.txt的先后顺序影响)
// O(发货订单) 123123(货物编号) 1000(交易货物数量) 9(客户编号)
public void output() throws IOException {
BufferedWriter errorwriter = new BufferedWriter(new FileWriter("Errors.txt"));
BufferedWriter shopwriter = new BufferedWriter(new FileWriter("Shopping.txt"));
int index = -1;// 存储库存中对应的要输出的货物的编号
for (int i = 0; i < Output.size(); i++) {
for (int j = 0; j < inventory.size(); j++) {
if (Output.get(i)[1].equals(inventory.get(j).getItem_number())) {// 使输出的货物的编号与库存中的编号对应
index = j;
}
}
if (index == -1) {// 没有对应编号的元素
errorwriter.write("库存中不存在编号为:" + Integer.parseInt(Output.get(i)[1]) + "不存在\n");
} else {
int nowQuantity = inventory.get(index).getQuantity() - Integer.parseInt(Output.get(i)[2]);// 计算实际数量
if (nowQuantity < 0) {
// ErrorWriter.write("编号为 " + Output.get(i)[1] + " 的商品库存不足" + Output.get(i)[2] +
// "件,未发货至编号为 "
// + Output.get(i)[3] + " 的客户!\n");
errorwriter.write(Output.get(i)[3] + "\t" + Output.get(i)[1] + "\t" + Output.get(i)[2]);
} else {
inventory.get(index).setQuantity(nowQuantity);
// 收货编号,货号,数量
shopwriter.write(Output.get(i)[3] + "\t" + Output.get(i)[1] + "\t" + Output.get(i)[2] + "\n");
// shopwriter.write(
// "编号为 " + Integer.parseInt(Output.get(i)[1]) + " 的商品 " + Integer.parseInt(Output.get(i)[2])
// + " 件已发货至编号为 " + Integer.parseInt(Output.get(i)[3]) + " 的客户\n");
}
}
}
shopwriter.close();
errorwriter.close();
cmbItem_number();//将这相同的两条发货信息合并
}
void cmbItem_number() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("Shopping.txt"));
ArrayList<String[]> shop = new ArrayList<>();
// 收货编号,货号,数量
String line;
while ((line = reader.readLine()) != null) {
String[] arr = line.split("\t");
shop.add(arr);
}
reader.close();
// int indexs[]=new int[shop.size()];
for (int i = 0; i < shop.size(); i++) {
for (int j = i + 1; j < shop.size(); j++) {
if (shop.get(i)[1].equals(shop.get(j)[1])) {
shop.get(i)[2] = String.valueOf(Integer.parseInt(shop.get(i)[2])+Integer.parseInt(shop.get(j)[2]));
shop.remove(j);
j--;
}
}
}
BufferedWriter writer = new BufferedWriter(new FileWriter("Shopping.txt"));
for (String arr[] : shop) {
writer.write(arr[0] + "\t" + arr[1] + "\t" + arr[2] + "\n");
}
writer.close();
}
// R (到货单) 123123 1 (到货数量)
// 以'R'开头的事务表示这是一个到货单记录,
// 在'R'后面是Item number和它的数量Quanlity。
// 处理一条到货单意味着增加库存中相应货物的数量(增加的数量=到货单中的数量)。
// 注意:如果在Transactions.txt文件中,到货单出现在发货单之后,到货单中的货物数量可以用来填补发货单中的数量
// (可以理解成在Transactions.txt中,优先处理到货单)。
public void receive() throws IOException {
for (int i = 0; i < Receive.size(); i++) {
int index = -1;// 存储库存中对应的要输出的货物的编号
for (int j = 0; j < inventory.size(); j++) {
if (Receive.get(i)[1].equals(inventory.get(j).getItem_number())) {// 使输出的货物的编号与库存中的编号对应
index = j;
}
}
if (index == -1) {
FileWriter writer = new FileWriter("Errors.txt", true);
writer.write("编号为" + Receive.get(i)[1] + "的货物不存在,请先添加这种货物");
writer.close();
} else {
inventory.get(index)
.setQuantity(inventory.get(index).getQuantity() + Integer.parseInt(Receive.get(i)[2]));
}
}
}
// 以'A'开头的事务表示向库存中增加一种新的货物(即这种货物以前库存中没有)
// 在'A'后面是Item number,供应商supplier以及货物的描述description。
// 处理一个新增货物记录意味着向库存中增加一个数量Quantity为0的新的Item。
// 你可以假设在一个Transactions.txt中,新增货物记录总是出现在第一个到货单之前。
public void add() {
if (Add.isEmpty())
return;// 没有要添加的直接返回
for (int i = 0; i < Add.size(); i++) {
int index = 0;
for (int j = 0; j < inventory.size(); j++) {
// 寻找合适的位置加入
if (Integer.parseInt(inventory.get(j).getItem_number()) > Integer.parseInt(Add.get(i)[1])) {
index = j;
break;
}
}
Goods g = new Goods();
g.setItem_number(Add.get(i)[1]);
g.setQuantity(0);
g.setSupplier(Add.get(i)[2]);
g.setDescription(Add.get(i)[3]);
inventory.add(index, g);
}
}
// D (删除某种货物) 1234(删除的货物编号)
// 以'D'开头的事务表示从库存中删除一种货物,在'D'后面是Item号。
// 删除操作总是在所有的事物处理之后才被处理,以保证对于可能出现的同一种货物的发货单的操作能在删除之前被正确处理。
// 如果要删除的某种货物的库存量Quantity不为0的话,系统应该向Errors.txt记录出错信息。
public void delete() throws IOException {
FileWriter errorWriter = new FileWriter("Errors.txt", true);// 不清除原有的
for (int i = 0; i < Delete.size(); i++) {
int index = -1;
for (int j = 0; j < inventory.size(); j++) {// 查找要删除的编号的下标
if (Delete.get(i)[1].equals(inventory.get(j).getItem_number())) {
index = j;
}
}
if (index == -1) {
errorWriter.write("要删除的编号为 " + Delete.get(i)[1] + " 的货物不存在!\n");
continue;
} else if (inventory.get(index).getQuantity() == 0) {
inventory.remove(index);
} else {
errorWriter.write("要删除的编号为" + Delete.get(index)[1] + "的货物库存不为0!\n");
}
}
errorWriter.close();
}
}
主类
这个不必借鉴,根据自己的需要写
package inventory;
import java.io.IOException;
import transactions.Transaction;
public class Main {
public static void main(String[] args) throws IOException {
Transaction T=new Transaction();
System.out.println("交易前的库存为:");
T.showInventory();
T.handleTransaction();
System.out.println("交易后的库存为:");
T.showInventory();
}
}