java(十一)IO流
深度遍歷演示:
package day22;
import java.io.File;
public class FileTest {
/**
* 需求:對指定目錄進行所有內容的列出。(包含子目錄,前面學的都只能列出當前目錄內容,子目錄不行)
* 也可以理解為深度遍歷。
*/
public static void main(String[] args) {
File dir=new File("e:\\javatest");
listAll_2(dir,0);
}
public static void listAll_1(File dir) {
System.out.println("dir:"+dir.getAbsolutePath());
//獲取指定目錄下當前的所有文件或者文件對象
File[] files=dir.listFiles();
for(int x=0;x<files.length;x++){
if(files[x].isDirectory()){
listAll_1(files[x]); //這就是遞歸
}
else
System.out.println("file:"+files[x].getAbsolutePath());
}
}
public static void listAll_2(File dir, int level) { //定義一個計數器,記錄當前文件的層級,從而決定前面加幾個空格,為什麽不在方法中定義?這就和遞歸有關了。
System.out.println(getSpace(level)+dir.getName());
level++;
File[] files=dir.listFiles();
for(int x=0;x<files.length;x++){
if(files[x].isDirectory()){
listAll_2(files[x],level); //這就是遞歸
}
else
System.out.println(getSpace(level)+files[x].getName());
}
}
private static String getSpace(int level) {
StringBuilder sb=new StringBuilder();
for(int x=0;x<level;x++){
sb.append(" ");
}
return sb.toString();
}
}
上面代碼中涉及到了遞歸,接下來就學習一下遞歸的知識。
遞歸: 函數自身直接或者間接地調用到了自身。
* 什麽時候使用?
* 一個功能在被重復使用,並且每次使用時,參與運算的結果和上一次調用有關。
* 這時可以用遞歸來解決問題。
* 註意:1.遞歸必須明確條件,否則容易棧溢出
* 2.遞歸的次數別太多,否則棧溢出異常。
package day22;
public class DiGuiDemo {
/**
* 遞歸演示。
*/
public static void main(String[] args) {
/*
* 遞歸:函數自身或者直接或者間接調用到了自身
* 什麽時候使用?
* 一個功能在被重復使用,並且每次使用時,參與運算的結果和上一次調用有關。
* 這時可以用遞歸來解決問題
* 註意:1.遞歸必須明確條件,否則容易棧溢出
* 2.遞歸的次數別太多,否則棧溢出異常。
*/
show();
}
public static void show(){
toBin(6); //把一個數轉換成二進制
}
//直接調用自身
public static void toBin(int i) {
if(i>0){
toBin(i/2);
System.out.print(i%2);
}
}
//間接調用自身
public static void method(){
bala();
}
public static void bala(){
method();
}
}
Properties集合
* Map
* |--Hashtable;
* |--Properties;
*
* Properties集合特點:
* 1.該集合中的鍵和值都是字符串。
* 2.集合中的數據可以保存到流中,或者從流中獲取出來。
*
* 通常該集合用於操作以鍵值對形式存在的配置文件。
package day22;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//properties集合的基本功能存和取演示
propertiesDemo();
//properties集合和流對象相結合的演示
propertiesDemo2();
//持久化,將集合的數據存儲到文件中
propertiesDemo3();
//把文件中的數據讀取到集合中。
propertiesDemo4();
//修改文件的配置信息
propertiesDemo5();
}
public static void propertiesDemo() {
//創建一個Properties集合
Properties prop=new Properties();
//存儲元素
prop.setProperty("zhangsan", "30");
prop.setProperty("lisi", "32");
prop.setProperty("wangwu", "36");
prop.setProperty("zhaoliu", "20");
//修改元素
prop.setProperty("wangwu", "26");
//取出所有元素
Set<String> names=prop.stringPropertyNames();
for(String name:names){
String value=prop.getProperty(name);
System.out.println(name+":"+value);
}
}
public static void propertiesDemo2() {
Properties prop=new Properties();
prop.setProperty("zhangsan", "30");
prop.setProperty("lisi", "32");
prop.setProperty("wangwu", "36");
prop.setProperty("zhaoliu", "20");
prop.list(System.out); //list方法,列出集合中的鍵和值,一般調試時用。
}
public static void propertiesDemo3() throws IOException{
Properties prop=new Properties();
prop.setProperty("zhangsan", "30");
prop.setProperty("lisi", "32");
prop.setProperty("wangwu", "36");
prop.setProperty("zhaoliu", "20");
// 持久化
//需求:集合中的這些字符串鍵值信息方法一結束就會消失,想要將它們持久化存儲到文件中。
// 就要用到store方法,這需要關聯輸出流,所以創建一個。
FileOutputStream fos=new FileOutputStream("info.txt");
// 將集合數據存儲到文件中,使用store方法。
prop.store(fos, "name+age");
fos.close();
}
public static void propertiesDemo4() throws IOException{
Properties prop=new Properties();
//需求:與Demo3相反,這次要把硬盤中的數據讀到集合中。
//註意:文件中的數據必須是鍵值對。
FileInputStream fis=new FileInputStream("info.txt");
//使用load方法。
prop.load(fis);
fis.close();
prop.list(System.out); //這個list就是驗證一下確實讀取到集合中了。
}
public static void propertiesDemo5() throws IOException{
/*
* 需求:對已有的配置文件信息進行修改。
* 步驟:先讀取這個文件,並將這個文件的鍵值數據存儲到集合中。
* 再通過集合對數據進行修改,最後通過流將修改後的數據存儲到文件中。
*/
File file=new File("info.txt");
if(!file.exists()){
file.createNewFile();
}
FileReader fr=new FileReader("info.txt");
Properties prop=new Properties();
prop.load(fr);
prop.setProperty("wangwu", "16");
FileWriter fw=new FileWriter(file);
prop.store(fw, "");
fr.close();
fw.close();
prop.list(System.out);
}
}
properties的一個練習:
package day22;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
/**
* 需求:定義一個功能,獲取一個應用程序運行的次數,若超過5次,
* 給出 “使用次數已到,請註冊” 的提示,並不要再運行程序。
*
* 思路:1.得有一個計數器,每次程序啟動就計數一次,並且是在原有的基礎上計數。
* 2.計數器就是一個變量。突然冒出一個想法,程序啟動時進行計數,計數器必須存在於內存並進行運算。
* 可是程序一結束,計數器不就消失了嗎?再次啟動程序,計數器不就重置了嘛,但我們需要多次啟動同一個
* 應用程序,啟動的是同一個計數器,這就需要計數器的生命周期變長,從內存存儲到硬盤文件中。
* 3.如何使用這個計數器呢?
* 首先,程序啟動時,應該先讀取這個用於記錄計數器信息的配置文件,獲取上一次計數器次數,
* 其次,對該次數就行自增,並且自增後的次數要重新存儲到配置文件中。
* 4.文件中的信息該如何進行存儲呢?
* 直接存儲次數值可以,但不明確該數據的含義,所以起名字就很重要了。這就有了名字和值得對應,就要用鍵值對。
* 可是映射關系map搞定,又要讀取硬盤上的數據,所以map+IO=Properties。
* @throws IOException
*/
public static void main(String[] args) throws IOException {
getAppCount();
}
public static void getAppCount() throws IOException{
//將配置文件封裝成對象
File confile=new File("count.properties.txt");
if(!confile.exists())
confile.createNewFile();
FileInputStream fis=new FileInputStream(confile);
Properties prop=new Properties();
prop.load(fis);
//從集合中通過鍵獲取次數
String value=prop.getProperty("time");
//定義計數器,記錄獲取到的次數
int count=0;
if(value!=null){
count=Integer.parseInt(value);
if(count>=5){
//System.out.println("使用次數已到,請註冊");
//return; //這樣寫不好,函數結束了,其他程序還在運行。
throw new RuntimeException("使用次數已到,請註冊");
}
}
count++;
//將改變後的次數重新存儲到集合中
prop.setProperty("time", count+"");
FileOutputStream fos=new FileOutputStream(confile);
prop.store(fos, "");
fos.close();
fis.close();
}
}
練習:
package day22;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Test {
/**
* 需求:獲取指定目錄下,指定擴展名的文件(包含子目錄中的),
* 這些文件的絕對路徑寫入到一個文本文件中。
* 簡單說,就是建立一個指定擴展名的文件的列表。
*
* 思路:1.必須進行深度遍歷。
* 2.要在遍歷的過程中進行過濾。將符合條件的內容都存儲到容器中。
* 3.對容器中的內容進行遍歷並將絕對路徑寫入到文件中。
*/
public static void main(String[] args) {
File dir=new File("e://javatest");
FilenameFilter filter=new FilenameFilter(){
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
};
List<File> list=new ArrayList<File>();
getFiles(dir,filter,list);
File destFile=new File(dir,"javalist.txt");
writeToFile(list,destFile);
}
public static void getFiles(File dir,FilenameFilter filter,List<File> list){
/*
* 對指定目錄的內容進行深度遍歷,並按照制定過濾器進行過濾,
* 將過濾後的內容存儲到指定容器list中。
*/
File[] files=dir.listFiles();
for(File file:files){
if(file.isDirectory()){
//遞歸
getFiles(file,filter,list);
}
else{
//對遍歷到的文件進行過濾。將符合條件的File對象存儲到List集合中
if(filter.accept(dir,file.getName())){
list.add(file);
}
}
}
}
public static void writeToFile(List<File> list,File destFile) {
BufferedWriter bufw=null;
try {
bufw=new BufferedWriter(new FileWriter(destFile));
for(File file:list){
bufw.write(file.getAbsolutePath());
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
throw new RuntimeException("寫入失敗");
} finally{
if(bufw!=null)
try {
bufw.close();
} catch (IOException e) {
throw new RuntimeException("關閉失敗");
}
}
}
}
IO包中的其他類:
打印流:PrintWriter和PrintStream。可以直接操作輸入流和文件。
1.它提供了打印方法可以對多種數據類型值進行打印,並保持數據的表示形式。
2.它不拋IOException.
3.他的構造函數接收三種類型的值:
a.字符串路徑
b.File對象
c.字節輸出流
序列流:SequenceInputStream,對多個流進行合並。
練習:文件切割器和文件合並器
package day22;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class splitFileDemo {
/**
* 練習:文件切割器
* @throws IOException
*/
private static final int SIZE = 1024*1024; //這就是1M。
public static void main(String[] args) throws IOException {
File file=new File("e:\\javatest\\eclipse\\HelloJava\\LoginScreenLoop.mp3");
splitFile(file);
}
public static void splitFile(File file) throws IOException{
//用讀取流關聯源文件
FileInputStream fis=new FileInputStream(file);
//定義一個1M的自定義緩沖區
byte[] buf=new byte[SIZE]; //定義一個指定大小的緩沖區,待會就把文件要切成這麽大的。
//創建目的
FileOutputStream fos=null;
int len=0;
int count=1;
/*
* 切割文件時,必須要記錄被切割文件的名稱和切割出來的碎片文件的個數,以方便合並。
* 這個信息為了進行簡單的描述,使用鍵值對的方式,用到了properties對象。
*/
Properties prop=new Properties();
File dir=new File("e:\\javatest\\eclipse\\HelloJava\\partfiles");
if(!dir.exists()){
dir.mkdirs();
}
while((len=fis.read(buf))!=-1){
fos=new FileOutputStream(new File(dir,(count++)+".part")); //碎片文件文件名要註意,類型也別直接就寫txt
fos.write(buf,0,len);
fos.close();
}
fos.close();
fis.close();
//將被切割文件的信息保存到prop中。
prop.setProperty("partcount", count+"");
prop.setProperty("filename", file.getName());
//將prop的數據存儲到文件中。
fos=new FileOutputStream(new File(dir,count+".properties"));
prop.store(fos, "save file information");
}
}
package day22;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
public class MergerDemo {
/**
* 文件合並器
*
* 文件切割器切了之後肯定還要合並嘛
* @throws IOException
*/
public static void main(String[] args) throws IOException {
File dir=new File("partfiles");
mergeFile(dir);
}
public static void mergeFile(File dir) throws IOException{
//先拿被切割文件的配置信息。不知道這個文件叫啥,只知道後綴名是.properties。所以要用過濾器
File[] files=dir.listFiles(new SuffixFilter(".properties"));
if(files.length!=1)
throw new RuntimeException(dir+",該目錄下沒有properties擴展名的文件或者不唯一");
//記錄配置文件對象,並獲取文件中的信息
File confile=files[0];
Properties prop=new Properties();
FileInputStream fis=new FileInputStream(confile);
prop.load(fis);
String filename=prop.getProperty("filename");
int count =Integer.parseInt(prop.getProperty("partcount"));
//獲取該目錄下的碎片文件
File[] partFiles=dir.listFiles(new SuffixFilter(".part"));
if(partFiles.length!=count-1){
throw new RuntimeException("碎片文件不符合要求");
}
//將碎片文件和流對象關聯並存儲到集合中
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
for(int x=1;x<=partFiles.length;x++){
al.add(new FileInputStream(new File(dir,x+".part")));
}
//將多個流合並成一個序列流
Enumeration<FileInputStream> en=Collections.enumeration(al);
SequenceInputStream sis=new SequenceInputStream(en);
FileOutputStream fos=new FileOutputStream(new File(dir,filename));
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
IO包中的其他類:
操作基本數據類型:
DataInputStream和DataOutputStream
操作字節數組:
ByteArrayInputStream和ByteArrayOutputStream
操作字符數組:
CharArrayReader和CharArrayWriter
操作字符串:
StringReader和StrinWriter
DataInputStream和DataOutputStream:
package day22;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataStreamDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
writeData();
readData();
}
public static void readData() throws IOException {
DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
String str=dis.readUTF();
System.out.println(str);
}
public static void writeData() throws IOException {
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeUTF("你好");
dos.close();
}
}
ByteArrayInputStream和ByteArrayOutputStream:
這兩個類關閉無效,關閉後還可以調用且不拋異常。因為此類中的方法不調用底層資源,只是在內存中操作一個數組。
package day22;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class ByteArrayStream {
/**
* @param args
*/
public static void main(String[] args) {
ByteArrayInputStream bis=new ByteArrayInputStream("abcdefg".getBytes());
ByteArrayOutputStream bos=new ByteArrayOutputStream();
int ch=0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
//bis這裏不用關 ,關了也沒用。
System.out.println(bos.toString());
}
}
CharArrayReader和CharArrayWriter
StringReader和StrinWriter
這四個和ByteArrayInputStream,ByteArrayOutputStream類似,類比學習即可。
編碼表:
ASCII:美國標準信息交換碼 用一個字節的7位可以表示。所以0開頭的一般是美國碼表。
ISO8859-1:拉丁碼表。歐洲碼表,用一個字節的8位表示。
GB2312:中國的中文編碼表
GBK:中國的中文編碼表的升級,融合了更多的中文文字符號。
Unicode:國際標準碼,融合了多種文字。所有文字都用兩個字節來表示,java語言使用就是unicode。
UTF-8:最多用三個字節來表示一個字符。
package day22;
import java.io.IOException;
public class EncodeDemo {
/**
* 字符串--> 字節數組 編碼
* 字節數組--> 字符串 解碼
* @throws IOException
*
*/
public static void main(String[] args) throws IOException {
String str="你好";
//編碼
byte[] buf=str.getBytes("GBK");
//你好對應的編碼(GBK): -60 -29 -70 -61
//你好對應的編碼(UTF-8): -28 -67 -96 -27 -91 -67
printBytes(buf);
//解碼
String s1=new String(buf,"GBK");
System.out.println("s1="+s1);
}
public static void printBytes(byte[] buf) {
for(Byte b:buf){
System.out.print(b+" ");
}
}
}
一個練習:
package day22;
import java.io.IOException;
public class EncodeTest {
/**
* 在java中,字符串“abcd”與字符串“ab你好”的長度是一樣的,都是四個字符。
* 但對應的字節數不同,因為一個漢字占兩個字節。
* 定義一個方法,按照最大字節數來去子串。
* 如:對於“ab你好”,如果取三個字節,那麽子串就是ab與“你”字的半個
* 那麽半個就要舍棄。如果取四個字節就是“ab你”,取五個就是“ab你”。
* @throws IOException
*/
public static void main(String[] args) throws IOException {
String str="ab你好cd謝謝";
int len=str.getBytes("GBK").length;
for(int x=0;x<len;x++){
System.out.println("截取"+(x+1)+"個字節的結果是:"+cutStringByU8Byte(str,x+1));
}
}
//用U8碼表做的方法
public static String cutStringByU8Byte(String str, int len) throws IOException {
byte[] buf=str.getBytes("UTF-8");
int count=0;
for(int x=len-1;x>=0;x--){
if(buf[x]<0)
count++;
else
break;
}
if(count%3==0)
return new String(buf,0,len,"utf-8");
else if(count%3==1)
return new String(buf,0,len-1,"utf-8");
else
return new String(buf,0,len-2,"utf-8");
}
//用GBK做的方法
public static String cutStringByByte(String str,int len) throws IOException{
byte[] buf=str.getBytes("gbk");
int count=0;
for(int x=len-1;x>=0;x--){
if(buf[x]<0)
count++;
else
break;
}
if(count%2==0)
return new String(buf,0,len);
else
return new String(buf,0,len-1);
}
}
本文出自 “12946849” 博客,請務必保留此出處http://12956849.blog.51cto.com/12946849/1949953
java(十一)IO流