阅读 171

node+ts完成课程设计

一、背景

就于前几日,我们数据结构老师发布了课程设计:

问题描述:建立身份证信息管理系统,能够进行身份证信息的录入、查找,保存,要求考虑查找效率,用二叉排序树存储信息。 具体功能有:

(1)能够进行身份证号码及相关信息的录入,相关信息包括姓名、地址和手机号;

(2)能够快速进行身份证号码的查询,并输出相关信息;

(3)可以修改身份证号码对应的其他信息,如姓名、地址;

(4)可以完成身份证信息的删除。

(5)信息保存到数据文件中。

但我一深思,我c语言差不多一个学期没用了,这不得凉凉。

-91dab9a5ab46c26

当我回到寝室开始上号11550153457764

看到熟悉的ts语法,我突发奇想,这能用c来写,为啥就不能用ts来写。

11550095343534

二、途中曲折,一言难尽

//# BST.ts文件 import { IDData, BNode } from "./dataType"; class BST {   public root: BNode = null;   public BNode = class implements BNode {     data: IDData;     left: BNode = null;     right: BNode = null;     constructor(data: IDData) {       this.data = data;     }   }   //向树中插入节点   insert(data: IDData): void {     const newNode = new this.BNode(data);     if (this.root == null) {       this.root = newNode;     } else {       this.insertNode(this.root, newNode)     }   }   //寻找插入节点   private insertNode(root: BNode, node: BNode) {     //向左查找     if (Number(node.data.id) < Number(root.data.id)) {       //如果左子树空直接插入       if (root.left == null) {         root.left = node       } else {         //如果不空继续递归调用         this.insertNode(root.left, node)       }     } else {       if (root.right == null) {         root.right = node       } else {         this.insertNode(root.right, node)       }     }   }   //遍历数据(中序遍历)   inorderTraversal(): IDData[] {     const result: IDData[] = [];     this.preorderTraversalNode(this.root, result);     return result;   }   private preorderTraversalNode(node: BNode, result: IDData[]) {     if (node == null) return result;     this.preorderTraversalNode(node.left, result)     result.push(node.data)     this.preorderTraversalNode(node.right, result)   }   //查找   search(id: IDData['id']): IDData {     let node: BNode = this.root;     while (node) {       if (Number(id) < Number(node.data.id)) {         node = node.left       } else if (Number(id) > Number(node.data.id)) {         node = node.right       } else {         return node.data       }     }   }   //删除   delete(id: IDData['id']): IDData {     let currentNode: BNode = this.root;     let parentNode: BNode = null;     let isLeftChild: boolean = true;     //循环查找要删除的节点 currentNode,已及它的parrentNode,isLeftChild     while (currentNode.data.id != id) {       parentNode = currentNode;       if (Number(id) < Number(currentNode.data.id)) {         isLeftChild = true;         currentNode = currentNode.left;       } else {         isLeftChild = false;         currentNode = currentNode.right;       }       //找到最后没找到返回null       if (currentNode == null) {         return null;       }     }     //1.删除的是叶子节点     if (currentNode.left == null && currentNode.right == null) {       if (currentNode == this.root) {         this.root == null;       } else if (isLeftChild) {         parentNode.left = null;       } else {         parentNode.right = null;       }       //2.删除只有一个子节点的节点     } else if (currentNode.right == null) {//currentNode只存在左节点       //判断是否是根节点       if (currentNode == this.root) {         this.root = currentNode.left;         //判断currentNode是不是父节点的左子叶       } else if (isLeftChild) {         //如果是就让父节点的左子叶重接仅剩是currentNode的子叶         parentNode.left = currentNode.left       } else {         parentNode.right = currentNode.left       }     } else if (currentNode.left == null) {//currentNode只存在由节点       //判断是否是根节点       if (currentNode == this.root) {         this.root = currentNode.right         //判断currentNode是不是父节点的左子叶       } else if (isLeftChild) {         //如果是就让父节点的左子叶重接仅剩是currentNode的子叶         parentNode.left = currentNode.right       } else {         parentNode.right = currentNode.right       }     //3.删除的是两个子节点的情况(后继)     }else{       //1.找到后续节点       let successor=this.getSuccessor(currentNode);       //2.判断是否为根节点       if(currentNode==this.root){         this.root=successor;       }else if(isLeftChild){         parentNode.left=successor;       }else{         parentNode.right=successor;       }       //3.将后续的左节点改为被删除的左节点       successor.left=currentNode.left;     }     return currentNode.data;   }   private getSuccessor(delNode:BNode):BNode{     //保存要找到的后续     let successor:BNode=delNode;     let current:BNode=delNode.right;     let successorParent:BNode=delNode;     while(current!=null){       successorParent=successor;       successor=current;       current=current.left;     }     //判断寻找的后续节点是否直接就是要删除的节点的right     if(successor!=delNode.right){       successorParent.left=successor.right;       successor.right=delNode.right     }     return successor   } } export default BST 复制代码

声明文件

//# dataType.d.ts 文件 export declare enum Sex{   male="男",   female="女" } export declare interface IDData{   id:string,   name:string,   telephone:string,   address:string,   sex:Sex,   age:number } export declare interface BNode{   data:IDData,   left:BNode,   right:BNode } 复制代码

上面是完整的二叉排序树代码,由于刚开始接触数据结构对算法等都还不是很熟悉,

这里参考了

JavaScript数据结构与算法

核心二叉树写好了,随之而来的就是以下几个问题:

1.在哪里运行?

2.如何接收命令行参数?

3.存储在什么格式的数据文件里?

4.怎样存储到数据文件里?

5.怎么提高用户体验?

二、发现问题并解决

1.在哪里运行?

毋庸置疑在node环境中运行,刚好前段时间也自学了一点进程,线程,net等模块。

我单纯就为了实践一下,把这次课程设计当作一份试卷检验一下以前学的知识。

2.如何接收命令行参数?

node官网教程里演示了readLine模块,这是一种不错的方法,我用的inquirer。

3.存储在什么格式的数据文件里?

当时室友在和我回寝室的路上,我室友学的java,我问他,你准备用什么格式的文件保存数据,他说:“txt”。

但我转念一想,txt文本好像并无格式可言,要是能放Excel表格里就好了。

当我回去打开vs code看到了包描述文件package.json时,我觉得可以用json数据保存,json保存的数据里都是键值对,对象和数组,这样我就可以把每一条身份信息放一个对象再存数组里。

4.怎样存储到数据文件里?

这里我用的fs模块一次性读入,写入。

5.怎么提高用户体验?

写个服务器结合vue整个网页?我转念一想,还有20天期末考试了,写完这个还有时间复习吗?

然后还是选择了控制台打印,当我实践过程中发现打印json字符串数据量一多就没法看了,能不能打印表格呢?经过一番搜索,找到了word-table这个包。并决定用chalk尝试过自己做个cli的同学应该都知道,还有一个commander,但我这里并不打算用这个。

以下直接上代码

//# src/CommandeLine/index.ts import inquirer from "inquirer"; import chalk from "chalk"; enum Sex{   male="男",   female="女" } export async function init(){   return await inquirer.prompt([     {       type:'list',       name:'serve',       message:chalk.yellow('请选择服务:'),       choices:['录入身份信息','查询身份信息','查看所有数据','删除指定信息','修改指定信息','保存','退出']     }   ]) } export async function input(){   return await inquirer.prompt([     {       type:'input',       name:'name',       message:'请输入名字',       default:"张三"     },     {       type:'input',       name:'age',       message:'请输入年龄',       default:'18'     },     {       type:'confirm',       name:'sex',       message:'你的性别是男?',       default:'男'     },     {       type:'input',       name:'id',       message:chalk.greenBright('请输入身份证号'),       validate(value){         return !value.length ? new Error(chalk.red('身份证号不能为空')) : true       }     },     {       type:'input',       name:'address',       message:'请输入地址',       default:'未知'     },     {       type:'input',       name:'telephone',       message:'请输入电话号码'     }   ]) } export async function confirm(){   return await inquirer.prompt([{     type:"confirm",     name:"continueOperation",     message:chalk.blue('是否继续?'),     default:true   }]) } export async function search(){   return await inquirer.prompt([     {       type:"input",       name:'id',       message:chalk.greenBright('请输入查询身份证号:'),       validate(value){         return !value.length ? new Error(chalk.red('身份证号不能为空')) : true       }     }   ]) } export async function modify(){   return await inquirer.prompt([     {       type:'input',       name:'name',       message:'请输入名字',       default:"张三"     },     {       type:'input',       name:'age',       message:'请输入年龄',       default:'18'     },     {       type:'confirm',       name:'sex',       message:'你的性别是男?',     },     {       type:'input',       name:'address',       message:'请输入地址',       default:'未知'     },     {       type:'input',       name:'telephone',       message:'请输入电话号码'     }   ]) } export async function signout(){   return await inquirer.prompt([     {       type:'confirm',       message:chalk.green('是否退出'),       name:'logout'     }   ]) } 复制代码

在写的过程中进行模块化时,刚开始用的直接导出,没使用async,await,导致命令行提示问句时与预想不符合,后面尝试了一下回调的方法,但写起来容易造成回调地狱,由于inquirer直接支持promise所以我就写的这种。

简单说明一下:

在main.ts文件中我对operation.ts 和index.ts创建了子进程,operation.ts主要进行对二叉树的操作,index.ts主要是进行数据表格打印。另外我在operation.ts开启了另一个子进程readWrite.ts,

这也是第一次尝试,在子进程中再开一个子进程。readWrite.ts进程主要是对data.json文件读写。

以下是代码:

// # src/child_process/operation.ts import BST from '../BST' import net from 'net' import ChildProcess from 'child_process' import path from 'path' //创建二叉树 const bst = new BST() //必须用绝对路径(在子进程中当前工作文件变了所有相对路径找不到文件) const child=ChildProcess.fork(path.join(__dirname,'readWrite')) //建立tcp通信(客户端) const client = net.createConnection({   port: 8000,   host: 'localhost' }) client.on('connect', () => {   //建立连接后在操作数据   child.on('message',(data:any)=>{     //将json字符串解析成数组     data=JSON.parse(data)     //对数组遍历把每一项插入树中     data.forEach(i => {       bst.insert(i)     });   })   process.on('message', (msg: any) => {     if (msg.type == '查询') {       //console.log(msg.data)       const res1 = bst.search(msg.data)       let res = [];       res.push(res1)       client.write(JSON.stringify(res))     } else if (msg.type == '查询所有') {       //保存中序遍历数据       const res = bst.inorderTraversal()       //向子进程2发送数据打印表格       client.write(JSON.stringify(res))     } else if (msg.type == '录入') {       //向二叉树执行插入       msg.data.forEach(i => {         bst.insert(i)       });     } else if (msg.type == '删除') {       bst.delete(msg.data)     }else if (msg.type == '修改') {       //先删除       bst.delete(msg.data)       //再重新写入       //插入id       msg.IdData.id=msg.data       bst.insert(msg.IdData)     }else if (msg.type == '保存') {       //向子进程发送写入文件信号       let res:any=bst.inorderTraversal()       res=JSON.stringify(res,null,'\t')       child.send(res)     }   }) }) client.on('data', (data) => {   console.log(data.toString()) }) 复制代码

// # src/child_process/readWrite.ts import fs from 'fs' import util from 'util' import path from 'path' const read=util.promisify(fs.readFile); const write=util.promisify(fs.writeFile); async function readFile(path:string){   return await read(path); } async function writeFile(path:string,data:any){   return await write(path,data); } let main=async()=>{   let res=await readFile(path.join(__dirname,'../data.json'))   process.send(res.toString())   process.on('message',async(msg:string)=>{     await write(path.join(__dirname,'../data.json'),msg)   }) } main() 复制代码

// # src/child_process/index.ts import WordTable from 'word-table'; import chalk from 'chalk'; import net from 'net' let header = [   "id",   "name",   "age",   "sex",   "telephone",   "address" ] let body = []; //建立tcp通信(服务端) const server = net.createServer() //监听8000端口 server.listen(8000) server.on('connection', socket => {   //接收数据打印表格   socket.on('data', (data) => {     let msg = JSON.parse(data.toString())     msg.forEach(e => {       let arr = [];       arr.push(e.id)       arr.push(e.name)       arr.push(e.age)       arr.push(e.sex)       arr.push(e.telephone)       arr.push(e.address)       body.push(arr)     });     const wt = new WordTable(header, body)     //console.log(chalk.green('插入数据成功:'))     console.log(chalk.bgGray.yellow(wt.string()))     //重置body为下次打印准备     body=[]   }) }) 复制代码

运行效果:

image-20211207202257467

image-20211207202344207

image-20211207202505408

效果可能一般,但我觉得值,至少是自己肝了两天写好的。

三、总结

就这样我花了两天时间完成了我的课程设计,期间发现问题并解决问题,这是一个痛苦并快乐的事,我也发现了自己的一些问题:

一、typescript写的还不够好,在使用node自带模块时用成了anyscript

二、另外数据结构有待提升

三、在node执行以及node底层还有更多的地方等着去学习

在今年寒假,笔者会努力提升自己,再未来笔者也希望为社区做点贡献。

如果有一天,当你的努力配得上你的梦想,那么,你的梦想也绝对不会辜负你的努力。让自己尽可能变得优秀,当你为一件事情拼命努力的时候,全世界都会帮你!


作者:叶师傅
链接:https://juejin.cn/post/7038932745875095566

 伪原创工具 SEO网站优化  https://www.237it.com/ 


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐