添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

最近在做一个大型Java项目的移植工作,遇到大量字符编码问题,传统的Windows平台主要使用GBK编码,而linux上面为了保持字符的统一性,使用UTF8编码,这个就导致从windows迁移过来的配置文件和资源文件乱码。之前我写过一篇 《字符编码浅析》 ,简单介绍了下这几种编码的不同。

这里我首先解决Linux平台下的乱码问题:如果只是要看一个不同编码的文件,我们可以用vim打开,然后:set fileencoding=utf8  gbk….的方式改变查看文档的编码。如果我们想转换文档编码形式,那么使用以下的命令(将一个GBK编码的文件转换成UTF-8编码):

$enconv -L zh_CN -x UTF-8 filename

文档编码转换完成,这里我从Java的角度阐述如果读写。因为网上关于Java读写的博文足够多,我主要来总结一下:

Java 的IO有两种流的类型:那就是字节流(InputStream、OutputStream)和字符流(Reader、Writer)。我们在读写中文的时候务必使用字符流,因为中文是2个字节组成,如果读取一个字节必然出现问题。

InputStream常用的类是如下,直接对读数据的是节点流,浅色的是处理流,依靠InputStream对数据进行读写,拥有更强大的功能。

OutputStream常用类如下,直接写的是节点流,浅色的是处理流。

二进制读写的时候要使用Stream的方式进行,而读写字符就使用Reader、Writer。

总结一下两种类型,节点流为以下表格,通过这些对象直接对数据源进行读写。

FileReader、FileWriter FileInputStream、FileOutputStream Memory Array CharArrayReader、CharArrayWriter ByteArrayInputStream、ByteArrayOutputStream Memory String StringReader、StringWriter PipedReader、PipedWriter PipedInputStream、PipedOutputStream
  • BufferedReader和BufferedWriter来读写文件 ( 字符读写,只要不是二进制文件,都可以用字符,字节是给二进制文件使用的 ) ,这种方式读写效率最好( 而且可以选择想要的字符集 ),这个主要将数据先缓冲起来,然后一起写入或者读取出来。经常使用的是readLine()方法,一次读取一行数据。其中我们可以根据装饰者模式修改BufferReader和BufferWriter中的类,比如new InputStreamReader(new FileInputStream(…), StandardCharsets.UTF_8),通过这种方式,就可以以UTF-8的方式读取文件,或者以new OutputStreamWriter(new FileOutputStream(“test.txt”), “UTF-8”)写文件
  • 使用方式 FileReader->BufferedReader->readLine、FileWriter->BufferedWriter->write

    private static void read() throws FileNotFoundException, IOException { File file = new File("a.txt");// 指定要读取的文件 // 获得该文件的缓冲输入流 BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); String line = "";// 用来保存每次读取一行的内容 while ((line = bufferedReader.readLine()) != null) { System.out.println(line); bufferedReader.close();// 关闭输入流 private static void write() throws IOException { File file = new File("a.txt");// 指定要写入的文件 if (!file.exists()) {// 如果文件不存在则创建 file.createNewFile(); // 获取该文件的缓冲输出流 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); bufferedWriter.write("你好世界"); bufferedWriter.newLine(); bufferedWriter.write("hello world"); bufferedWriter.flush();// 清空缓冲区 bufferedWriter.close();// 关闭输出流

    与他类似的就是BufferedInputStream和BufferedOuputStream,用这两个读取二进制文件,不同的在于读取的是二进制文件。

    FileInputStream ——>BufferedInputStream -> read、FileOutputStream->BufferedOutputStream->write

    // 指定要读取文件的缓冲输入字节流 BufferedInputStream in = new BufferedInputStream(new FileInputStream("a.jpg")); File file = new File("b.jpg"); if (file != null) { file.createNewFile(); // 指定要写入文件的缓冲输出字节流 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); byte[] bb = new byte[1024];// 用来存储每次读取到的字节数组 int n;// 每次读取到的字节数组的长度 while ((n = in.read(bb)) != -1) { out.write(bb, 0, n);// 写入到输出流 out.close();// 关闭流 in.close();

    有的时候我们还要使用File对象,这个时候可以查看 FileInputStream、FileOutputStream、FileReader、FileWriter 的构造方法。类似于FileReader(File file)。

    字符流到字节流的转换也很重要,类似于:

    InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = null; try { s = br.readLine(); OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("....")); osw.write("mircosoftibmsunapplehp");

    Java中io类太多了,有时候容易眼花,不知道用哪个,这个时候只要记住字符流和字节流的异同,合理使用类,并查询java api即可。

    http://blog.csdn.net/dangnianmingyue_gg/article/details/47361323

    最近在编写字符设备驱动,在使用场景上面存在不同的实现:阻塞I/O,非阻塞I/O和异步通知三种,之前都是朦朦胧胧知道三者区别,而没有认真的学习三者不同,这这篇文章中我会仔细的比较三者的区别。

    设备的阻塞访问

    指的是执行设备操作时如果无法回去资源,那么挂起进程,挂起的进程进入休眠状态,kernel将其从rq中移出,直到条件满足,示例代码:

    char buf; fd = open("/dev/ttyS1",O_RDWR); res = read(fd,&buf,1); if(res == 1) printf("%c\n",buf);

    阻塞访问的优点就是节省CPU资源,资源没有得到满足,那么挂起即可,进程进入休眠状态,将cpu资源让给其他进程(当然如果进入休眠,那么当资源满足,我们需要一种方式唤醒这个休眠进程,可以使用信号)。阻塞I/O 一般使用等待队列来实现。

    设备的非阻塞访问

    指的是如果得不到资源,那么立即返回,并不挂起这个进程,我们可以不断的轮训这个设备,直到这个设备满足资源。

    char buf; fd = open("/dev/ttyS1",O_RDWR | O_NONBLOCK); while(read(fd,&buf,1)!= 1) printf("%c\n",buf);

    非阻塞访问的最大缺点是因为要不停的轮训设备,会浪费大量的cpu时间,但是我们可以借助sigaction通过异步通知的方式访问串口提高cpu利用率,说到非阻塞,通常会用到select() poll() 系统调用,这两个调用最后都会调用到驱动设备中的poll函数。

    poll函数原型是unsigned int (* poll)(struct file *filp,struct poll_table *wait),在驱动里面,调用poll_wait() 向poll_table注册等待队列,当字符设备中存在数据时,return POLLIN,POLLRDNORM,POLLOUT。这里我们要注意: 设备驱动的poll函数本身并不会阻塞,但是poll和select()系统调用会阻塞等待文件描述符集合中的至少一个可访问或者超时。

    异步通知的全程是“信号驱动的异步I/O”,也就是说一旦设备准备就绪,主动通知应用程序,这样应用程序根本就不需要查询设备状态。

    我们可以使用信号来通知设备处理,其中STDIN_FILENO是int类型,不同于STDIN 的FILE * 类型,使用signal添加信号处理函数,使用fcntl()设置SIGIO信号被STDIN_FILENO接收,之后使用O_ASYNC 使得IO具有异步特性。

    #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #define MAX_LEN 100 void input_handler(int num) char data[MAX_LEN]; int len; len = read(STDIN_FILENO,&data,MAX_LEN); data[len] = 0; printf("input:%s\n",data); int main() int oflags; signal(SIGIO,input_handler); fcntl(STDIN_FILENO,F_SETOWN,getpid()); oflags = fcntl(STDIN_FILENO,F_GETFL); fcntl(STDIN_FILENO,F_SETFL,oflags | O_ASYNC); while(1);

    [1] UNIX 高级编程

    [2] Linux 设备驱动开发

    [3] http://stackoverflow.com/questions/15102992/what-is-the-difference-between-stdin-and-stdin-fileno

    [4] http://www.c4learn.com/c-programming/c-reference/fread-function/

    while (rp->rio_cnt <= 0) { /* refill if buf is empty */ rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, sizeof(rp->rio_buf)); if (rp->rio_cnt < 0) { if (errno != EINTR) /* interrupted by sig handler return */ return -1; else if (rp->rio_cnt == 0) /* EOF */ return 0; rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */ /* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */ cnt = n; if (rp->rio_cnt < n) cnt = rp->rio_cnt; memcpy(usrbuf, rp->rio_bufptr, cnt); rp->rio_bufptr += cnt; rp->rio_cnt -= cnt; return cnt; ssize_t rio_readn(int fd, void *usrbuf, size_t n) size_t nleft = n; ssize_t nread; char *bufp = usrbuf; while (nleft > 0) { if ((nread = read(fd, bufp, nleft)) < 0) { if (errno == EINTR) /* interrupted by sig handler return */ nread = 0; /* and call read() again */ return -1; /* errno set by read() */ else if (nread == 0) break; /* EOF */ nleft -= nread; bufp += nread; return (n - nleft); /* return >= 0 */

    我们看到read返回值是有可能小于要求sizoof(buffer)的值,这种现象在kernel character device与network中非常普遍!

    另外,read()应该也要处理用户发来的信号,如果遇到sigal信号,要使得返回值置0,并使得buffer指针的移动。

    所以我们要注意,并进行比较。

    比如在 http://www.lizhaozhong.info/archives/1066 中read也是实现了类似的思想。

    维护一个len与count的关系,每次调用read函数都是确保len减去一个count大小的buffer,直到len<count,然后len赋值为count。

    write函数也是类似,只不过len与count之间主要做加法。

  • Springboot 拦截器的坑 WebMvcConfigurationSupport 失效 - 10,276 views
  • JDK8版本过高引起MySQL连接失败:javax.net.ssl.SSLHandshakeException: No appropriate protocol - 5,898 views
  • 关于我 - 4,496 views
  • 从0构建一个基于BananaPi的OpenWrt系统 - 4,368 views
  • DRAM页映射到BANK中的两种形式 - 4,266 views
  • Configure Manager Rules - 3,481 views
  • SYSTEM DESIGN - 3,352 views
  • 内存管理子系统学习 - 3,292 views
  • kdump 在Debian/Fedora/CentOS下的配置 - 3,246 views
  • VFS子系统学习 - 3,194 views
  •