版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
PAGE1Android期末作业院系:计算机与信息工程学院班级:10级软件技术1班姓名:学号:完成日期:2021年1月5日基于android平台的连连看系统本系列文章记录只是为了增加android项目实战经验,将所学的知识用于相应的项目开发当中。首先介绍一下android中连连看项目的架构及所用到的技术进行简要分析,框架基本如下图所示:本程序主要包含两大模块:即(1)表示层模块;(2)后台逻辑模块;其中表示层模块可以理解为游戏的UI及一些游戏辅助效果,表示层模块中,重要的是实现游戏的布局地图,项目中实现中,游戏的布局将使用自定义View的方式,在屏幕上贴图实现。而菜单模块及选关的dialog,只是为用户提供一些常见的选择,如重玩,过关继续,音效开关等等,为了有一个更好的用户交互环境,dialog的实现将通过自定义dialog的方式。而游戏音效是MediaPlayer在不同的状态场景下播放不同的游戏音效。而后台逻辑模块中,即时对于程序计算的实现与程序各种状态的监听,将是整个程序运行的基础。此模块中将实现对于游戏剩余时间限制和游戏状态的监听与处理。对于游戏剩余时间的监听,将开启单独的线程进行处理,从而不至于影响主程序逻辑的运行;游戏的状态的监控处理中,将会实现对于连通的两个图标的消除(即游戏界面的更新),游戏输赢的监听判断,游戏暂停与否等(暂停状态需要同时将剩余时间暂停,而时间监听线程需要知道所处状态,此二者紧密联系)。对于本程序中最重要的还是程序中核心算法模块的实现,在游戏中,最主要的算法是判断两个选中的图标是否能够连通,其余两个算法也依赖于此算法而进行。下面着重介绍一下连接算法:在介绍连接算法之前,先简单介绍一下连连看的布局算法,为了简单起见,我们使用4*4的棋盘,假设棋子有四种:首先在程序初始化时,我们先将要加载的图片在棋盘上按序绘制出来,注意每一种图标我们绘制的时候需要一次性绘制两次,这样,才能保重绘制出来的每种图标的个数都是偶数个。假设最初如下图(1):图(1)最初绘制图(2)调换后棋盘这样绘制后,我们进行一次遍历,随机的调换棋盘中的图标(是现有棋盘中的图标之间的调换,并不是更改成为其他的图标)。经过调换的棋盘可能如图(2)所示这样就完成了棋盘的初始化,当然我们的棋盘在最外面一层中是不添加图标的,为的是我们连线时候能够使用最外层画线,而不会出现穿过图标画线的情况,棋盘如下图:在上一篇文章helloPe的android项目实战之连连看—设计篇中,我们进行了对android中连连看的项目的设计,包括功能模块的划分以及核心算法的设计。此文章接上文对android平台连连看程序进入实现阶段。在此项目中,根据上文中对于功能的分析,我们将实现以下类(下面即是工程的文件目录)在开发中,我们遵循由下向上的方式,也就是说,我们首先开发位于最底层的类,这种类并不依赖于其他的我们需要实现的类。根据上文的分析,首先我们开发在表示层模块中的界面显示类,首先是BoardView类,在android平台下,采用继承自View类的方式,看此类的代码,代码中尽量添加了详细的注释:packagenate.llk.view;/*导入包种种再次略去*/publicclassBoardViewextendsView{protectedstaticfinalintxCount=10;protectedstaticfinalintyCount=12;map连连看游戏棋盘,map中添加的int型在程序中的意思是index,而不是屏幕坐标!protectedint[][]map=newint[xCount][yCount];iconSize图标大小,图标是正方形,所以一个int变量表示即可protectedinticonSize;*iconCounts图标的数目protectedinticonCounts=19;*icons所有的图片protectedBitmap[]icons=newBitmap[iconCounts];*path可以连通点的路径privatePoint[]path=null;*selected选中的图标protectedList<Point>selected=newArrayList<Point>();*@paramcontext*@paramattrspublicBoardView(Contextcontext,AttributeSetattrs){super(context,attrs);calIconSize();Resourcesr=getResources();//载入连连看中的图标资源loadBitmaps(1,r.getDrawable(R.drawable.fruit_01));loadBitmaps(2,r.getDrawable(R.drawable.fruit_02));loadBitmaps(3,r.getDrawable(R.drawable.fruit_03));loadBitmaps(4,r.getDrawable(R.drawable.fruit_04));loadBitmaps(5,r.getDrawable(R.drawable.fruit_05));loadBitmaps(6,r.getDrawable(R.drawable.fruit_06));loadBitmaps(7,r.getDrawable(R.drawable.fruit_07));loadBitmaps(8,r.getDrawable(R.drawable.fruit_08));loadBitmaps(9,r.getDrawable(R.drawable.fruit_09));loadBitmaps(10,r.getDrawable(R.drawable.fruit_10));loadBitmaps(11,r.getDrawable(R.drawable.fruit_11));loadBitmaps(12,r.getDrawable(R.drawable.fruit_12));loadBitmaps(13,r.getDrawable(R.drawable.fruit_13));loadBitmaps(14,r.getDrawable(R.drawable.fruit_14));loadBitmaps(15,r.getDrawable(R.drawable.fruit_15));loadBitmaps(16,r.getDrawable(R.drawable.fruit_17));loadBitmaps(17,r.getDrawable(R.drawable.fruit_18));loadBitmaps(18,r.getDrawable(R.drawable.fruit_19));privatevoidcalIconSize(){//取得屏幕的大小DisplayMetricsdm=newDisplayMetrics();((Activity)this.getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);iconSize=dm.widthPixels/(xCount);*函数目的在于载入图标资源,同时将一个key(特定的整数标识)与一个图标进行绑定*@paramkey特定图标的标识*@paramddrawable下的资源publicvoidloadBitmaps(intkey,Drawabled){Bitmapbitmap=Bitmap.createBitmap(iconSize,iconSize,Bitmap.Config.ARGB_8888);Canvascanvas=newCanvas(bitmap);d.setBounds(0,0,iconSize,iconSize);d.draw(canvas);icons[key]=bitmap;//未用0号index*View自带的,但是在此方法中,有画路径(删除联通的两个图标),*绘制棋盘的所有图标(也可理解为刷新,只要此map位置值>0)*放大第一个选中的图标(selected.size()==1)@OverrideprotectedvoidonDraw(Canvascanvas){if(path!=null&&path.length>=2){for(inti=0;i<path.length-1;++i){Paintpaint=newPaint();paint.setColor(Color.BLUE);paint.setStrokeWidth(3);paint.setStyle(Paint.Style.STROKE);Pointp1=indexToScreen(path[i].x,path[i].y);Pointp2=indexToScreen(path[i+1].x,path[i+1].y);canvas.drawLine(p1.x+iconSize/2,p1.y+iconSize/2,p2.x+iconSize/2,p2.y+iconSize/2,paint);map[path[0].x][path[0].y]=0;map[path[path.length-1].x][path[path.length-1].y]=0;selected.clear();path=null;*绘制棋盘的所有图标当这个坐标内的值大于0时绘制for(intx=1;x<xCount-1;++x){for(inty=1;y<yCount-1;++y){if(map[x][y]>0){Pointp=indexToScreen(x,y);canvas.drawBitmap(icons[map[x][y]],p.x,p.y,null);*绘制选中图标,当选中时图标放大显示//for(Pointposition:selected){if(selected.size()>0){Pointposition=selected.get(0);Pointp=indexToScreen(position.x,position.y);if(map[position.x][position.y]>=1){canvas.drawBitmap(icons[map[position.x][position.y]],null,newRect(p.x-5,p.y-5,p.x+iconSize+5,p.y+iconSize+5),null);super.onDraw(canvas);*@paramx数组中的横坐标*@paramy数组中的纵坐标*@return将图标在数组中的坐标转成在屏幕上的真实坐标publicPointindexToScreen(intx,inty){returnnewPoint(x*iconSize,y*iconSize);*@paramx屏幕中的横坐标*@paramy屏幕中的纵坐标*@return将图标在屏幕中的坐标转成在数组上的虚拟坐标publicPointscreenToIndex(intx,inty){intxindex=x/iconSize;intyindex=y/iconSize;if(xindex<xCount&&yindex<yCount){returnnewPoint(xindex,yindex);}else{returnnewPoint(0,0);*传进来path数据更新显示,也就是将能够连接的图标消除*@parampathpublicvoiddrawLine(Point[]path){this.path=path;this.invalidate();此类当中,主要是实现了将连连看图标资源的载入并且使之与一个特定的int型key相绑定,所以在后面的对于图标的贴图,我们能够更加方便的操作。当然此类中还需要存在一些必要的工具函数,比如说screenToIndex方法等,因为我们是自定义View在屏幕上绘图,需要用到屏幕坐标,但是同时,连连看游戏中,我们还需要知道图标的索引(由于图标都是等长等宽,容易实现屏幕坐标与index索引之间的转换),以使方便操作。当然,此类中最重要的还是重写的onDraw函数;此函数中首先判断path是否为null并且是否两个及以上的元素,我们之前定义path变量时,是将其作为保存连通路径的工具。(path中的值也就是连通路径我们将在连接算法实现时中加入)这里我们首先在onDraw函数中绘制出线条(如果连通),随后将路径的首尾中的map值设为0,程序中,第0行与最后一行map值始终为0,第0列与最后一列map值始终为0,map中的值0为0代表此处已经没有了图标,根据前面与图标资源的绑定值与map中的值对应,map中的值为几则在相应的index上贴上相应的图标。在onDraw函数中,还有一个功能就是将选择的第一个图标放大,以提醒玩家。最后绘制(贴图),如前面所说,map值为多少就在对应位置贴上相应的图标资源,有前面载入资源时可知并没有对应于0的图标资源,为0时即不贴图。为了防止代码混乱,上面的BoardView类并没有实现全部的功能,如touch事件的监听,连接算法的实现,判断是否无解等等。所以我们将BoardView类进行扩展,继承BoardView的GameView(这样做也使代码不至于太混乱)。限于篇幅,我们可以先将GameView中用于监听剩余时间的内部类实现(该类实现了Runnable接口):*用于更新剩余时间的线程*@authorhelloPeclassRefreshTimeimplementsRunnable{@Overridepublicvoidrun(){if(isContinue){while(leftTime>0&&!isStop){timerListener.onTimer(leftTime);leftTime--;try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();if(isStop&&leftTime>0){if(win());//setMode(WIN);elsesetMode(PAUSE);//setMode(LOSE);elseif(leftTime==0){setMode(LOSE);publicvoidstopTimer(){isStop=true;isContinue=false;publicvoidsetContinue(){isContinue=true;isStop=false;refreshTime=newRefreshTime();Threadt=newThread(refreshTime);//注意正确启动一个实现Runnable接口的线程t.start();上面已经提过,此线程用于控制游戏的时间。在此,再介绍自定义的几个接口,publicinterfaceOnStateListener{publicvoidOnStateChanged(intStateMode);}只含有一个方法,主要对于游戏状态的变换的监听,比如pause,stop等等。publicinterfaceOnTimerListener{publicvoidonTimer(intleftTime);}用于监听剩余时间,与上面线程不同的是,此方法中利用上面线程的leftTime的结果,主要用于更新游戏中用于提醒玩家的时间进度条。publicinterfaceOnToolsChangeListener{publicvoidonRefreshChanged(intcount);publicvoidonTipChanged(intcount);}tool即是我们的游戏中提供给玩家的两个工具,一个是refresh一下游戏界面,即将现有的棋盘重新打乱(当然,现有图表数量不变),另一个是之前提过的hint的自动帮助功能,帮助玩家找到一组能够连通的图标。当然,这两种工具都有次数的限制。文接上回,之前介绍了项目的架构,进行了功能的分析,同时进行了BoardView类及时间控制类的开发及几个几口的介绍。这次我们将完整的实现游戏棋盘的绘制与touch事件的处理,以及游戏核心算法中连接算法、hint自动帮助算法与判断是否无解算法的实现。这些代码的处理都在继承自BoardView类的GameView类中。首先在GameView类中添加实现本游戏主要算法的代码,即连接算法的代码(用于判断给定的两个位置的图标能够相连通):*本游戏的核心算法,判断两个连接点是否能够连接,这里传进来的就是我们点击的两个点转化成index的值List<Point>p1Expand=newArrayList<Point>();List<Point>p2Expand=newArrayList<Point>();publicbooleanlink(Pointp1,Pointp2){if(p1.equals(p2)){returnfalse;}path.clear();if(map[p1.x][p1.y]==map[p2.x][p2.y]){if(linkDirect(p1,p2)){path.add(p1);path.add(p2);returntrue;*一个拐点的判断Pointpx=newPoint(p1.x,p2.y);//假设第一种可能点if(map[p1.x][p2.y]==0&&linkDirect(p1,px)&&linkDirect(px,p2)){path.add(p1);path.add(px);path.add(p2);returntrue;}Pointpy=newPoint(p2.x,p1.y);//假设第二种可能点if(map[p2.x][p1.y]==0&&linkDirect(p1,py)&&linkDirect(py,p2)){//首先判断map[p2.x][p1.y]中介点是否有图标path.add(p1);path.add(py);path.add(p2);returntrue;*两个折点(corner)expandX(p1,p1Expand);expandX(p2,p2Expand);for(inti=0;i<p1Expand.size();i++)for(intj=0;j<p2Expand.size();j++){if(p1Expand.get(i).x==p2Expand.get(j).x){if(linkDirect(p1Expand.get(i),p2Expand.get(j))){path.add(p1);path.add(p1Expand.get(i));path.add(p2Expand.get(j));path.add(p2);returntrue;}}}expandY(p1,p1Expand);expandY(p2,p2Expand);for(Pointexp1:p1Expand)for(Pointexp2:p2Expand){if(exp1.y==exp2.y){if(linkDirect(exp1,exp2)){path.add(p1);path.add(exp1);path.add(exp2);path.add(p2);returntrue;}}}returnfalse;//最后三种方式都不能连通,还是要returnfalse,不然在两个同样的图标下却没有返回值!}returnfalse;}*判断直线链接,无拐角,传进来的点值是ScreenToIndex过的了,不过这里传进来的不一定就是我们点击的点,也可能是我们的拐角点(辅助点)*@paramp1*@paramp2publicbooleanlinkDirect(Pointp1,Pointp2){//if(map[p1.x][p1.y]==map[p2.x][p2.y]){//纵向直线if(p1.x==p2.x){inty1=Math.min(p1.y,p2.y);inty2=Math.max(p1.y,p2.y);booleanflag=true;for(inty=y1+1;y<y2;y++){//这个循环里容易漏掉两个相邻的情况,所以才加上上面的flag样式if(map[p1.x][y]!=0){flag=false;break;}}if(flag){returntrue;}}//横直线判断if(p1.y==p2.y){intx1=Math.min(p1.x,p2.x);intx2=Math.max(p1.x,p2.x);booleanflag=true;for(intx=x1+1;x<x2;x++){if(map[x][p1.y]!=0){flag=false;break;}}if(flag){returntrue;}}}returnfalse;}publicvoidexpandX(Pointp,List<Point>list){list.clear();for(intx=p.x+1;x<xCount;x++){//注意此时可以等于xCount-1if(map[x][p.y]!=0)break;list.add(newPoint(x,p.y));}for(intx=p.x-1;x>=0;x--){if(map[x][p.y]!=0)break;list.add(newPoint(x,p.y));}}*向Y方向扩展,传进来的点是index过的,而list是作为“返回值”需要保存的值publicvoidexpandY(Pointp,List<Point>list){list.clear();for(inty=p.y+1;y<yCount;y++){if(map[p.x][y]!=0)break;list.add(newPoint(p.x,y));}for(inty=p.y-1;y>=0;y--){if(map[p.x][y]!=0)break;list.add(newPoint(p.x,y));}}代码中尽量添加注释,此段代码中实现了第一篇文章中进行的算法分析,其中link(Pointp1,Pointp2)函数作为算法真正的完整实现者,算法的主逻辑有它实现,linkDirect(Pointp1,Pointp2)函数作为一个工具函数,用于判断给定的两个位置(注意不是两个图标,因为给定的位置不一定含有图标,当我们在判断”一折型“和“二折型”的情况的时候即使如此)。而expandX(Pointp,List<Point>list)与expandY(Pointp,List<Point>list)两个方法的同样作为工具函数,在判断“二折型”情况时候将会使用,也就是前面所说的“横向扫描”与“纵横扫描”。而对于link(Pointp1,Pointp2)函数中,我们的逻辑还是将大问题化为小问题处理。最终还是分解到调用linkDirect(Pointp1,Pointp2)函数来进行“直线型”的处理。以上即是程序的连接算法的实现,除了程序算法逻辑的理解之外,还需注意在判断的时候,若能够连通,我们已经将privateList<Point>path=newArrayList<Point>();保存连通路径的path附上值,记得当link函数返回true时,path中即保存了一条相通的路径!完成了连接算法,下一步我们将依赖于连接算法的实现,完成扫描是否当前地图已经出现无解的情况,因为程序的地图是随机生成的,难免有时候会出现无解的情况;下面我们将实现判断是否处于无解状态,实现函数:*用于判断是否当前已经无解publicbooleandie(){for(inty=1;y<yCount;y++)//表示从此行中的一个元素开始扫描(起点)for(intx=1;x<xCount;x++){//表示此行中指定列,组成扫描起点if(map[x][y]!=0){for(intj=y;j<yCount;j++){//表示正在被扫描的行if(j==y){//循环中的第一次扫描,为什么特殊?因为此时不一定从一行中的第一个元素开始扫描for(inti=x+1;i<xCount-1;i++){if(map[x][y]==map[i][j]&&link(newPoint(x,y),newPoint(i,j))){returnfalse;}}}else{for(inti=1;i<xCount-1;i++){if(map[x][y]==map[i][j]&&link(newPoint(x,y),newPoint(i,j)))returnfalse;}}}returntrue;代码中也有相应注释,每一次判断相当于一次遍历棋盘,同时注意,如果die()函数返回为false,这则证明link()函数返回了true!前面已经提醒过:当link返回true时,我们用于保存连通路径的path对象中已经保存了一条连通路径的点的集合,只不过在die()函数中运行得到的是按遍历顺序而来的,并不是我们所指定的两个始点与终点两个图标;所以在这儿,可以借die()的判断,完成我们算法实现的第三个功能,即hint的自动帮助?*当点击help按钮时候调用,会帮助玩家消除一对图标publicvoidautoHelp(){if(help==0){//soundPlay.play(ID_SOUND_ERROR,0);return;}else{//soundPlay.play(ID_SOUND_TIP,0);help--;toolsChangedListener.onTipChanged(help);drawLine(path.toArray(newPoint[]{}));refreshHandler.sendRefresh(500);}}当然此处需要介绍一下最后一行代码的来历:classRefreshHandlerextendsHandler{@OverridepublicvoidhandleMessage(Messagemsg){super.handleMessage(msg);if(msg.what==REFRESH_VIEW){GameView.this.invalidate();if(win()){setMode(WIN);isStop=true;isContinue=false;}elseif(die()){//调用一次die方法!此时如果die返回为false,即还能够连通change();}}}publicvoidsendRefresh(intdelayTime){Messagemsg=newMessage();this.removeMessages(0);msg.what=REFRESH_VIEW;this.sendMessageDelayed(msg,delayTime);}}当然对于是否已经为赢了的判断win()函数比较简单,就是扫描棋盘,如果所有位置map值都为了0,即赢了,若不是,还未完成;这里就不贴代码了。GameView类中还有一个职能就是初始化一张棋盘:我们初始化棋盘时,利用前面讲解的初始算法技术,遍历棋盘,先将棋盘填满,但是填满首先还有一个规则就是每一种图标的填入必须同时填入两张,是为每种图标都为偶数个而设定!介绍一下最后调用的change()函数,也是出自于第一篇的棋盘初始算法,用于随机将棋盘中的图标打乱:随机将现有的布局打乱,重新布局,map中现有图标数量不变,相当于一次refreshpublicvoidchange(){Randomrandom=newRandom();inttmp,xtmp,ytmp;for(intx=1;x<xCount-1;x++){for(inty=1;y<yCount-1;y++){xtmp=1+random.nextInt(xCount-2);ytmp=1+random.nextInt(yCount-2);tmp=map[x][y];map[x][y]=map[xtmp][ytmp];map[xtmp][ytmp]=tmp;}}if(die()){//如出现无解情况,即需要再次随机重新打乱change();}}GameView类还是一个View,在此类中我们还要重写View的onTouchEvent方法:*对于选择的处理,如果是第一次按下,则将其加入到selected当中,*若是第二次(selected.size()==1),则先判断能不能连通@OverridepublicbooleanonTouchEvent(MotionEventevent){intsx=(int)event.getX();intsy=(int)event.getY();Pointp=screenToIndex(sx,sy);if(map[p.x][p.y]!=0){if(selected.size()==1){if(link(selected.get(0),p)){//能够连通,path中的数据是在link判断时如果返回真,方法内部就已经将数据添加进去selected.add(p);drawLine(path.toArray(newPoint[]{}));refreshHandler.sendRefresh(500);}else{//不能够连通selected.clear();selected.add(p);}else{//此时的selected中的size只能等于0selected.add(p);GameView.this.invalidate();}}returnsuper.onTouchEvent(event);}方法中用到的selected是BoardView中的protectedList<Point>selected=newArrayList<Point>();代码中对于功能及实现有相应的注释。到此我们可以提供接口startGame以供在程序的activity中调用:publicvoidstartPlay(){help=3;refresh=3;isContinue=true;isStop=false;toolsChangedListener.onRefreshChanged(refresh);toolsChangedListener.onTipChanged(help);leftTime=totalTime;initMap();refreshTime=newRefreshTime();Threadt=newThread(refreshTime);//注意正确启动一个实现Runnable接口的线程类t.start();GameView.this.invalidate();}注意GameView中并没有实现相关的自定义的接口,而是我们将会在程序的activity中实现项目中涉及的三个接口,但是,我们可以在GameView中进行注册:publicvoidsetOnTimerListener(OnTimerListeneronTimerListener){this.timerListener=onTimerListener;}PublicvoidsetOnToolsChangedListener(OnToolsChangeListenertoolsChangeListener){this.toolsChangedListener=toolsChangeListener;}publicvoidsetOnStateChangeListener(OnStateListenerstateListener){this.stateListener=stateListener;}在程序中调用GameView相关函数进行初始化注册。根据多态性的原理,在GameView中调用的相关接口中的函数,也就是activity中实现的接口。
中南民族大学学生课程设计报告课程名称:C程序设计语言选题名称:通讯录管理年级:专业:信息管理与信息系统学号:姓名:指导教师:完成地点:管理学院综合实验室完成日期:1.课程设计的目的为了熟练掌握C语言的语法特点、及其数据结构,提高自身编写程序的能力。通过课程设计实现理论知识与实际情况的结合,使所学知识能够在现实社会中起到一定的作用,防止所编写的程序脱离实际,让程序可以更好的发挥作用,以便减轻实际工作中所遇到的繁琐步骤,提高现实工作中的效率。此外通过课程设计提高逻辑思考能力和解决实际问题的能力。程序设计是公认的、最能直接有效地训练学生的创新思维,培养分析问题、解决问题能力的学科之一。其次课程设计有利于治学态度的培养。程序设计中,语句的语法和常量变量的定义都有严格的要求,有时输了一个中文标点、打错了一个字母,编译就不通过,程序无法正常运行。因此,程序设计初学阶段,学生经常会犯这样的错误,可能要通过几次乃至十多次的反复修改、调试,才能成功,但这种现象会随着学习的深入而慢慢改观。这当中就有一个严谨治学、一丝不苟的科学精神的培养,又有一个不怕失败、百折不挠品格的锻炼。通讯录信息管理系统是生活中不可缺少的部分编写了一个通讯录信息管理系统是十分必要的。本程序设计具有通讯信息的录入,进行保存、查找、删除等功能,操作界面简洁美观,易于操作。程序用了条件、循环、指针、结构体等知识点,综合了文件的打开和保存编写的。可运用于小的管理软件。软件运用菜单实现交互式管理,用户轻松的按键既可实现对软件的操作,操作简单易懂,功能丰富,可以很好的满足的需要。通过对通讯录管理系统的设计,进一步理解和掌握C语言这门课程的知识点,能够熟练的调用各种函数,把各种C语句有机的结合起来,提高自己C语言程序设计的能力,为今后解决实际问题打下良好基础。2.设计方案论证2.1设计思路建立一个函数,用来添加、显示、删除、查询通讯录等信息,完成通讯录的功能。主函数可以调用六个子函数,分别完成添加记录、显示记录、删除记录、查询记录、退出系统等功能。在主函数中可以以1、2、3、4、0数字键分别可以执行某个功能模块。退出程序删除记录查询记录显示退出程序删除记录查询记录显示记录添加记录通讯录管理系统 2.2程序设计2.2.1根据图1定义数据类型建立函数typedefstruct { charscore;/*编号*/ charname[10];/*姓名*/ charnum[15];/*号码*/ charemail[20]; /*邮箱*/ charage[8]; /*年龄*/ charadds[20]; /*住址*/ }Person;2.2.2主函数及其流程图Y定义主函数main()和一系列的功能函数,只有通过这些函数才可实现程序的功能。每次使用都会调用主函数。主函数主要是利用switch语句对数据进行处理,流程图如下:Y添加记录选择1开始N选择2YYYYNNNN退出系统查询记录删除记录显示记录选择0选择4选择3添加记录选择1开始N选择2YYYYNNNN退出系统查询记录删除记录显示记录选择0选择4选择3结束结束2.2.3增加函数及其流程图.添加通讯录记录流程图:.显示通讯录记录流程图:.删除通讯录记录流程图:.查询通讯录记录流程图:2.3源程序#include<stdio.h>#include<stdlib.h>#include<string.h>typedefstruct { charscore;/*编号*/ charname[10];/*姓名*/ charnum[15];/*号码*/ charemail[20]; /*邮箱*/ charage[8]; /*年龄*/ charadds[20]; /*住址*/ }Person;Personpe[80];intmenu_select() { charc; do{ system("cls"); printf("\t\t*****通讯录*****\n"); printf("\t\t┌───────┐\n"); printf("\t\t│1.添加记录│\n"); printf("\t\t│2.显示记录│\n"); printf("\t\t│3.删除记录│\n"); printf("\t\t│4.查询记录│\n"); printf("\t\t│0.退出程序│\n"); printf("\t\t└───────┘\n"); printf("\t\t请您选择(0-4):"); c=getchar(); }while(c<'0'||c>'4'); return(c-'0'); }intInput(Personper[],intn){ inti=0; charsign,x[10]; while(sign!='n'&&sign!='N') { printf("\t编号:"); scanf("\t%d",&per[n+i].score); printf("\t姓名:"); scanf("\t%s",per[n+i].name); printf("\t年龄:"); scanf("\t%s",per[n+i].age); printf("\t电话号码:"); scanf("\t%s",per[n+i].num); printf("\t通讯住址:"); scanf("\t%s",per[n+i].adds); printf("\t电子邮箱:"); scanf("\t%s",per[n+i].email); gets(x); printf("\n\t是否继续添加?(Y/N)"); scanf("\t%c",&sign); i++; } return(n+i);}voidDisplay(Personper[],intn){ inti; printf("\n");/*格式*/ printf("编号姓名年龄电话号码通讯地址电子邮箱\n"); printf("\n"); for(i=1;i<n+1;i++) { printf("%-5d%-8s%-6s%-13s%-15s%-15s\n",per[i-1].score,per[i-1].name,per[i-1].age,per[i-1].num,per[i-1].adds,per[i-1].email); if(i>1&&i%10==0) { printf("\t\n"); printf("\t"); system("pause"); printf("\t\n"); } } printf("\n"); system("pause");}intDelete_a_record(Personper[],intn){ chars[20]; inti=0,j; printf("\t请输入想删除记录中的名字:"); scanf("%s",s); while(strcmp(per[i].name,s)!=0&&i<n)i++; if(i==n) { printf("\t通讯录中没有此人!\n"); return(n); } for(j=i;j<n-1;j++) { strcpy(per[j].num,per[j+1].num); strcpy(per[j].name,per[j+1].name); strcpy(per[j].age,per[j+1].age); strcpy(per[j].adds,per[j+1].adds); strcpy(per[j].email,per[j+1].email); per[j].score=per[j+1].score; } printf("\t\t\t已经成功删除!\n"); return(n-1);}voidQuery_a_record(Personper[],intn){ intm; printf("\t\n请选择查询方式:\n");printf("\t┌──────┐\n");printf("\t│1姓名│\n");printf("\t│2电话│\n");printf("\t│3地址│\n");printf("\t│4返回│\n");printf("\t└──────┘\n");printf("请选择:");scanf("%d",&m);while(m!=1&&m!=2&&m!=3&&m!=4){ printf("输入错误,请重新选择:"); scanf("%d",&m); } if(m==1) { chars[20]; inti=0; printf("\t请输入想查询的姓名:"); scanf("\t%s",s); while(strcmp(per[i].name,s)!=0&&i<n)i++; if(i==n) { printf("\t通讯录中没有此人!\n"); return; } printf("\t此人编号:%d\n",per[i].score); printf("\t此人年龄:%s\n",per[i].age); printf("\t电话号码:%s\n",per[i].num); printf("\t通讯地址:%s\n",per[i].adds); printf("\t电子邮箱:%s\n",per[i].email); }; if(m==2) { chars[20]; inti=0; printf("\t请输入想查询的电话:"); scanf("\t%s",s); while(strcmp(per[i].num,s)!=0&&i<n)i++; if(i==n) { printf("\t通讯录中没有此人!\n"); return; } printf("\t此人编号:%d\n",per[i].score); printf("\t此人姓名:%s\n",per[i].name); printf("\t此人年龄:%s\n",per[i].age); printf("\t通讯地址:%s\n",per[i].adds); printf("\t电子邮箱:%s\n",per[i].email); }; if(m==3) { chars[20]; inti=0; printf("\t请输入想查询的地址:"); scanf("\t%s",s); while(strcmp(per[i].adds,s)!=0&&i<n)i++; if(i==n) { printf("\t通讯录中没有此人!\n"); return; } printf("\t此人编号:%d\n",per[i].score); printf("\t此人姓名:%s\n",per[i].name); printf("\t此人年龄:%s\n",per[i].age); printf("\t电话号码:%s\n",per[i].num); printf("\t电子邮箱:%s\n",per[i].email); };}voidChange(Personper[],intn){ chars[20]; inti=0; printf("\t请输入想修改的记录中的名字:"); scanf("%s",s); while(strcmp(per[i].name,s)!=0&&i<n)i++; if(i==n) { printf("\t通讯录中没有此人!\n"); return; } printf("\t编号:"); scanf("\t%d",&per[i].score); printf("\t姓名:"); scanf("\t%s",per[i].name); printf("\t年龄:"); scanf("\t%s",per[i].age); printf("\t电话号码:"); scanf("\t%s",per[i].num); printf("\t通讯住址:"); scanf("\t%s",per[i].adds); printf("\t电子邮箱:"); scanf("\t%s",per[i].email); printf("\t修改成功!");}voidWritetoText(Personper[],intn){ inti=0; FILE*fp;/*定义文件指针*/ charfilename[20];/*定义文件名*/ printf("\t保存到文件\n");/*输入文件名*/ printf("\t请输入所保存的文件名:"); scanf("\t%s",filename); if((fp=fopen(filename,"w"))==NULL) { printf("\t无法打开文件\n"); system("pause"); return; } fprintf(fp,"******************************************通讯录******************************************\n"); fprintf(fp,"编号姓名年龄电话号码
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 养猪场建设施工合同
- 办公室管理革命:6S管理技巧
- 融资市场动态分析报告
- 健身房财务部长聘用合同
- 2024年设计合同标的和服务内容
- 2025年度消防安全设施设备采购与安装合同3篇
- 集市客户服务集贸市场管理办法
- 信息技术主管聘用合同
- 娱乐设施屋面防水工程合同
- 医疗行业风险控制手册
- GB/T 21099.2-2024企业系统中的设备和集成过程控制用功能块(FB)和电子设备描述语言(EDDL)第2部分:FB概念规范
- 主持人培训课件
- 内蒙古包头市青山区2023-2024学年七年级上学期期末调研检测数学试卷(含解析)
- 期末模拟练习(试题)(含答案)-2024-2025学年三年级上册数学西师大版
- 2024-2025学年语文二年级上册统编版期末测试卷(含答案)
- 足内翻的治疗
- 音乐表演生涯发展展示
- 2024年黑龙江农业工程职业学院单招职业适应性测试题库
- 企业法律顾问详细流程
- 国际能源署IEA:2030年中国的电力系统灵活性需求报告(英文版)
- 2024年世界职业院校技能大赛高职组“关务实务组”赛项参考试题库(含答案)
评论
0/150
提交评论