1. 字符流
當(dāng)使用字節(jié)流讀取文本文件時(shí),可能會(huì)有一個(gè)小問題。就是遇到中文字符時(shí),可能不會(huì)顯示完整的字符,那是因?yàn)橐粋€(gè)中文字符可能占用多個(gè)字節(jié)存儲(chǔ)。所以Java提供一些字符流類,以字符為單位讀寫數(shù)據(jù),專門用于處理文本文件。
1.1 字符輸入流【Reader】
java.io.Reader
抽象類是表示用于讀取字符流的所有類的超類,可以讀取字符信息到內(nèi)存中。它定義了字符輸入流的基本共性功能方法。
-
public void close()
:關(guān)閉此流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。 -
public int read()
: 從輸入流讀取一個(gè)字符。 -
public int read(char[] cbuf)
: 從輸入流中讀取一些字符,并將它們存儲(chǔ)到字符數(shù)組 cbuf中 。
1.2 FileReader類
java.io.FileReader
類是讀取字符文件的便利類。構(gòu)造時(shí)使用系統(tǒng)默認(rèn)的字符編碼和默認(rèn)字節(jié)緩沖區(qū)。
小貼士:
字符編碼:字節(jié)與字符的對應(yīng)規(guī)則。Windows系統(tǒng)的中文編碼默認(rèn)是GBK編碼表。
idea中UTF-8
字節(jié)緩沖區(qū):一個(gè)字節(jié)數(shù)組,用來臨時(shí)存儲(chǔ)字節(jié)數(shù)據(jù)。
構(gòu)造方法
-
FileReader(File file)
: 創(chuàng)建一個(gè)新的 FileReader ,給定要讀取的File對象。 -
FileReader(String fileName)
: 創(chuàng)建一個(gè)新的 FileReader ,給定要讀取的文件的名稱。
當(dāng)你創(chuàng)建一個(gè)流對象時(shí),必須傳入一個(gè)文件路徑。類似于FileInputStream 。
- 構(gòu)造舉例,代碼如下:
public class FileReaderConstructor throws IOException{
public static void main(String[] args) {
// 使用File對象創(chuàng)建流對象
File file = new File("a.txt");
FileReader fr = new FileReader(file);
// 使用文件名稱創(chuàng)建流對象
FileReader fr = new FileReader("b.txt");
}
}
讀取字符數(shù)據(jù)
-
讀取字符:
read
方法,每次可以讀取一個(gè)字符的數(shù)據(jù),提升為int類型,讀取到文件末尾,返回-1
,循環(huán)讀取,代碼使用演示:
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileReader fr = new FileReader("read.txt");
// 定義變量,保存數(shù)據(jù)
int b ;
// 循環(huán)讀取
while ((b = fr.read())!=-1) {
System.out.println((char)b);
}
// 關(guān)閉資源
fr.close();
}
}
輸出結(jié)果:
我
是
程
序
員
小貼士:雖然讀取了一個(gè)字符,但是會(huì)自動(dòng)提升為int類型。
-
使用字符數(shù)組讀取:
read(char[] cbuf)
,每次讀取b的長度個(gè)字符到數(shù)組中,返回讀取到的有效字符個(gè)數(shù),讀取到末尾時(shí),返回-1
,代碼使用演示:
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileReader fr = new FileReader("read.txt");
// 定義變量,保存有效字符個(gè)數(shù)
int len ;
// 定義字符數(shù)組,作為裝字符數(shù)據(jù)的容器
char[] cbuf = new char[2];
// 循環(huán)讀取
while ((len = fr.read(cbuf))!=-1) {
System.out.println(new String(cbuf));
}
// 關(guān)閉資源
fr.close();
}
}
輸出結(jié)果:
我是
程序
員序
獲取有效的字符改進(jìn),代碼使用演示:
public class FISRead {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileReader fr = new FileReader("read.txt");
// 定義變量,保存有效字符個(gè)數(shù)
int len ;
// 定義字符數(shù)組,作為裝字符數(shù)據(jù)的容器
char[] cbuf = new char[2];
// 循環(huán)讀取
while ((len = fr.read(cbuf))!=-1) {
System.out.println(new String(cbuf,0,len));
}
// 關(guān)閉資源
fr.close();
}
}
輸出結(jié)果:
我是
程序
員
1.3 字符輸出流【W(wǎng)riter】
java.io.Writer
抽象類是表示用于寫出字符流的所有類的超類,將指定的字符信息寫出到目的地。它定義了字節(jié)輸出流的基本共性功能方法。
-
void write(int c)
寫入單個(gè)字符。 -
void write(char[] cbuf)
寫入字符數(shù)組。 -
abstract void write(char[] cbuf, int off, int len)
寫入字符數(shù)組的某一部分,off數(shù)組的開始索引,len寫的字符個(gè)數(shù)。 -
void write(String str)
寫入字符串。 -
void write(String str, int off, int len)
寫入字符串的某一部分,off字符串的開始索引,len寫的字符個(gè)數(shù)。 -
void flush()
刷新該流的緩沖。 -
void close()
關(guān)閉此流,但要先刷新它。
1.4 FileWriter類
java.io.FileWriter
類是寫出字符到文件的便利類。構(gòu)造時(shí)使用系統(tǒng)默認(rèn)的字符編碼和默認(rèn)字節(jié)緩沖區(qū)。
構(gòu)造方法
-
FileWriter(File file)
: 創(chuàng)建一個(gè)新的 FileWriter,給定要讀取的File對象。 -
FileWriter(String fileName)
: 創(chuàng)建一個(gè)新的 FileWriter,給定要讀取的文件的名稱。
當(dāng)你創(chuàng)建一個(gè)流對象時(shí),必須傳入一個(gè)文件路徑,類似于FileOutputStream。
- 構(gòu)造舉例,代碼如下:
public class FileWriterConstructor {
public static void main(String[] args) throws IOException {
// 使用File對象創(chuàng)建流對象
File file = new File("a.txt");
FileWriter fw = new FileWriter(file);
// 使用文件名稱創(chuàng)建流對象
FileWriter fw = new FileWriter("b.txt");
}
}
基本寫出數(shù)據(jù)
寫出字符:write(int b)
方法,每次可以寫出一個(gè)字符數(shù)據(jù),代碼使用演示:
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileWriter fw = new FileWriter("fw.txt");
// 寫出數(shù)據(jù)
fw.write(97); // 寫出第1個(gè)字符
fw.write('b'); // 寫出第2個(gè)字符
fw.write('C'); // 寫出第3個(gè)字符
fw.write(30000); // 寫出第4個(gè)字符,中文編碼表中30000對應(yīng)一個(gè)漢字。
/*
【注意】關(guān)閉資源時(shí),與FileOutputStream不同。
如果不關(guān)閉,數(shù)據(jù)只是保存到緩沖區(qū),并未保存到文件。
*/
// fw.close();
}
}
輸出結(jié)果:
abC田
小貼士:
- 雖然參數(shù)為int類型四個(gè)字節(jié),但是只會(huì)保留一個(gè)字符的信息寫出。
- 未調(diào)用close方法,數(shù)據(jù)只是保存到了緩沖區(qū),并未寫出到文件中。
關(guān)閉和刷新
因?yàn)閮?nèi)置緩沖區(qū)的原因,如果不關(guān)閉輸出流,無法寫出字符到文件中。但是關(guān)閉的流對象,是無法繼續(xù)寫出數(shù)據(jù)的。如果我們既想寫出數(shù)據(jù),又想繼續(xù)使用流,就需要flush
方法了。
-
flush
:刷新緩沖區(qū),流對象可以繼續(xù)使用。 -
close
:先刷新緩沖區(qū),然后通知系統(tǒng)釋放資源。流對象不可以再被使用了。
代碼使用演示:
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileWriter fw = new FileWriter("fw.txt");
// 寫出數(shù)據(jù),通過flush
fw.write('刷'); // 寫出第1個(gè)字符
fw.flush();
fw.write('新'); // 繼續(xù)寫出第2個(gè)字符,寫出成功
fw.flush();
// 寫出數(shù)據(jù),通過close
fw.write('關(guān)'); // 寫出第1個(gè)字符
fw.close();
fw.write('閉'); // 繼續(xù)寫出第2個(gè)字符,【報(bào)錯(cuò)】java.io.IOException: Stream closed
fw.close();
}
}
小貼士:即便是flush方法寫出了數(shù)據(jù),操作的最后還是要調(diào)用close方法,釋放系統(tǒng)資源。
寫出其他數(shù)據(jù)
-
寫出字符數(shù)組 :
write(char[] cbuf)
和write(char[] cbuf, int off, int len)
,每次可以寫出字符數(shù)組中的數(shù)據(jù),用法類似FileOutputStream,代碼使用演示:
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileWriter fw = new FileWriter("fw.txt");
// 字符串轉(zhuǎn)換為字節(jié)數(shù)組
char[] chars = "我是程序員".toCharArray();
// 寫出字符數(shù)組
fw.write(chars); // 我是程序員
// 寫出從索引2開始,2個(gè)字節(jié)。索引2是'程',兩個(gè)字節(jié),也就是'程序'。
fw.write(b,2,2); // 程序
// 關(guān)閉資源
fos.close();
}
}
-
寫出字符串:
write(String str)
和write(String str, int off, int len)
,每次可以寫出字符串中的數(shù)據(jù),更為方便,代碼使用演示:
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象
FileWriter fw = new FileWriter("fw.txt");
// 字符串
String msg = "我是程序員";
// 寫出字符數(shù)組
fw.write(msg); //我是程序員
// 寫出從索引2開始,2個(gè)字節(jié)。索引2是'程',兩個(gè)字節(jié),也就是'程序'。
fw.write(msg,2,2); // 程序
// 關(guān)閉資源
fos.close();
}
}
- 續(xù)寫和換行:操作類似于FileOutputStream。
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名稱創(chuàng)建流對象,可以續(xù)寫數(shù)據(jù)
FileWriter fw = new FileWriter("fw.txt",true);
// 寫出字符串
fw.write("我是");
// 寫出換行
fw.write("\r\n");
// 寫出字符串
fw.write("程序員");
// 關(guān)閉資源
fw.close();
}
}
輸出結(jié)果:
我是
程序員
小貼士:字符流,只能操作文本文件,不能操作圖片,視頻等非文本文件。
當(dāng)我們單純讀或者寫文本文件時(shí) 使用字符流 其他情況使用字節(jié)流
2. IO異常的處理
JDK7前處理
之前的入門練習(xí),我們一直把異常拋出,而實(shí)際開發(fā)中并不能這樣處理,建議使用try...catch...finally
代碼塊,處理異常部分,代碼使用演示:
public class HandleException1 {
public static void main(String[] args) {
// 聲明變量
FileWriter fw = null;
try {
//創(chuàng)建流對象
fw = new FileWriter("fw.txt");
// 寫出數(shù)據(jù)
fw.write("我是程序員"); //我是程序員
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
JDK7的處理
還可以使用JDK7優(yōu)化后的try-with-resource
語句,該語句確保了每個(gè)資源在語句結(jié)束時(shí)關(guān)閉。所謂的資源(resource)是指在程序完成后,必須關(guān)閉的對象。
格式:
try (創(chuàng)建流對象語句,如果多個(gè),使用';'隔開) {
// 讀寫數(shù)據(jù)
} catch (IOException e) {
e.printStackTrace();
}
代碼使用演示:
public class HandleException2 {
public static void main(String[] args) {
// 創(chuàng)建流對象
try ( FileWriter fw = new FileWriter("fw.txt"); ) {
// 寫出數(shù)據(jù)
fw.write("我是程序員"); //我是程序員
} catch (IOException e) {
e.printStackTrace();
}
}
}
JDK9的改進(jìn)
JDK9中try-with-resource
的改進(jìn),對于引入對象的方式,支持的更加簡潔。被引入的對象,同樣可以自動(dòng)關(guān)閉,無需手動(dòng)close,我們來了解一下格式。
改進(jìn)前格式:
// 被final修飾的對象
final Resource resource1 = new Resource("resource1");
// 普通對象
Resource resource2 = new Resource("resource2");
// 引入方式:創(chuàng)建新的變量保存
try (Resource r1 = resource1;
Resource r2 = resource2) {
// 使用對象
}
改進(jìn)后格式:
// 被final修飾的對象
final Resource resource1 = new Resource("resource1");
// 普通對象
Resource resource2 = new Resource("resource2");
// 引入方式:直接引入
try (resource1; resource2) {
// 使用對象
}
改進(jìn)后,代碼使用演示:
public class TryDemo {
public static void main(String[] args) throws IOException {
// 創(chuàng)建流對象
final FileReader fr = new FileReader("in.txt");
FileWriter fw = new FileWriter("out.txt");
// 引入到try中
try (fr; fw) {
// 定義變量
int b;
// 讀取數(shù)據(jù)
while ((b = fr.read())!=-1) {
// 寫出數(shù)據(jù)
fw.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 綜合練習(xí)
練習(xí)1:拷貝文件夾
public class Test01 {
public static void main(String[] args) throws IOException {
//拷貝一個(gè)文件夾,考慮子文件夾
//1.創(chuàng)建對象表示數(shù)據(jù)源
File src = new File("D:\\aaa\\src");
//2.創(chuàng)建對象表示目的地
File dest = new File("D:\\aaa\\dest");
//3.調(diào)用方法開始拷貝
copydir(src,dest);
}
/*
* 作用:拷貝文件夾
* 參數(shù)一:數(shù)據(jù)源
* 參數(shù)二:目的地
*
* */
private static void copydir(File src, File dest) throws IOException {
dest.mkdirs();
//遞歸
//1.進(jìn)入數(shù)據(jù)源
File[] files = src.listFiles();
//2.遍歷數(shù)組
for (File file : files) {
if(file.isFile()){
//3.判斷文件,拷貝
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}else {
//4.判斷文件夾,遞歸
copydir(file, new File(dest,file.getName()));
}
}
}
}
練習(xí)2:文件加密
public class Test02 {
public static void main(String[] args) throws IOException {
/*
為了保證文件的安全性,就需要對原始文件進(jìn)行加密存儲(chǔ),再使用的時(shí)候再對其進(jìn)行解密處理。
加密原理:
對原始文件中的每一個(gè)字節(jié)數(shù)據(jù)進(jìn)行更改,然后將更改以后的數(shù)據(jù)存儲(chǔ)到新的文件中。
解密原理:
讀取加密之后的文件,按照加密的規(guī)則反向操作,變成原始文件。
^ : 異或
兩邊相同:false
兩邊不同:true
0:false
1:true
100:1100100
10: 1010
1100100
^ 0001010
__________
1101110
^ 0001010
__________
1100100
*/
}
public static void encryptionAndReduction(File src, File dest) throws IOException {
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
int b;
while ((b = fis.read()) != -1) {
fos.write(b ^ 2);
}
//4.釋放資源
fos.close();
fis.close();
}
}
練習(xí)3:數(shù)字排序
文本文件中有以下的數(shù)據(jù):
2-1-9-4-7-8
將文件中的數(shù)據(jù)進(jìn)行排序,變成以下的數(shù)據(jù):
1-2-4-7-8-9
實(shí)現(xiàn)方式一:
public class Test03 {
public static void main(String[] args) throws IOException {
/*
文本文件中有以下的數(shù)據(jù):
2-1-9-4-7-8
將文件中的數(shù)據(jù)進(jìn)行排序,變成以下的數(shù)據(jù):
1-2-4-7-8-9
*/
//1.讀取數(shù)據(jù)
FileReader fr = new FileReader("myio\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.read()) != -1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
String str = sb.toString();
String[] arrStr = str.split("-");//2-1-9-4-7-8
ArrayList<Integer> list = new ArrayList<>();
for (String s : arrStr) {
int i = Integer.parseInt(s);
list.add(i);
}
Collections.sort(list);
System.out.println(list);
//3.寫出
FileWriter fw = new FileWriter("myio\\a.txt");
for (int i = 0; i < list.size(); i++) {
if(i == list.size() - 1){
fw.write(list.get(i) + "");
}else{
fw.write(list.get(i) + "-");
}
}
fw.close();
}
}
實(shí)現(xiàn)方式二:文章來源:http://www.zghlxwxcb.cn/news/detail-719353.html
public class Test04 {
public static void main(String[] args) throws IOException {
/*
文本文件中有以下的數(shù)據(jù):
2-1-9-4-7-8
將文件中的數(shù)據(jù)進(jìn)行排序,變成以下的數(shù)據(jù):
1-2-4-7-8-9
細(xì)節(jié)1:
文件中的數(shù)據(jù)不要換行
細(xì)節(jié)2:
bom頭
*/
//1.讀取數(shù)據(jù)
FileReader fr = new FileReader("myio\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.read()) != -1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
Integer[] arr = Arrays.stream(sb.toString()
.split("-"))
.map(Integer::parseInt)
.sorted()
.toArray(Integer[]::new);
//3.寫出
FileWriter fw = new FileWriter("myio\\a.txt");
String s = Arrays.toString(arr).replace(", ","-");
String result = s.substring(1, s.length() - 1);
fw.write(result);
fw.close();
}
}
后記
????????美好的一天,到此結(jié)束,下次繼續(xù)努力!欲知后續(xù),請看下回分解,寫作不易,感謝大家的支持??! ??????文章來源地址http://www.zghlxwxcb.cn/news/detail-719353.html
到了這里,關(guān)于從零開始學(xué)習(xí) Java:簡單易懂的入門指南之IO字符流(三十一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!