最近在做一个大型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