首页
动态
归档
Github
留言
工具箱
更多
邻居
壁纸
音乐
Search
1
欢迎访问我的日志空间
8,244 阅读
2
C语言读写程序文件-学习三十二
742 阅读
3
WEB音乐播放器源码
712 阅读
4
Typecho - Joe主题魔改版(持续更新)
660 阅读
5
Typecho-1.1/17.10.30-定制版源码
639 阅读
学习笔记
源码库
BUG
第九艺术
登录
Search
标签搜索
学习笔记
C语言
typecho
Java
扫雷
源码
PHP
插件
网站源码
音乐播放器
模板
git
github
bug
XG.孤梦
累计撰写
50
篇文章
累计收到
44
条评论
首页
栏目
学习笔记
源码库
BUG
第九艺术
页面
动态
归档
Github
留言
工具箱
邻居
壁纸
音乐
搜索到
39
篇与
的结果
2022-05-18
Java实现扫雷小游戏一
实现项目结构分析主界面主窗体(com.main/MainFrame.java)包含菜单栏、计数区panel和雷区panel。(1)构造方法public MainFrame() { init(); this.setIconImage(Tools.getImageIcon().getImage()); // 设置图标 this.setTitle("扫雷"); // 设置标题 this.setSize(new Dimension(220,300)); // 窗口大小 this.setResizable(false); // 这样让窗口不可放大 this.setLocationRelativeTo(null); this.setLocation(new Point(800,300)); // 设置窗口位置 this.setVisible(true); // 设置窗口显示 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口 }(2)初始化方法 private void init() { // 菜单栏 this.setJMenuBar(menuBar); }菜单栏(com.panel/BombJMenuBar.java)(1)添加菜单、菜单项等public class BombJMenuBar extends JMenuBar { // 菜单项 JMenu menuGame = new JMenu("游戏(G)"); JMenuItem menuItemStart = new JMenuItem("开局(N)"); JMenuItem menuItemLow = new JMenuItem("初级(B)"); JMenuItem menuItemMid = new JMenuItem("中级(I)"); JMenuItem menuItemHigh = new JMenuItem("高级(E)"); JMenu menuHero = new JMenu("英雄榜(T)"); JMenuItem menuHeroLow = new JMenuItem("初级英雄榜"); JMenuItem menuHeroMid = new JMenuItem("中级英雄榜"); JMenuItem menuHeroHigh = new JMenuItem("高级英雄榜"); JMenuItem menuItemOrder = new JMenuItem("自定义(C)"); JMenuItem menuItemExit = new JMenuItem("退出(X)"); JMenu menuHelp = new JMenu("帮助(H)"); JMenuItem menuItemAbout = new JMenuItem("关于扫雷(A)"); JMenuItem menuItemHole = new JMenuItem("外挂(W)"); // 需要在类中声明主窗体变量(属性):作为菜单栏与主窗体交互的媒介 // 声明主窗体对象 MainFrame mainframe; // 编写构造函数和初始化界面方面 public BombJMenuBar(MainFrame mainframe) { this.mainframe = mainframe; init(); }(2)初始化方法 public void init() { // 设置快捷键 menuGame.setMnemonic('G'); // Alt + G menuItemStart.setMnemonic('N'); menuItemLow.setMnemonic('B'); menuItemMid.setMnemonic('I'); menuItemHigh.setMnemonic('E'); menuHero.setMnemonic('T'); menuItemOrder.setMnemonic('C'); menuItemExit.setMnemonic('X'); menuHelp.setMnemonic('H'); menuItemAbout.setMnemonic('A'); menuItemHole.setMnemonic('W'); // 把菜单添加到菜单栏 // 游戏菜单 this.add(menuGame); menuGame.add(menuItemStart); menuGame.addSeparator(); // 添加分隔线 menuGame.add(menuItemLow); menuGame.add(menuItemMid); menuGame.add(menuItemHigh); menuGame.addSeparator(); menuGame.add(menuHero); menuHero.add(menuHeroLow); menuHero.add(menuHeroMid); menuHero.add(menuHeroHigh); menuGame.addSeparator(); menuGame.add(menuItemOrder); menuGame.addSeparator(); menuGame.add(menuItemExit); // 帮助菜单 this.add(menuHelp); menuHelp.add(menuItemAbout); menuHelp.addSeparator(); menuHelp.add(menuItemHole); }运行雷区(com.panel/BombJPanel)雷区是由小方格组成,在众多小方格中随机布雷。(先设计MineLable)(1)MineLablepublic class MineLabel extends JLabel { private boolean mineTag = false; // 判断是否是雷 private boolean expendTag = false; // 判断雷块是否展开 private boolean flagTag = false; // 判断雷块是否插了旗子 private int rowx; // 雷块所在的行 private int coly; // 雷块所在的列 private int countAround; // 计算雷块周围的雷数 private int rightClickCount; // 记录右键点击次数 }(2)编写构造方法public class BombJPanel extends JPanel { // 定义属性 MineLabel[][] labels = new MineLabel[9][9]; // 声明主窗体对象 MainFrame mainframe; MouListener listener; public BombJPanel(MainFrame mainframe) { this.mainframe = mainframe; //定义布局方式,网格布局 this.setLayout(new GridLayout(Tools.rows, Tools.cols)); listener = new MouListener(labels,mainframe); init(); }程序写到这里很多地方都需要要到图片以及雷区行列数等,故定义工具类定义好一些可能在程序中多次用到且后期会改变的资源或者变量。见工具类Tools(3)初始化private void init() { // 实例化小方格 for(int i = 0;i<labels.length;i++) { for(int j = 0; j<labels[i].length;j++) { labels[i][j] = new MineLabel(i, j); labels[i][j].setIcon(Tools.blank); this.add(labels[i][j]); labels[i][j].addMouseListener(listener); } }创建一个合成边框,指定了用于外部和内部边缘的 border 对象在init() 里插入 // 实现边框效果 Border lowerBorder = BorderFactory.createLoweredBevelBorder(); Border emptyBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5); //边框大小 CompoundBorder compoundBorder = BorderFactory.createCompoundBorder(emptyBorder, lowerBorder); this.setBorder(compoundBorder); this.setBackground(Color.LIGHT_GRAY);createLoweredBevelBorder();创建一个具有凹入斜面边缘的边框,将组件当前背景色的较亮的色度用于高亮显示,较暗的色度用于阴影。(在凹入边框中,阴影位于顶部,高亮显示位于其下。)BorderFactory.createEmptyBorder(5, 5, 5, 5);创建占用空间但不绘制的空边框,指定顶部、左侧、底部和右侧的宽度。运行在MainFrame的构造函数中添加 this.pack(); public MainFrame() { init(); ...... this.pack(); // 使控件更紧凑,窗口自动适应大小 ...... }运行计数区(com.panel/FaceJPanel)(1)定义雷的数量与时间显示public class FaceJPanel extends JPanel { // 雷数 private JLabel labelCountG = new JLabel(); // 个位 private JLabel labelCountS = new JLabel(); // 十位 private JLabel labelCountB = new JLabel(); // 百位 // 笑脸 private JLabel labelFace = new JLabel(); // 时间 private JLabel labelTimeG = new JLabel(); // 个位 private JLabel labelTimeS = new JLabel(); // 十位 private JLabel labelTimeB = new JLabel(); // 百位 // // 声明主窗体对象 MainFrame mainframe; (2)编写构造方法 public FaceJPanel(MainFrame mainframe) { this.mainframe = mainframe; this.setLayout(new BorderLayout()); this.setVisible(true); init(); }(3)初始化private void init() { JPanel panel = new JPanel(); // 布局盒子 BoxLayout boxLayout = new BoxLayout(panel, BoxLayout.LINE_AXIS); panel.setLayout(boxLayout); labelFace.addMouseListener(new FacelabelListener()); // 添加表情按钮监听器 // 添加计数区监听器 panel.addMouseListener(new MouseAdapter(){ public void mousePressed(MouseEvent arg0) { if(!Tools.isBoom) { labelFace.setIcon(Tools.face2); } } public void mouseReleased(MouseEvent arg0) { if(!Tools.isBoom) { labelFace.setIcon(Tools.face0); } } }); Icon icon0 = new ImageIcon("./image/d0.gif"); // 计算雷的每位数图片 Icon icon3 = new ImageIcon("./image/d" + Tools.allcount / 100 + ".gif");// 百位 Icon icon2 = new ImageIcon("./image/d" + Tools.allcount /10%10 + ".gif"); // 十位 Icon icon1 = new ImageIcon("./image/d" + Tools.allcount % 10 + ".gif"); // 个位 Icon iconSmile = new ImageIcon("./image/face0.gif"); // 雷数显示图片 labelCountG.setIcon(icon1); labelCountS.setIcon(icon2); labelCountB.setIcon(icon3); // 时间显示图片 labelTimeG.setIcon(icon0); labelTimeS.setIcon(icon0); labelTimeB.setIcon(icon0); // 表情显示图片 labelFace.setIcon(iconSmile); // 添加控件到panel panel.add(Box.createHorizontalStrut(2)); // 最左侧 panel.add(labelCountB); panel.add(labelCountS); panel.add(labelCountG); panel.add(Box.createHorizontalGlue()); // 添加水平方向的构件,占位 panel.add(labelFace); panel.add(Box.createHorizontalGlue()); panel.add(labelTimeB); panel.add(labelTimeS); panel.add(labelTimeG); panel.add(Box.createHorizontalStrut(2)); // 最右侧 // 实现边框效果 Border borderLow = BorderFactory.createLoweredBevelBorder(); // 内边框 Border borderEmpty = BorderFactory.createEmptyBorder(2, 2, 2, 2); Border borderCom1 = BorderFactory.createCompoundBorder(borderLow, borderEmpty); panel.setBorder(borderCom1); panel.setBackground(Color.LIGHT_GRAY); // 外边框 Border borderEmpty1 = BorderFactory.createEmptyBorder(5, 5, 0, 5); this.setBorder(borderEmpty1); this.setBackground(Color.LIGHT_GRAY); this.add(panel); }(4)在主窗体MainFrame中添加FaceJPanelpublic class MainFrame extends JFrame{ ...... private BombJMenuBar menuBar = new BombJMenuBar(this); FaceJPanel faceJPanel = new FaceJPanel(this); BombJPanel bombJPanel = new BombJPanel(this); ...... }在init方法中插入private void init() { ...... this.add(faceJPanel,layout.NORTH); ...... }运行工具类(com.tools/Tools)public class Tools { // 窗口图标 public static ImageIcon imageIcon = new ImageIcon("./image/icon.gif"); public static ImageIcon getImageIcon() { return imageIcon; } public static int rows = 9; // 雷区行数 public static int cols = 9; // 雷区列数 public static int timecount = 0; // 计时 public static int allcount = 10; // 所有雷的数量 public static int bombCount = allcount; // 剩余为未被标记雷数 public static boolean isBoom = false; // 是否踩雷 public static boolean isStart = false; // 是否开始 public static boolean isHole = false; // 是否开启后门外挂 // 排行榜 public static int time = 0; // 初级 public static int time1= 999; public static int time2= 999; public static int time3= 999; public static String name1="匿名"; public static String name2="匿名"; public static String name3="匿名"; // 中级 public static int time01= 999; public static int time02= 999; public static int time03= 999; public static String name01="匿名"; public static String name02="匿名"; public static String name03="匿名"; // 高级 public static int time001= 999; public static int time002= 999; public static int time003= 999; public static String name001="匿名"; public static String name002="匿名"; public static String name003="匿名"; // 游戏等级 public static final String LOWER_LEVEL = "初级"; public static final String MIDDLE_LEVEL = "中级"; public static final String HEIGHT_LEVEL = "高级"; public static final String CUSTOM_LEVEL = "自定义"; // 游戏当前等级 public static String currentLevel = LOWER_LEVEL; // 用来存放0-8地雷数字标签图片 public static ImageIcon mineCount[]; // 用来存放时间数字标签图片 public static ImageIcon timeCount[]; // 静态块 static { mineCount = new ImageIcon[9]; for (int i = 0; i <= 8; i++) { mineCount[i] = new ImageIcon("./image/" + i + ".gif"); } timeCount = new ImageIcon[11]; for (int i = 0; i < 10; i++) { timeCount[i] = new ImageIcon("./image/d" + i + ".gif"); } timeCount[10] = new ImageIcon("./image/d10.gif"); } // 笑脸表情标签图片 public static ImageIcon face0 = new ImageIcon("./image/face0.gif"); public static ImageIcon face1 = new ImageIcon("./image/face1.gif"); public static ImageIcon face2 = new ImageIcon("./image/face2.gif"); public static ImageIcon face3 = new ImageIcon("./image/face3.gif"); public static ImageIcon face4 = new ImageIcon("./image/face4.gif"); public static ImageIcon face5 = new ImageIcon("./image/face5.gif"); public static ImageIcon face6 = new ImageIcon("./image/face6.gif"); public static ImageIcon face7 = new ImageIcon("./image/face7.gif"); public static ImageIcon face8 = new ImageIcon("./image/face8.gif"); // 分别对应 mine雷标签图片 public static ImageIcon mine = new ImageIcon("./image/mine.gif"); public static ImageIcon mine0 = new ImageIcon("./image/mine0.gif"); public static ImageIcon mine1 = new ImageIcon("./image/mine1.gif"); //分别对应 ask问号标签图片 public static ImageIcon ask = new ImageIcon("./image/ask.gif"); public static ImageIcon ask1 = new ImageIcon("./image/ask1.gif"); public static ImageIcon iconTemp = new ImageIcon("./image/icon.gif"); public static Image icon = iconTemp.getImage(); public static ImageIcon blank = new ImageIcon("./image/blank.gif"); public static ImageIcon blood = new ImageIcon("./image/blood.gif"); public static ImageIcon error = new ImageIcon("./image/error.gif"); // 旗子标签图片 public static ImageIcon flag = new ImageIcon("./image/flag.gif"); // hole外挂标签图片 public static ImageIcon hole = new ImageIcon("./image/hole.gif"); }
2022年05月18日
233 阅读
0 评论
0 点赞
2022-05-02
Java实现扫雷小游戏介绍
游戏介绍扫雷游戏是一款windows经典的电脑休闲小游戏,办公室打发无聊时间用的,非常受人喜爱,扫雷就是要把所有非地雷的格子揭开即胜利,踩到地雷格子就算失败。让玩家们找回童年玩此款游戏的乐趣,在游戏中只要掌握一些小技巧,闯关就会变得容易很多哦!初步分析通过扫雷界面图片可以分析出:界面是一个窗口,有菜单栏、标题栏和游戏区。菜单栏里有游戏和帮助两个菜单。游戏菜单里有:开局、初级、中级、高级、自定义、扫雷英雄榜、退出等7个子菜单;1、在游戏菜单里能够设置游戏的重新开局, 也可以通过点击游戏区里的笑脸来重新开局2、设置游戏的级别,分为初中高三个游戏级别:初级是由9*9的方格组成,雷数为10,非雷数71;中级由16*16的方格组成,排有40个雷,非雷数为216;高级是由16*30的方格组成,其中有99个雷,非雷数为381;3、自定义功能能够让玩家重新定义游戏的布局(行列范围9~30)与雷数(少于格子数且不能全是雷);4、表情 游戏仍然在继续的时候显示微笑的表情::(呵呵),当鼠标按住方格的时候显示惊讶的表情::(惊讶),踩中地雷的时候显示悲哀的表情::(泪),当你成功排除了所有雷,就会显示酷的表情::(酷)。5、在你翻开第一个方块起,计算器就开始计时,直到你成功排掉所有地雷或者踩中地雷的时候就结束计时。游戏规则与玩法游戏规则扫雷就是要把所有非地雷的格子揭开即胜利,踩到地雷格子就算失败;游戏主区域由很多个方格组成;使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷;如果点开的格子为空白格,即其周围有0颗雷,则其周围格子自动打开,如果其周围还有空白格,则会引发连锁反应;在你认为有雷的格子上,点击右键即可标记雷,如果不能确定,可以双击右键即标记问号;如果一个已打开格子周围所有的雷已经正确标出,则可以在此格上同时点击鼠标左右键以打开其周围剩余的无雷格。玩法左键单击:在判断出不是雷的方块上按下左键,可以打开该方块。如果方块上出现数字,则该数字表示其周围3×3区域中的地雷数(一般为8个格子,对于边块为5个格子,对于角块为3个格子,所以扫雷中最大的数字为8);如果方块上为空(相当于0),则可以递归地打开与空相邻的方块;如果不幸触雷,则游戏结束。右键单击:在判断为地雷的方块上按下右键,可以标记地雷(显示为小红旗)。标记地雷后重复一次右击则标记(?),需要一次或两次操作右击来取消标雷)。双击:同时按下左键和右键完成双击。当双击位置周围已标记雷数等于该位置数字时操作有效,相当于对该数字周围未打开的方块均进行一次左键单击操作。地雷未标记完全时使用双击无效。若数字周围有标错的地雷,则游戏结束。需求分析扫雷的左键逻辑。扫雷中鼠标左键被用来打开当前地图上的方块,但是如果你仔细研究,就会发现方块被打开发生在鼠标左键抬起之后,而不是鼠标左键按下的时候,这一点非常重要。如果在已经开启的方块上点击鼠标是没有任何作用的,并且如果方块上方被标记为旗帜,则该方块也无法被鼠标左键开启,这也是为了防止误操作导致游戏意外结束。如果游戏在初始状态,鼠标左键的抬起事件会触发了雷区(或者称之为地图)的初始化以及方块打开操作,并开始计时。如果游戏处于运行状态,则要判断点击的位置是否为地雷,如果是地雷直接结束,否则执行默认的方块打开操作。鼠标右键主要是用来标记当前方块的属性,是地雷(旗帜)还是不确定(问号),这里需要注意的是标记的过程中,是鼠标点击的时候就进行了,而不是按键抬起之后。随着方块标记的转变,地雷的显示数量也随之改变。这里还有一个小细节,就是鼠标的右键操作并不会导致游戏开始计时,换句话说右键操作并不会让游戏进入运行状态。自动打开操作是扫雷游戏的基本规则。如果在双击(左右键)的位置存在一个数字,且周围 8 个方块上方已经被标记上了和数字相同的旗帜,则同时点击鼠标左右键会自动打开周围未标记的方块。方块被打开是在鼠标弹起操作后,双击按下只是显示相关方块的背景,给人的效果是方块被按下去。统计点击位置周围的标记数量,标记数量和显示数字一致的话,打开剩余方块。这里有个小细节,就是打开的时候并不是仅仅打开周围的 8 个方块,如果这 8 个方块中存在空白的情况,会触发成片开启的情况。软件功能框架图
2022年05月02日
396 阅读
0 评论
0 点赞
2022-02-27
C语言读写程序文件-学习三十二
顺序读写数据文件在顺序写时,先写入的数据存放在文件中前面,后写入的数据存放在文件中后面。在顺序读时,先读文件中前面的数据,后读文件中后面的数据。对顺序读写来说,对文件读写数据的顺序和数据文件中的物理顺序是一致的。读写一个字符的函数fgetc()函数原型:int fgetc( FILE * fp );调用形式:fgetc(fp)作用:从 fp 所指向的输入文件中读取一个字符。返回值:成功则返回的是读取的字符,发生错误则返回 EOF(即-1)。fputc()函数原型:int fputc( int c, FILE *fp );调用形式:fputc(c,fp)功能:把参数 c 的字符值写入到 fp 所指向的输出流(文件)中。返回值:写入成功,它会返回它会返回写入的字符(一个非负值),发生错误,则会返回 EOF(即-1)。例如:从一个磁盘文本文件顺序读入字符并在屏幕上显示出来。#include <stdio.h> #include <string.h> void main() { char c; FILE* fp = NULL; fopen_s(&fp, "a.txt", "r"); c = fgetc(fp); while (c != EOF) { putchar(c); c = fgetc(fp); } fclose(fp); }feof()函数调用方式:feof(fp)功能:对于二进制文件读取时判断是否结束。返回值:如果是文件结束,函数feof()值为1(真);否则为0(假)。例如:从一个磁盘二进制文件顺序读入字符并在屏幕上显示出来。#include <stdio.h> #include <string.h> void main() { char c; FILE* fp = NULL; fopen_s(&fp, "a", "r"); c = fgetc(fp); while (!feof(fp)) { putchar(c); c = fgetc(fp); } fclose(fp); }例子从键盘输入一些字符,逐个把它们送到磁盘上去直到用户输入一个“#”为止。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char c,filename[10]; FILE *fp = NULL; printf("请输入所用的文件名:"); scanf("%s", filename); if ((fp = fopen(filename, "w")) == NULL) { printf("无法打开此文件\n"); exit(0); } c = getchar(); // 接收执行scanf时最后输入的回车符 // 如果注释掉,文件中会首先换行,然后再输入的字符串 printf("请输入一个字符串(以#结束)"); c = getchar(); // 第一个输入的字符被赋给变量c while (c!='#') { fputc(c, fp); putchar(c); // 字符被输出到显示器 c = getchar(); } putchar(10); // 向屏幕输出一个换行符 fclose(fp); }将一个磁盘文件中的信息复制到另一个磁盘文件中。今要求建立的a.txt文件中的内容复制到另一个磁盘文件b.txt中。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char c=NULL,infile[10],outfile[10]; FILE *in = NULL; FILE *out = NULL; printf("请输入读取的文件名:"); scanf("%s", infile); printf("请输入输出的文件名:"); scanf("%s", outfile); if ((in = fopen(infile, "r")) == NULL) { printf("无法打开此文件\n"); exit(0); } if ((out = fopen(outfile, "w")) == NULL) { printf("无法打开此文件\n"); exit(0); } while (c!=EOF) { c= fgetc(in); fputc(c, out); putchar(c); // 字符被输出到显示器 } putchar(10); // 向屏幕输出一个换行符 fclose(in); fclose(out); }读写一个字符串的函数fgets()函数原型:char *fgets( char *buf, int n, FILE *fp );调用格式:char *fgets( str, n, fp ); 功能:从 fp 所指向的输入流(文件)中读取长度为(n - 1)的字符串存放到字符数组str中,并在最后追加一个 null 字符(即'\0')来终止字符串。返回值:读成功返回地址str ,失败则返回NULLfputs()函数原型:int fputs( const char *s, FILE *fp );调用格式:int fputs( str, fp );功能:str所指向的字符串写到文件指针变量fp所指向的文件中。返回值:如果写入成功,则返回写出的字符个数(一个非负值),如果发生错误,则返回EOF(即-1)。说明:fgets(str,n,fp);中 n 是要求得到的字符个数,但实际上只读 n-1 个字符,然后在最后加一个\0字符, 这样得到的字符串共有n个字符,把它们放到字符数组str中。fgets()函数,如果在读完 n-1 个字符之前就遇到一个换行符 \n 或文件的末尾 EOF, 则读入结束,则只会返回读取到的字符,包括换行符。fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。fputs()函数字符串末尾的\0不输出。例子从键盘读入若千个字符串,然后把字符串送到磁盘文件中保存。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char str[3][10]; int i; FILE *fp = NULL; printf("请输入要写入的字符串:\n"); for (i = 0; i < 3; i++) { gets(str[i]); } if ((fp = fopen("a.txt", "w")) == NULL) { printf("无法打开此文件\n"); exit(0); } for (i = 0; i < 3; i++) { fputs(str[i], fp); fputs("\n", fp); printf("写入成功:"); printf("%s\n", str[i]); } fclose(fp); }读取文件中的字符串,打印到控制台#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main() { char str[3][10]; int i; FILE *fp = NULL; if ((fp = fopen("a.txt", "r")) == NULL) { printf("无法打开此文件\n"); exit(0); } for (i = 0; i < 3; i++) { fgets(str[i],10,fp); printf("%s", str[i]); } fclose(fp); }用格式化的方式读写文件一般调用方式为:fprintf(文件指针, 格式字符串, 输出表列);例如:fprintf(fp,"%d,%f",i,f);fscanf(文件指针, 格式字符串, 输入表列);例如:fscanf(fp,"%d,%f",&i,&f);用fprintf和fscanf对磁盘文件读写,使用方便,容易理解,但是由于在输入时要将ASCII码转换为二进制形式,在输出时又要将二进制形式转换符,花费时间比较多。因此在内存与磁盘频繁交换的情况下,最好不要用 fprintf 和 fscanf 函数,可以用fread 和 fwrite 函数。二进制 I/O 函数fread函数原型:size_t fread(void *buffer, size_t size, size_t count, FILE *a_file);返回值:如果读取成功,则返回读的块数,失败,则返回0 。fwrite函数原型:size_t fwrite(const void *buffer, size_t size, size_t count, FILE *a_file);返回值:如果写入成功,则返回写的块数,失败,则返回0 。这两个函数都是用于存储块的读写 - 通常是数组或结构体。例子:从键盘输入5个学生的有关数据,然后把它们转存到磁盘文件上去,读取并打印到控制台。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) #define SIZE 5 struct Student { int num; char name[10]; int age; char addr[15]; }stu[SIZE]; void main() { void save(); void reads(); int i; printf("请输入学生数据:\n"); for (i = 0; i < SIZE; i++) { scanf("%d %s %d %s", &stu[i].num, stu[i].name, &stu[i].age, stu[i].addr); } save(); printf("学生信息:\n"); reads(); } void save() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "wb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) { printf("写入文件错误\n"); } } fclose(fp); } void reads() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "rb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { fread(&stu[i], sizeof(struct Student), 1, fp); printf("%4d %-10s %4d %-15s\n", stu[i].num, stu[i].name, stu[i].age, stu[i].addr); } fclose(fp); }从已有的二进制文件“stu2.dat”中,读入数据并输出到“stu.dat”文件中#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) #define SIZE 7 struct Student { int num; char name[10]; int age; char addr[15]; }stu[SIZE]; void main() { void load(); void save(); void reads(); load(); save(); printf("学生信息:\n"); reads(); } void save() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "wb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) { printf("写入文件错误\n"); } } fclose(fp); } void reads() { int i; FILE *fp = NULL; if ((fp = fopen("stu.dat", "rb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { fread(&stu[i], sizeof(struct Student), 1, fp); printf("%4d %-10s %4d %-15s\n", stu[i].num, stu[i].name, stu[i].age, stu[i].addr); } fclose(fp); } void load() { int i; FILE *fp = NULL; if ((fp = fopen("stu2.dat", "rb")) == NULL) { printf("无法打开此文件\n"); return; } for (i = 0; i < SIZE; i++) { if (fread(&stu[i], sizeof(struct Student), 1, fp) != 1) { if (feof(fp)) { fclose(fp); return; } printf("无法打开此文件\n"); } } fclose(fp); }其它读写函数putw 和 getw 函数作用:以二进制形式,对磁盘文件读写一个int型的整数,2个字节。返回值:成功,则返回所写的整数值;失败,则返回EOF例如:putw(10, fp); i=getw(fp);随机读写数据文件对文件进行顺序读写比较容易理解,也容易操作,但有时效率不高。文件位置标记为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(简称文件标记),用来指示“接下来要读写的下一个字符的位置”。一般情况下,在对字符文件进行顺序读写时,文件标记指向文件开头,进行读的操作时,就读第一个字符,然后文件标记向后移动一个位置,在下一次读操作时,将位置标记指向第二个字符读入,以此类推直到遇文件尾结束。随机读写可以在任何位置读取和写入数据文件位置标记的定位将文件的指针指向文件的开头,进行文件操作rewind函数函数原型:void rewind(FILE *fp);功能:重置文件位置指针到文件开头。返回值:无-例如:#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) void main(){ FILE *fp1, *fp2; fp1 = fopen("file1.dat", "r"); fp2 = fopen("file2.dat", "w"); while (!feof(fp1)) { putchar(getc(fp1)); } putchar(10); rewind(fp1); while (!feof(fp1)) { putc(getc(fp1), fp2); } fclose(fp1); fclose(fp2); }可以强制使文件标记指向指定的位置fseek函数调用形式:fseek(文件类型指针,位移量,起始点)起始点:0代表“文件开始位置(SEEK_SET)”,1为“文件当前位置(SEEK_CUR)”,2代表“文件的末尾(SEEK_END)”。位移量指以起始点为基点,向前移动的字节数。位移量应是long型数据(在数字的末尾加一个字母L)。fseek函数一般用于二进制文件。例如: fseek(fp,100L,0); // 将位置指针移动到离文件头100个字节处 fseek(fp,50L,1); // 离当前位置50个字节处 fseek(fp,-10L,2); // 从文件末尾处向后退10个字节 ftell函数由于文件中的文件位置标记经常移动,人们往往不容易知道其当前位置,所以常用ftell函数得到当前位置(相对于文件开头的位移量来表示)。如果调用函数时出错(如不存在fp指向的文件),ftell函数返回值为-1L。例如:i=ftell(fp);if(i==-1L){printf("error\n");}例子在磁盘文件上存有10个学生的数据要求将第1,3,5,7,9个学生数据输入计算机,并在控制台显示出来。#include <stdio.h> #include <string.h> #include <stdlib.h> #pragma warning (disable:4996) struct Student { int num; char name[10]; int age; }stu[10]; void main() { FILE *fp; int i; if ((fp = fopen("stu.dat", "rb")) == NULL) { printf("打开文件失败"); exit(0); } for (i = 0; i < 10; i+=2) { fseek(fp, i * sizeof(struct Student), 0); fread(&stu[i], sizeof(struct Student), 1, fp); printf("%4d %-10s %4d\n", stu[i].num, stu[i].name, stu[i].age); } fclose(fp); }文件读写的出错检测ferror函数一般调用形式:ferror(fp);返回值:如果为0,表示未出错,否则表示出错。每次调用输入输出函数,都产生新的ferror函数值,因此调用输入输出函数后立即检查。调用fopen时,ferror的初始值自动置为0。clearerr函数作用是使文件错误标志和文件结束标志置为0。调用一个输入输出函数时出现错误(ferror值为非零值),立即调用clearerr(fp),使ferror(fp)值变0,以便再进行下一次检测只要出现文件读写错误标志,它就一直保留,直到对同一文件调用 clearerr 函数或 rewind 函数,或任何其他一个输入输出函数总结在使用文件时,首先要定义一个文件指针:FILE *fp;然后通过该指针来操作相应的文件;通过fopen这个函数,使文件指针fp和相应的文件建立了联系,通过fclose函数将切断fp和文件的联系;如果以一次一个字符的方式处理文件,需要用 fgetc 或者 fputc 函数;如果以一次一行的方式处理文件,可以用 fgets 或者 fputs 函数;如果以一次一个结构体的方式处理文件,可以用 fread 和 fwrite 函数(多为二进制文件);
2022年02月27日
742 阅读
0 评论
0 点赞
2022-02-26
C语言文件-学习三十一
为什么要引入文件数据量大数据访问频繁程序结果需要长期保存使用文件的目的1.程序与数据分离:数据文件的改动不引起程序的改动;2.数据共享:不同程序可以访问同一数据文件中的数据;3.能长期保存程序运行的中间数据或结果数据。文件“文件”指存储在外部介质上数据的集合一批数据是以文件的形式存放在外部介质上的操作系统是以文件为单位对数据进行管理想找存放在外部介质上的数据,先按文件名找到指定的文件,然后再从该文件读数据要向外部介质上存储数据也必须先建立一个文件(以文件名作为标志),才能向它输出数据输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为流(stream),即数据流。流表示了信息从源到目的端的流动。输入操作时,数据从文件流向计算机内存输出操作时,数据从计算机流向文件从C程序的观点来看,无论程序一次读写一个字符,或一行文字,或一个指定的数据区,作为输入输出的各种文件或设备都是统一以逻辑数据流的方式出现的。C语言言把文件看作是一个字符(或字节)的序列。输出流就是一个字符流或字节(内容为二进制数据)流。C的数据文件由一连串的字符(或字节)组成,而不考虑行的界限,两行数据间不会自动加分隔符,对文件的存取是以字符(字节)为单位的。输入输出数据流的开始和结束仅受程序控制而不受物理符号(如回车、换行符)控制,这就增加了处理灵活性。这种文件称为流式文件。文件名文件要有一个唯一的文件标识,以便用户识别和引用。文件标识包括三部分:1.文件路径2.文件名主干3.文件后缀文件路径表示文件在外部存储设备中的位置。例如: C:\VS\temp\file1.datC:\VS\temp\ 文件路径file1 文件名主干 (命名规则遵循标识符的命名规则).dat 文件后缀 (doc、txt、dat、c、cpp、obj、exeopt、 bmp等)文件的分类按存储介质:普通文件:存储介质文件(磁盘、磁带等设备文件)设备文件:非存储介质(键盘、显示器等设备文件)按数据的组织形式:文本文件:ASCII文件,每个字节存放一个字符的ASCII码文本文件特点:存储量大、速度慢、便于对字符操作二进制文件:数据按其在内存中的存储形式原样存放文本文件特点:存储量小、速度快、便于存放中间结果例如:int型数 10000内存存储形式: 0010011 00010000存储以文本文件形式:1 0 0 0 0分别以 1 和 0 的 ASCII码 的形式与下面对应0110001 00110000 00110000 00110000 00110000存储以二进制文件形式: 0010011 00010000 (与内存存储形式一致)文件缓冲区ANSI C标准采用“缓冲文件系统”处理数据文件缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲。从内存向磁盘输出数据,必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去文件类型指针每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的,该结构体类型是由系统声明的,取名为FILE。例如:在stdio.h头文件中有以下的文件类型声明:typedef struct { short level; // 缓冲区"满"或"空"的程度 unsigned flags; //文件状态标志 char fd; // 文件描述符 unsigned char hold; // 产如果没有缓冲区,则不读取字符 short bsize; // 缓冲区大小 unsigned char *buffer; // 产数据缓冲区的位置 unsigned char *curp; // 产当前激活指针 unsigned istemp; //产临时文件,指示器 short token; // 用于有效性检查 }FILE;用FILE定义文件指针例如:FILE *fp文件打开时,系统自动建立文件结构体,并把指向它的指针返回来,程序通过这个指针获得文件信息,访问文件。文件关闭后,它的文件结构体被释放。打开和关闭文件C文件操作用库函数实现,包含在stdio.h文件使用方式:打开文件 --> 文件读/写 --> 关闭文件系统自动打开和关闭三个标准文件:标准输人-----键盘 stdin标准输出-----显示器 stdout标准出错输出-----显示器 stderr用fopen函数打开数据文件“打开”是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据)。在编写程序时,在打开文件的同时,一般都指定一个指针变量指向该文件,也就是建立起指针变量与文件之间的联系,这样就可以通过该指针变量对文件进行读写。函数原型:FILE *fopen(char *name , char *mode);char *name 要打开文件的名字char *mode 使用文件的方式(读、写、读写)返回值:正常打开,返回指向文件结构体的指针;打开失败,返回NULL。fopen函数参数中的文件名可以带文件路径,但是文件路径中的“\”必须使用转义字符。例如:要打开在路径为C:\VS\temp\file1.dat的文件应该写成:fopen("C:\\VS\\temp\\file1.dat","r");mode 的值可以是下列值中的一个模式描述r打开一个已有的文本文件,允许读取文件。w打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。a打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。r+打开一个文本文件,允许读写文件。w+打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。a+打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:"rb" , "wb", "ab", "rb+" 或 "r+b", "wb+" 或 "w+b", "ab+" 或 "a+b"说明:1.用“r”方式打开一个文本文件,只能用于向计算机输入,而不能用作向该文件输出数据, 而且该文件应该已经存在,并存有数据,这样程序才能从文件中读数据。不能用“r”方式打开一个并不存在的文件,否则出错。为输入打开一个二进制文件,用“rb”。2.用“w”方式打开一个文本文件,只能用于向该文件写数据(即输出文件),而不能用来向计算机输入。如果原来不存在该文件,则在打开文件前新建一个以指定的名字命名的文件。如果原来已存在一个以该文件名命名的文件,则在打开文件前先将该文件删去,然后重新建立一个文件。为输出打开一个二进制文件,用“wb”。3.如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用“a”方式打开。但此时应保证该文件已存在;否则将得出错的信息。打开文件时,文件读写标记移到文件末尾。向二进制文件尾添加数据,用“ab”。4.用 “r+、w+、a+ ” 方式打开的文本文件既可以用来输入数据,也可以用来输出数据。如果是二进制文件用 “rb+、wb+、ab+ ”。5.在向计算机输入文本文件时,会将回车换行符转换为一个换行符,在输出时把换行符转换成为回车和换行两个字符。 在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到外部文件中的数据形式完全一致,一一对应。6.如果打开失败,fopen函数将会带回一个出错信息。 fopen函数将带回一个空指针值NULL常用下面的方法打开一个文件: if ((fp = fopen("file1", "r")) == NULL) { printf("Can not open this file\n"); exit(0); // 终止正在执行的程序 }注意在VS中编译器会出现使用fopen()不安全,建议使用fopen_s()的提示格式:fp=fopen("文件路径","模式"); fopen_s(&fp,"文件路劲","模式");C标准建议用表列出的文件使用方式打开文本文件或二进制文件,但目前使用的一些C编译系统可能不完全提供所有这些功能。比如:有的C版本只能用 “r",“w",“a" 方式,有的C版本不用 “r+”,“w+”,“a+",而用 “rw”,“wr",“ar” 等。用fclose函数关闭数据文件“关闭”是指撤销文件信息区和文件缓冲区。关闭文件用fclose函数。fclose函数调用的一般形式为:fclose(文件指针);例如:fclose (fp);
2022年02月26日
624 阅读
0 评论
0 点赞
2022-02-25
C语言共用体与枚举类型-学习三十
共用体类型共用体也是一种构造类型,将不同类型的数据组合在一起。但与结构体类型不同,在共用体内的不同成员占用同一段存储区,成员之间互相覆盖,即在同一时刻只有一个成员起作用。为共用体变量分配空间的大小是以所有成员中占用空间字节数最多的成员为标准。共用体类型的声明及变量的定义共用体类型的声明与结构体的声明完全相同,只是关键字为union。共用体类型的声明格式:union 共用体名{ 数据类型 成员名1; 数据类型 成员名2; ... 数据类型 成员名n; };例如: union data { int i; char c; float f; };共用体变量的定义方式与结构体变量的定义方式相似,也有3种方式:1.类型声明与变量定义分开 union data { int i; char c; float f; }; union data d1, d2;2.在声明类型的同时定义变量 union data { int i; char c; float f; }d1, d2;3.直接定义共用体类型的变量,不给出共用体名 union { int i; char c; float f; }d1, d2;共用体变量初始化对于共用体变量,在定义的同时,可以初始化,但是初始化只能对第一个成员初始化。例如:union data d1={10}; 即 共用体里的int i = 10;共用体变量中成员的引用与结构体变量成员引用的方式相同,也使用 “->” 和 “.” 两种运算符来实现;格式:共用体变量名.成员名共用体指针->成员名例如:union data d1, *tpd; pd = &d1;对d1成员的引用可以是:d1.i 或 pd ->i 、d1.c 或 pd ->c、d1.f 或 pd ->f同类型的共用体变量之间可以互相赋值。例子:#include <stdio.h> union data{ int i; char c; float f; }; struct data2{ int i; char c; float f; }; void main() { union data ud; struct data2 sd; ud.i = 10; ud.c = 'A'; ud.f = 20; sd.i = 10; sd.c = 'A'; sd.f = 20; printf("size of ud: %d, size of sd: %d\n", sizeof ud, sizeof sd); printf("ud.i: %d,ud.c: %c,ud.f: %f\n", ud.i, ud.c, ud.f); printf("sd.i: %d,sd.c: %c,sd.f: %f\n", sd.i, sd.c, sd.f); }从上面的运行结果可看出对共用体变量成员的赋值,保存的是最后的赋值前面对其他成员的赋值均被覆盖。由于结构体变量的每个成员拥有不同的内存单元,因而不会出现这种情况。共用体与结构体比较1.结构体类型是一种复杂而灵活的构造数据类型,它可以将多个相互关联但类型不同的数据项作为一个整体进行处理。 定义结构体变量时,每一个成员都要分配空间存放各自的数据。2.共用体是另一种均造数据类型,定义共用体变量时,只按占用空间最大的成员来分配空间,在同一时刻只能存放一个成员的值。3.共用体变量的定义形式与结构体变量的定义形式类似,都有三种形式,只是关键字不同。4.结构体变量通过初值列表实现对变量中的成员初始化。共用体变量只能对第一个成员初始化。5.共用体成员的访问方式与结构体相同,成员的访问通过运算符 “.” 和 “->” 实现。例子设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。教师的数据包括:姓名、号码、性别、职业、职务。现要求把它们放在同一表格中#include <stdio.h> #pragma warning (disable:4996) struct{ int num; char name[10]; char sex; char job; union { int banji; char position[10]; }category; }person[2]; void main() { int i; for (i = 0; i < 2; i++) { printf("请输入信息:\n"); scanf("%d %s %c %c", &person[i].num, &person[i].name, &person[i].sex, &person[i].job); if (person[i].job == 's') { scanf("%d", &person[i].category.banji); } else if(person[i].job=='t'){ scanf("%s", &person[i].category.position); } else { printf("输入错误!"); } } printf("\n"); for (i = 0; i < 2; i++) { if (person[i].job == 's') { printf("%-6d %-10s %-3c %-3c %-6d\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.banji); } else { printf("%-6d %-10s %-3c %-3c %-6s\n", person[i].num, person[i].name, person[i].sex, person[i].job, person[i].category.position); } } }枚举类型如果一个变量只有几种可能的值,则可以定义为枚举(enumration)类型,所谓“枚举”就是把可能的值一一列举出来,变量的取值限于列举出来的值的范围内。枚举类型声明格式:enum 枚举类型名{ 枚举元素列表 // 枚举元素之间以逗号间隔 };枚举变量的定义:1.先声明枚举数据类型,再定义枚举变量 enum week{ sun , mon ,tue , wed , thu , fri , sat }; enum week workday;2.声明枚举类型的同时定义枚举变量 enum week{ sun , mon ,tue , wed , thu , fri , sat }workday;3.不指定枚举类型名,直接定义枚举变量 enum { sun , mon ,tue , wed , thu , fri , sat }workday;枚举变量workday的值只能在枚举元素sun,mon, .… sat之间取其中一个,不能超出这个范围。枚举类型使用时需注意以下几点:1.枚举元素是常量,有固定的值,不能将其作为变量使用例如: sun=0; mon = 1 错误,不能为枚举元素赋值2.每一个枚举元素都代表了一个整数,编译程序按定义时的顺序默认它们的值为0、1、2、3...... 上面的定义中,sun的值为0、mon的值为1、.....sat的值为6。例如:如果有赋值语句:workday = mon;相当于:workday = (enum week)1;一个整数不能直接赋给一个枚举变量先进行强制类型转换才能赋值。3.如果不希望使用默认的值,也可以在声明数据类型时指定值。例如指定枚举元素sun的值为7,mon的值为1,tue的值为2,以后顺序加1,sat为6。 enum { sun=7, mon=1, tue=2, wed , thu , fri , sat }workday;4.枚举值,可以用来作判断比较例如:if(workday == mon)5.不能有两个相同名字的枚举元素,枚举元素也不能与符号常量和变量同名。用typedef声明新类型名1.简单地用一个新的类型名代替原有的类型名typedef int Integer; typedef float Real; int i,j; float a,b; // 等价 Integer i,j; Real a,b;2.命名一个简单的类型名代替复杂的类型表示方法命名一个新的类型名代表结构体类型:typedef struct{ int month; int day; int year; }Date; // 声明新类型名Date,它代表上面指定的一个结构体类型 Date birthday; Date *p; // p为指向此结构体类型数据的指针命名一个新的类型名代表数组类型 typedef int Num[100]; //声明NUM为整型数组类型 Num a; //定义a为整型数组变量命名一个新的类型名代表一个指针类型 typedef char *String; // 声明String为字符指针类型 String p,s[10]; // p为字符指针变量,s为指针数组命名一个新的类型名代表指向函数的指针类型typedef int (*Pointer)(); // 声明Pointer为指向函数的指针类型,该函数返回整型值 Pointer p1,p2; // p1,p2为Pointer类型的指针变量声明一个新的类型名的方法:1.先按定义变量的方法写出定义体 int i;2.将变量名换成新类型名(将i换成Count)3.在最前面加 typedef4.得到 typedef int Count;以定义上述的数组类型为例来说明:1.先按定义数组变量形式书写:int a[100];2.将变量名a换成自己命名的类型名:int Num[100];3.在前面加上typedef,得到typedef int Num[100];4.用来定义变量:Num a;5.相当于定义了:int a[100];对字符指针类型,也是:char *p;char *String;typedef char *String;String p;说明:1.以上的方法实际上是为特定的类型指定了一个同义字(synonyms)。例如:typedef int Num[100]; Num 是 int [100] 的同义词typedef int (*Pointer)(); Pointer是int(*)() 的同义词2.用typedef只是对已经存在的类型指定一个新的类型名,而没有创造新的类型。3.用tyoedef声明数组类型、指针类型、结构体类型、共用体类型、枚举类型等,使得编程更加方便。4.typedef 与 #define 表面上有相似之处typedef int COUNT#define COUNT int作用都是用COUNT代表int。#define是在预编译时处理,只能作简单的字符串替换, 而typedef是在编译时处理的,为已有类型命名。5.当不同源文件中用到同一类型数据时(数组、结构体等),常用typedef声明一些数据类型。把它们单独放在一个头文件中。6.使用typedef名称有利于程序的通用与移植。有时程序会依赖于硬件特性,用typedef类型就便于移植。
2022年02月25日
409 阅读
0 评论
0 点赞
1
2
3
...
8