五 字符集转换和乱码问题
a) 文件都是按字节存储的,即使是使用字符流对文件进行写入,最终也会转换成字节
进行存储。这时就需要将字符转换成字节,不同的字符集一般转换成的字节格式是
不同的。看下面代码
String str = "字符";
byte[] utf8 = str.getBytes("utf-8");
System.err.println(Arrays.toString(utf8));
//输出:[-27, -83, -105, -25, -84, -90]
byte[] gbk = str.getBytes("gbk");
System.err.println(Arrays.toString(gbk));
//输出:[-41, -42, -73, -5]
b) 由上面的代码可以看出,UTF-8字符集中,一个汉字占了3个字节,GBK占了两个
字节。所以如果用UTF-8转换的字节写入文件,用GBK读取是会出现乱码的情况。
建议用什么字符集写入就用什么字符集读取 。
c) 不同的字符集,一般所表示的字符是相同的,不同的是表示字符的字节。所以遇到
乱码情况,可以先以存入时的字符集,进行读取。然后再以需要的字符集进行存入。
d) 通过String实现字符集转换
String str = "字符";
//按utf-8字符集格式,将字符转换成字节
byte[] utf8 = str.getBytes("utf-8");
//按utf-8字符集格式,将字节转换成字符
str = new String(utf8, "utf-8");
//按gbk字符集格式,将字符转换成字节
byte[] gbk = str.getBytes("gbk");
//按gbk字符集格式,将字节转换成字符
str = new String(gbk, "gbk");
System.out.println(str);//输出:字符
e) 通过java.nio.Charset实现字符集转换,ByteBuffer和CharBuffer都是nio包中的类
一般作为nio通道的缓存使用
// 虚拟机中的可用字符集
SortedMap sort = Charset.availableCharsets();
for (String charsetName : sort.keySet())
System.err.println(charsetName);// 输出所有字符集名称
// 查看虚拟机默认字符集,在虚拟机启动时默认字符集确定,通常取决于底层操作系统的语言环境和字符集。
Charset charset = Charset.defaultCharset();
System.err.println(charset);// 输出:GBK
// 判断虚拟机字符集是否可用
boolean boo = Charset.isSupported("GBK");
System.err.println(boo);// 输出:true
String str = "字符";
// 注册一个字符集
Charset gbk = Charset.forName("GBK");
// 按gbk字符集格式,将字符转换成字节
ByteBuffer bb = gbk.encode(str);
// byte[] bytes = bb.array();
// 按gbk字符集格式,将字节转换成字符
CharBuffer cb = gbk.decode(bb);
System.out.println(cb);// 输出:字符
Charset utf8 = Charset.forName("utf-8");
System.out.println(utf8.decode(utf8.encode(cb)));// 输出:字符
一 File类
a)File.pathSeparator系统有关的默认路径分隔符 windows是; unix是:
b)File.separator系统有关的默认名称分隔符 windows是\,unix是/
c)mkdirs()方法可以创建路径中所有不存在的目录
public static void main(String[] args) throws IOException {
String path = "D:" + File.separator + "test.txt";
File file = new File(path);
// 文件如果不存在
if (!file.exists()) {
// 创建一个文件夹
file.mkdir();
// 删除一个文件/文件夹
if (file.exists())
file.delete();
// 创建一个文件
file.createNewFile();
}
}
c) 文件/文件夹的名字、路径
public static void main(String[] args) throws IOException {
String path = "D:" + File.separator;
File file = new File(path);
// 判断file是否是文件夹
if (file.isDirectory()) {
// 返回当前目录下所有文件\文件夹名字
String[] names = file.list();
for (String name : names) {
System.out.println(name);
}
// 返回当前目录下,每个文件\文件夹的File对象
File[] files = file.listFiles();
for (File elem : files) {//遍历名字、路径
System.out.println(elem.getName());
System.out.println(elem.getPath());
}
}
}
d) 递归目录中所有文件
public class IoTest {
/**
* 递归当前目录下所有文件
*/
public static void filePrintln(File file) throws IOException {
//遍历当前目录下所有项
for (File f : file.listFiles())
if (f.isDirectory()) //如果是一个目录
filePrintln(f);// 递归
else
System.out.println(f.getPath());
}
public static void main(String[] args) throws IOException {
String path = "D:" + File.separator ;
IoTest.filePrintln(new File(path));
}
}
e) file.listFiles(filter)方法可以传一个FilenameFilter接口的实现类,这个接口只有一个方法
accept(),提供该参数的目的是listFiles()会回调accept(),进而决定哪些文件是所需的。
f)这是一个典型的策略模式,listFiles()可以根据FilenameFilter的不同实现类,进行文件
的筛选。
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.regex.Pattern;
public class IoTest {
public static class FileName implements FilenameFilter{
private Pattern pattern;
/**
* 构造器
* @param regex 传入一个正则表达式,指定为一个字符串
*/
public FileName(String regex){
//regex必须先编译成这个类的一个实例,由此可以产生一个匹配器,可以匹配任何字符序列
this.pattern = Pattern.compile(regex);
}
@Override
public boolean accept(File dir, String name) {
//通过正则表示匹配器,对每个文件名name进行筛选是否符合条件
return pattern.matcher(name).matches();
}
}
public static void main(String[] args) throws IOException {
String path = "D:" + File.separator ;
File file = new File(path);
//匹配小写字母命名的java文件
File[] files = file.listFiles(new IoTest.FileName("[a-z]+\\.[Jj]ava"));
for(File f : files){
System.out.println(f.getPath());
}
}
}
二 Paths类、Files类、Path接口
a)以上的类或接口都是Java SE7中添加进来的,在java.nio.file包中
b)Paths类只有两个方法,主要作用是生成Path对象
c)Path对象跟其名字意义是一样的,只涉及对路径的操作
public static void main(String[] args) throws IOException {
String p = "D:" + File.separator + "file" + File.separator + "TEXT.txt";
// 初始化一个Path对象
Path path = Paths.get(p);
//或者,功能等同于上面
path = Paths.get("D:","file","TEXT.txt");
//方法参数Paths.get(String first, String... more)
// Path对象——>File对象
File file = path.toFile();
// File对象——>Path对象
path = file.toPath();
// 文件路径
System.out.println(path);
// 输出:D:\file\TEXT.txt
// 文件名
System.out.println(path.getFileName());
// 输出:TEXT.txt
// 父路径,没有时返回null
System.out.println(path.getParent());
// 输出:D:\file
//跟路径
System.out.println(path.getRoot());
//输出:D:\
//移除.和..等冗余的路径元素
path.normalize();
//创建一个同级的Path对象
Path path2 = path.resolveSibling("TEXT2.txt");
System.out.println(path2);
//输出:D:\file\TEXT2.txt
//可以在目录下创建文件Path对象
Path path3 = path.resolve("TEXT2.txt");
System.out.println(path3);
//输出:D:\file\TEXT.txt\TEXT2.txt
}
d)Files只涉及对文件的操作,但是基本都是通过路径Path这个媒介
public static void main(String[] args) throws IOException {
// 初始化一个Path对象
Path path = Paths.get("D:", "file", "TEXT.txt");
// 文件是否存在,第二个参数貌似还没有什么实际意义
Files.exists(path, LinkOption.NOFOLLOW_LINKS);
// 是否目录
Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS);
// 按字节度量尺寸,只适用文件
Files.size(path);
//读取文件的属性,可以得到文件的创建、修改时间等
BasicFileAttributes ba = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
// 删除文件
Files.deleteIfExists(path);
}
e)Files类可以很方便的处理中等长度的文本文件
public static void main(String[] args) throws IOException {
// 初始化一个Path对象
Path path = Paths.get("D:", "file", "TEXT.txt");
// 读取文件中所有内容
byte[] bytes = Files.readAllBytes(path);
// 按照行进行读入
List lines = Files.readAllLines(path, Charset.forName("GBK"));
// 写入内容到文件,第二个参数貌似有问题,不太好用
Files.write(path, "sss".getBytes(), StandardOpenOption.WRITE);
// 在文件后面追加内容
Files.write(path, "追加".getBytes(), StandardOpenOption.APPEND);
// 复制字节输入流到文件
Files.copy(System.in, path, StandardCopyOption.REPLACE_EXISTING);
// 可以生成字节流、字符流
InputStream in = Files.newInputStream(path, StandardOpenOption.READ);
BufferedReader bf = Files.newBufferedReader(path, Charset.forName("GBK"));
}
f)通过Files创建目录或者文件不成功,记下以后解决
public static void main(String[] args) throws IOException {
// 初始化一个Path对象
Path path = Paths.get("D:","file","TEXTa.txt");
//创建新目录
//文件属性集合
Set set = EnumSet.allOf(PosixFilePermission.class);
// 这里会抛错,貌似windows不支持posix
Files.createDirectories(path, PosixFilePermissions.asFileAttribute(set));
//创建一个文件,同上面一样执行无法通过
Files.createFile(path, PosixFilePermissions.asFileAttribute(set));
}
g)Files可以很方便的复制文件
public static void main(String[] args) throws IOException {
// 初始化一个Path对象
Path fromPath = Paths.get("D:","file","TEXT.txt");
Path pathTo = Paths.get("D:","file","aaa","TEXT.txt");
// 将文件从一个位置复制到另一个位置
Files.copy(fromPath, pathTo, StandardCopyOption.REPLACE_EXISTING);
//复制并删除源文件
Files.move(fromPath, pathTo, StandardCopyOption.REPLACE_EXISTING);
//REPLACE_EXISTING 取代现有文件
//COPY_ATTRIBUTES复制所有文件属性
//ATOMIC_MOVE 操作原子性,成功||什么都没做
}
h)Files 对文件进行遍历,在遍历所有目录及其子目录时功能很强大
public static void main(String[] args) throws IOException {
// 初始化一个Path对象
Path path = Paths.get("D:", "file");
// 得到当前目录下所有文件路径。这种try的写法,1.7中新功能,可以自动关闭流
try(DirectoryStream dir = Files.newDirectoryStream(path)){
for (Path entry : dir)
System.out.println(entry);
}
// 通过glob模式对文件进行过滤
DirectoryStream dirGlob = Files.newDirectoryStream(path, "*.java");
// 遍历所有目录及其子目录,后台也是通过递归来实现
Files.walkFileTree(path, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// 输出所有文件路径
System.out.println(file);
return super.visitFile(file, attrs);
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
// 输出所有目录路径
System.out.println(dir);
return super.preVisitDirectory(dir, attrs);
}
});
}
讲解一下上面的最后一个方法
//其中第二个参数FileVisitor有四个方法
//方法1:在遇到文件时调用(非目录)
FileVisitResult visitFile(T file, BasicFileAttributes attrs)
//方法2:在处理一个目录之前调用
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
// 方法3:在处理一个目录之后调用
FileVisitResult postVisitDirectory(T dir, IOException exc)
//方法4:在试图处理一个文件或目录时发生错误,例如没有权限打开目录
FileVisitResult visitFileFailed(T file, IOException exc)
//在执行上面4个步骤时,都会返回FileVisitResult对象,可以指定是否希望执行下面的操作
//1:继续访问下一个文件
FileVisitResult.CONTINUE
//2:继续访问,但是不访问这个目录下的任何项
FileVisitResult.SKIP_SIBLINGS
//3:终止访问
FileVisitResult.TERMINATE