黑马程序员技术交流社区

标题: 键盘标准输入流的问题 [打印本页]

作者: littlefoxtail    时间: 2013-5-14 15:42
标题: 键盘标准输入流的问题
本帖最后由 littlefoxtail 于 2013-5-14 23:41 编辑

System.out返回的是InputStream,在调用read的时候不是读取数据的下一个字节然后转成int,那为什么键盘输入一个字符(两个字节)会直接返回这个字符的int值
作者: 殇_心。    时间: 2013-5-14 16:46
read方法返回的是一个int类型的值。
作者: ZhaoYuBetter    时间: 2013-5-14 16:48
类型提升了, int 类型是10进制类型,符合我们的日常生活习惯,
就返回了 int了。如果你想看到 byte 的值,可以将其转成2进制查看:
public static void main(String[] args) throws IOException {
                InputStream in = System.in;
                int value = in.read();
                String binaryString = Integer.toBinaryString(value);
                System.out.println(binaryString);
        }
作者: littlefoxtail    时间: 2013-5-14 17:35
我的意思是,比如输入一个a,它是两个字节,read()的时候应该是先读第一个字节 再读第二个字节吧
作者: xuemeng    时间: 2013-5-14 18:21
本帖最后由 xuemeng 于 2013-5-14 18:37 编辑

我贴上自己的笔记和代码吧 ,其实这个问题老毕有讲过!! 这个涉及到字节输入流的缓冲区读取原理了:
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
class Demo {
public static void main(String[] args) throws IOException {
MyBufferedInputStream mbis = new MyBufferedInputStream(
new FileInputStream("d:\\a.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("d:\\c.mp3"));
int num = 0;
while ((num = mbis.myRead()) != -1) {
bos.write(num);
}
bos.close();
mbis.myClose();
}
}
class MyBufferedInputStream {
// 自定义缓冲区, 肯定是一个装饰类, 那么肯定有个InputStream类型的私有属性,一个带InputStream类型参数的构造器
// 因为要到缓冲区去读数据,那么就要定义一个缓冲区,保证能够读取缓冲去的数据, 还要一个指针变量和计数器
// 定义InputStream类型的属性
private InputStream is;
// 定义指针属性
private int pos = 0;
// 定义计数器,记录从硬盘读取到的字节个数,同时,当用户从缓冲区中读取数据后,相对应的要减去, 以判断缓冲区中是否还有数据
private int count = 0;
// 定义一个容器,该容器是is对象从硬盘读取到的数据所存放的缓存区
private byte[] bytes = new byte[1024];
public MyBufferedInputStream(InputStream is) {
this.is = is;
}
// 定义读取数据方法,这个方法实际上是读取is对象读取到缓冲去的数据
public int myRead() throws IOException {
// 当调用myRead()方法时, 判断计数器是不是0,如果是0, 那么就要用is对象调用read(byte[]
// b)方法到硬盘上去读取数据到硬盘上
if (count == 0) {
// is对象读取数据到缓冲区
count = is.read(bytes);
// 判断是否读取到数据, 如果count值小于0, 说明硬盘上没有数据可读,那么调用myRead()方法一样什么也读不到,返回-1
if (count < 0) {
return -1;
}
// 如果当count==0成立, 那么is对象就会到硬盘上读取数据存储到缓冲区中, 这个时候,读取到的数据,指针必然指向0下标;
// 因为这个时候is对象要先读取数据到缓冲区, 用户从缓冲区读数据,刚独到缓冲去的数据, 那么记录的数据必然从0下标开始
pos = 0;
// 如果不是, 那么就表示读取到数据了,那么就把存储到缓冲区bytes中的第一个下标的值返回去
byte b = bytes[pos];
// 为了读取下个数据,指针要指向下一个值,计数器要减1,记录缓冲区还有多少数据没有被读取
pos++;
count--;
// 把从缓存区读取到的数据返回给用户
return b & 255;
// 当调用myRead方法,还有一种可能,就是缓冲区有数据, 如果有数据,这个时候指针和计数器一定
} else if (count > 0) {
byte b = bytes[pos];
pos++;
count--;
//注意看这里的红色字体部分!!1   
// 这里最后为什么返回的b还要做一个 & 运算? 而且为什么本方法返回值是int类型, 而不是byte类型?
// 原因是, 假如is对象读取到的数据中有这么一个字节 1111-1111, 这个数据的十进制其实就是 -1, 而我们在使用
// myRead()方法读取数据时, 用-1来判断数据是否读取完, 这样就会出问题了, 因为明明缓冲区中有数据, 只因为我们读取的
// 数据是-1, 返回-1, 导致缓冲区的数据没法继续读取了.这样绝对不可以,所以做一个与运算
// 11111111 (字节b)
// 00000000 - 00000000 - 00000000 - 11111111 (int类型的255进行&运算)
// 00000000 - 00000000 - 00000000 - 11111111
// (结果原数据没变,但是补0了,那么返回的就是10进制的255了,那么这样不是数据变了吗,其实并非如此,在写的时候,又会把这个数据前面的0截取掉,
// 那么最后写入的又是 11111111, 也就是-1;
return b & 0xff;
}
return -1;
}
// 写自己的关闭资源的方法
public void myClose() throws IOException {
is.close();
}
}

未命名.png (79.12 KB, 下载次数: 0)

未命名.png

作者: xuemeng    时间: 2013-5-14 18:42
本帖最后由 xuemeng 于 2013-5-14 18:44 编辑

再来补充一段代码, 两端代码和注释结合着看:
import java.io.IOException;
import java.io.InputStream;

class Demo {
        public static void main(String[] args) {
                // 通过键盘输入获取标准输入流对象
                InputStream is = System.in;

                // 因为是InputStream类型, 所以可以调用read方法,又因为是键盘输入,所以键盘输入是数据源,
                // 因为是键盘输入, 那么读取时就不好按数组读取了, 那么就一个字节一个字节读取,
                // 因为读取的是一个字节,那么用户输入时不可能只输入一个字节,那么就要循环读取用户输入的数据,
                // 因为每次读取一个数据, 有是循环读取, 那么这时读取到的数据肯定在内存空间会被释放掉,所以应该定义一个容器来储存读取到的数据
                StringBuilder sb = new StringBuilder();
                while (true) {
                        try {
                                // 读取数据,并定义一个变量接受
                                int num = is.read();
                                // 当独到\r时,程序继续读取
                                if (num == '\r') {
                                        continue;
                                }
                                // 如果\n,表示用户输入数据完成,那么把读取到的装入容器中的数据转换成字符串,并在控制台上打印出来,
                                // 程序继续循环,那么这个时候,要清空一下容器,不然下次用户输入的数据和本次的数据会添加在一起,导致数据出错
                                if (num == '\n') {
                                        // 如果读取到的数据转换成字符串后是over,那么跳出循环,程序结束
                                        if (sb.toString().equals("over")) {
                                                break;
                                        }
                                        System.out.println(sb.toString());
                                        sb.delete(0, sb.length());
                                }
                                /*
                                //如果读取到的数据既不是\r, 也不是\n, 那么就把读取到的数据添加到容器
                                //这里必须要判断一下,因为当上面的输出语句完成后,有个换行,如果不判断, 那么上面输出语句的换行\r \n就会被添加进来
                                if( num != '\r' && num != '\n')
                                sb.append((char)num);
                                */
                                
                                // 当然, 这里改成else也行,这样的话当读取到的不是\r, 也不是\n, 那么就执行下面的代码, 如果读取到\n后,
                                // 那么把数据打印出来,清空缓冲区,然后用户如果输入新的数据, 那么程序又读取新的数据去了, 而不会把输出语句的
                                // 换行\r\n字符添加到缓冲区中
                                else {
                                        sb.append((char) num);
                                }
                        } catch (IOException e) {
                                e.printStackTrace();
                        }
                }
        }
}
作者: 赵崇友    时间: 2013-5-14 18:49
xuemeng 发表于 2013-5-14 18:21
我贴上自己的笔记和代码吧 ,其实这个问题老毕有讲过!! 这个涉及到字节输入流的缓冲区读取原理了:
import ja ...

很牛,很强大……
作者: littlefoxtail    时间: 2013-5-14 20:05
xuemeng 发表于 2013-5-14 18:42
再来补充一段代码, 两端代码和注释结合着看:
import java.io.IOException;
import java.io.InputStream;

说的不是一个事吧 我看网上其他人说是:a在ASCII码中有相应的码对应,也就是说a只要八位就能表示了,read()读入一个字节的时候已经读入了a的ASCII码了,打印的时候会给其高8位补上0的,所以能正常显示a。不知道对不对
作者: xuemeng    时间: 2013-5-14 20:40
littlefoxtail 发表于 2013-5-14 20:05
说的不是一个事吧 我看网上其他人说是:a在ASCII码中有相应的码对应,也就是说a只要八位就能表示了,read ...

这么给你说吧, 你读取的是字节类型数, 但是底层会加工一下, 因为如果当你读取到的值刚好是11111111, 那么返回的是-1, 你的程序就没法往下执行了啊!!!
   我做的&运算就是给前面的加 0 啊
作者: littlefoxtail    时间: 2013-5-14 21:13
xuemeng 发表于 2013-5-14 20:40
这么给你说吧, 你读取的是字节类型数, 但是底层会加工一下, 因为如果当你读取到的值刚好是11111111, 那么 ...

我知道这个啊 但这跟我问的东西没关系啊
作者: xuemeng    时间: 2013-5-14 21:21
你不是问为什么返回的是int型吗?
作者: littlefoxtail    时间: 2013-5-14 21:48
汗!可能我没表达清楚,sorry,我的意思是比如键盘输入一个A,本来A应该是两个字节,现在调用read方法,应该只读到一个字节,现在把整个A的int给返回了




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2