欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何用Java來進(jìn)行文件切割和簡單的內(nèi)容過濾的實(shí)現(xiàn)

 更新時間:2017年01月27日 09:55:59   作者:sonnyching  
這篇文章主要介紹了如何用Java來進(jìn)行文件切割和簡單的內(nèi)容過濾的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。

一 由來

去年由于項(xiàng)目的需求,要將一個任意一個文件制作成一個xml文件,并且需要保持文件內(nèi)容本身不產(chǎn)生變化,還要能夠?qū)⑦@個xml重新還原為原文件。如果小型的文件還好處理,大型的xml,比如幾個G的文件,基本上就OOM了,很難直接從節(jié)點(diǎn)中提取數(shù)據(jù)。所以我采用了流的方式。于是有了這個文件的裁剪工具。

二 使用場景

本工具可能的使用場景:

1.對任一文件的切割/裁剪。通過字節(jié)流的方式,開始節(jié)點(diǎn)和終止節(jié)點(diǎn),裁剪出兩個節(jié)點(diǎn)之間的部分。

2.往任一文件的頭/尾拼接指定字符串??梢院苋菀讓⒁粋€文件嵌入在某一個節(jié)點(diǎn)中。

3.簡單的文本抽取。可以根據(jù)自己定義的規(guī)則,提取出來想要的文本內(nèi)容,并且允許對提取出來的文本進(jìn)行再處理(當(dāng)然,只是進(jìn)行簡單地抽取文字,并不是什么智能的復(fù)雜過程的抽取T_T )。

4.文本過濾。根據(jù)自己制定的規(guī)則,過濾掉指定的文字。

整個工具僅是對Java文件操作api的簡單加工,并且也沒有使用nio。在需要高效率的文件處理情景下,本工具的使用有待考量。文章目的是為了給出自己的一種解決方案,若有更好的方案,歡迎大家給出適當(dāng)?shù)慕ㄗh。

三 如何使用

別的先不說,來看看如何使用吧!

1.讀取文件指定片段

讀取第0~1048個字節(jié)之間的內(nèi)容。

public void readasbytes(){
    FileExtractor cuter = new FileExtractor();
    byte[] bytes = cuter.from("D:\\11.txt").start(0).end(1048).readAsBytes();
  }

2.文件切割

將第0~1048個字節(jié)之間的部分切割為一個新文件。

public File splitAsFile(){
    FileExtractor cuter = new FileExtractor();
    return cuter.from("D:\\11.txt").to("D:\\22.txt").start(0).end(1048).extractAsFile();
  }

3.將文件拼接到一個xml節(jié)點(diǎn)中

將整個文件的內(nèi)容作為Body節(jié)點(diǎn),寫入到一個xml文件中。返回新生成的xml文件對象。

  public File appendText(){

    FileExtractor cuter = new FileExtractor();
    return cuter.from("D:\\11.txt").to("D:\\44.xml").appendAsFile("<Document><Body>", "</Body></Document>");

  }

4.讀取并處理文件中的指定內(nèi)容

假如有需求:讀取11.txt的前三行文字。其中,第一行和第二行不能出現(xiàn)”帥”字,并且在第三行文字后加上字符串“我好帥!”。   

public String extractText(){
    FileExtractor cuter = new FileExtractor();
    return cuter.from("D:\\11.txt").extractAsString(new EasyProcesser() {
      @Override
      public String finalStep(String line, int lineNumber, Status status) {

        if(lineNumber==3){
          status.shouldContinue = false;//表示不再繼續(xù)讀取文件內(nèi)容
          return line+"我好帥!";
        }
        return line.replaceAll("帥","");
      }
    });

  }

4.簡單的文本過濾

將一個文件中所有的“bug”去掉,且返回一個處理后的新文件。

  public File killBugs(){
    FileExtractor cuter = new FileExtractor();
    return cuter.from("D:\\bugs.txt").to("D:\\nobug.txt").extractAsFile(new EasyProcesser() {
      @Override
      public String finalStep(String line, int lineNumber, Status status) {
        return line.replaceAll("bug", "");
      }
    }); 
  }

四 基本流程

通過接口回調(diào)的方式,將文件的讀取過程和處理過程分離開來;定義了IteratorFile類來負(fù)責(zé)遍歷一個文件,讀取文件的內(nèi)容;分字節(jié)、行兩種的方式來進(jìn)行文件內(nèi)容的遍歷。下面的介紹,也會分為讀取和處理兩個部分單獨(dú)介紹。

五 文件的讀取

定義回調(diào)接口

定義一個接口Process,對外暴露了兩個文件內(nèi)容處理方法,一個支持按字節(jié)進(jìn)行讀取,一個方法支持按行讀取。

public interface Process{

  /**
   * @param b 本次讀取的數(shù)據(jù)
   * @param length 本次讀取的有效長度
   * @param currentIndex 當(dāng)前讀取到的位置
   * @param available 讀取文件的總長度
   * @return true 表示繼續(xù)讀取文件,false表示終止讀取文件
   * @time 2017年1月22日 下午4:56:41
   */
  public boolean doWhat(byte[] b,int length,int currentIndex,int available);

  /**
   * 
   * @param line 本次讀取到的行
   * @param currentIndex 行號
   * @return true 表示繼續(xù)讀取文件,false表示終止讀取文件
   * @time 2017年1月22日 下午4:59:03
   */
  public boolean doWhat(String line,int currentIndex);

 讓ItratorFile中本身實(shí)現(xiàn)這個接口,但是默認(rèn)都是返回true,不做任何的處理。如下所示:

public class IteratorFile implements Process
{
......
/**
   * 按照字節(jié)來讀取遍歷文件內(nèi)容,根據(jù)自定義需要重寫該方法
   */
  @Override
  public boolean doWhat(byte[] b, int length,int currentIndex,int available) {
    return true;
  }

  /**
   * 按照行來讀取遍歷文件內(nèi)容,根據(jù)自定義需要重寫該方法
   */
  @Override
  public boolean doWhat(String line,int currentIndex) {
    return true;
  }
......
}

按字節(jié)遍歷文件內(nèi)容

實(shí)現(xiàn)按照字節(jié)的方式來進(jìn)行文件的遍歷(讀取)。在這里使用了skip()方法來控制從第幾個節(jié)點(diǎn)開始讀取內(nèi)容;然后在使用文件流讀取的時候,將每次讀取到得數(shù)據(jù)傳遞給回調(diào)接口的方法;需要注意的是,每次讀取到得數(shù)據(jù)是存在一個字節(jié)數(shù)組bytes里面的,每次讀取的長度也是需要傳遞給回調(diào)接口的。我們很容易看出,一旦dowhat()返回false,文件的讀取立即就退出了。

public void iterator2Bytes(){
    init();
    int length = -1;
    FileInputStream fis = null;
    try {
      file = new File(in);
      fis = new FileInputStream(file);
      available = fis.available();
      fis.skip(getStart());
      readedIndex = getStart();
      if (!beforeItrator()) return;
      while ((length=fis.read(bytes))!=-1) {
        readedIndex+=length;
        if(!doWhat(bytes, length,readedIndex,available)){
          break;
        }
      }
      if(!afterItrator()) return;
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }finally{
      try {
        fis.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

按行來遍歷文件內(nèi)容

常規(guī)的文件讀取方式,在while循環(huán)中,調(diào)用了回調(diào)接口的方法,并且傳遞相關(guān)的數(shù)據(jù)。

  public void iterator2Line(){
    init();
    BufferedReader reader = null;
    FileReader read = null;
    String line = null;
    try {
      file = new File(in);
      read = new FileReader(file);
      reader = new BufferedReader(read);
      if (!beforeItrator()) return;
      while ( null != (line=reader.readLine())) {
        readedIndex++;
        if(!doWhat(line,readedIndex)){
          break;
        }
      }
      if(!afterItrator()) return ;
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }finally{
      try {
        read.close();
        reader.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

然后,還需要提供方法來設(shè)置要讀取的源文件路徑。

  public IteratorFile from(String in){
    this.in = in;
    return this;
  }

六 文件內(nèi)容處理

FileExtractor介紹

定義了FileExtractor類,來封裝對文件內(nèi)容的處理操作;該類會引用到遍歷文件所需要的類IteratorFile。

FileExtractor的基本方法

  /**
   * 往文件頭或者文件結(jié)尾插入字符串
   * @tips 不能對同一個文件輸出路徑反復(fù)執(zhí)行該方法,否則會出現(xiàn)文本異常,因?yàn)橛玫搅薘andomAccessFile,如有需要,調(diào)用前需手動刪除原有的同名文件
   * @param startStr 文件開頭要插入的字符串
   * @param endStr 文件結(jié)尾要插入的字符串
   * @return 生成的新文件
   * @time 2017年1月22日 下午5:05:35
   */
  public File appendAsFile(final String startStr,String endStr){}


/**
   * 從指定位置截取文件
   * @tips 適合所有的文件類型
   * @return
   * @time 2017年1月22日 下午5:06:36
   */
  public File splitAsFile(){}


/**
   * 文本文件的特殊處理(情景:文本抽取,文本替換等)
   * @tips 只適合文本文件,對于二進(jìn)制文件,因?yàn)閾Q行符的原因?qū)е挛募霈F(xiàn)可能無法執(zhí)行等問題。
   * @time 2017年1月22日 下午5:09:14
   */
  public File extractAsFile(FlowLineProcesser method) {


/**
   * 文本文件的特殊處理(情景:文本抽取,文本替換等)
   * @tips 只適合文本文件,對于二進(jìn)制文件,因?yàn)閾Q行符的原因?qū)е挛募霈F(xiàn)可能無法執(zhí)行等問題。
   * @time 2017年1月22日 下午5:09:14
   */
  public String extractAsString(FlowLineProcesser method) {}

  /**
   * 讀取指定位置的文件內(nèi)容為字節(jié)數(shù)組
   * @return
   * @time 2017年1月23日 上午11:06:18
   */
  public byte[] readAsBytes(){}

其中,返回值為File的方法在處理完成后,都出返回一個經(jīng)過內(nèi)容后的新文件。

其他方法

同樣,設(shè)置源文件位置的方法,以及截取位置的相關(guān)方法

  /**
   * 設(shè)置源文件
   */
  public FileExtractor from(String in){
    this.in = in;
    return this;
  }

  /**
   * 設(shè)置生成臨時文件的位置(返回值為File的方法均需要設(shè)置)
   */
  public FileExtractor to(String out) {
    this.out = out;
    return this;
  }

  /**
   * 文本開始截取的位置(包含此位置),字節(jié)相關(guān)的方法均需要設(shè)置
   */
  public FileExtractor start(int start){
    this.startPos = start;
    return this;
  }

  /**
   * 文本截取的終止位置(包含此位置),字節(jié)相關(guān)方法均需要設(shè)置
   */
  public FileExtractor end(int end) {
    this.endPos = end;
    return this;
  }

按字節(jié)讀取文件時的文件內(nèi)容處理

有幾個重點(diǎn):

1.因?yàn)橐鶕?jù)字節(jié)的位置來進(jìn)行文件截取,所以需要根據(jù)字節(jié)來遍歷文件,所以要重寫doWhat()字節(jié)遍歷的的方法。并在外部構(gòu)造一個OutPutStream來進(jìn)行新文件的寫出工作。

2.每次遍歷讀取出的文件內(nèi)容,都存放在一個字節(jié)數(shù)組b里面,但并不是b中的數(shù)據(jù)都是有用的,所以需要傳遞b有效長度length。

3.readedIndex記錄了到本次為止(包括本次)為止,已經(jīng)讀取了多少位數(shù)據(jù)。

4.按照自己來遍歷文件時,如何判斷讀取到了的終止位置?

當(dāng)(已讀的數(shù)據(jù)總長度)readedIndex>endPos(終止節(jié)點(diǎn))時,說明本次讀取的時候超過了應(yīng)該終止的位置,此時b數(shù)組中有一部分?jǐn)?shù)據(jù)就是多讀的了,這部分?jǐn)?shù)據(jù)是不應(yīng)該被保存的。我們可以通過計(jì)算得到讀超了多少位,即length-(readedIndex-endPos-1),那么只要保存這部分?jǐn)?shù)據(jù)就可以了。

讀取指定片段的文件內(nèi)容:
 

  //本方法在需要讀取的數(shù)據(jù)多時,不建議使用,因?yàn)閎yte[]是不可變的,多次讀取的時候,需要進(jìn)行多次的byete[] copy過程,效率“感人”。
  public byte[] readAsBytes(){

    try {
      checkIn();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }

    //臨時保存字節(jié)的容器
    final BytesBuffer buffer = new BytesBuffer();

    IteratorFile c = new IteratorFile(){
      @Override
      public boolean doWhat(byte[] b, int length, int currentIndex,
          int available) {
        if(readedIndex>endPos){
          //說明已經(jīng)讀取到了endingPos位置并且讀超了
          buffer.addBytes(b, 0, length-(readedIndex-endPos-1)-1);
          return false;
        }else{
          buffer.addBytes(b, 0, length-1);
        }
        return true;
      }
    };
    //按照字節(jié)進(jìn)行遍歷
    c.from(in).start(startPos).iterator2Bytes();

    return buffer.toBytes();

  }

當(dāng)文件很大時,生成一個新的文件的比較靠譜的方法,所以,類似直接返回byte[],在文件讀取之前,設(shè)置一個outputSteam,在內(nèi)容循環(huán)讀取的過程中,將讀取的內(nèi)容寫入到一個新文件中去。

  public File splitAsFile(){
    ......
    final OutputStream os = FileUtils.openOut(file);
    try {
      IteratorFile itFile = new IteratorFile(){
        @Override
        public boolean doWhat(byte[] b, int length,int readedIndex,int available) {
          try {
            if(readedIndex>endPos){
              //說明已經(jīng)讀取到了endingPos位置,并且讀超了readedIndex-getEnd()-1位
              os.write(b, 0, length-(readedIndex-endPos-1));
              return false;//終止讀取
            }else{
              os.write(b, 0, length);
            }
            return true;
          } catch (IOException e) {
            e.printStackTrace();
            return false;
          }
        }
      }.from(in).start(startPos);

      itFile.iterator2Bytes();

    } catch (Exception e) {
      e.printStackTrace();
      this.tempFile = null;
    }finally{
      try {
        os.flush();
        os.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    return getTempFile();
  }

按行來讀取時的文件內(nèi)容處理

首先,再次聲明,按行來遍歷文件的時候,只適合文本文件。除非你對每一行的換行符用\r還是\n沒有要求。像exe文件,如果用行來遍歷的話,你寫出為一個新的文件的時候,任意一個的換行符的不對都可能導(dǎo)致一個exe文件變?yōu)椤眜nexe”文件!

過程中,我用到了:

一個輔助類Status,來輔助控制遍歷的流程。

一個接口FlowLineProcesser,類似于一個處理文本的流水線。

Status和FlowLineProcesser是相互輔助的,Status也能輔助FlowLineProcesse是流水線的具體過程,Status是控制處理過程中怎么處理d的。

我也想了許多次,到底要不要把這個過程搞的這么復(fù)雜。但是還是先留著吧…

先看輔助類Status:

public class Status{
  /**
   * 是否找到了開頭,默認(rèn)false,若true則后續(xù)的遍歷不會執(zhí)行相應(yīng)的firstStep()方法
   */
  public boolean overFirstStep = false;

  /**
   * 是否找到了結(jié)尾,默認(rèn)false,若true則后續(xù)的遍歷不會執(zhí)行相應(yīng)的finalStep()方法
   */
  public boolean overFinalStep = false;

  /**
   * 是否繼續(xù)讀取源文件,默認(rèn)true表示繼續(xù)讀取,false則表示,執(zhí)行本次操作后,遍歷終止
   */
  public boolean shouldContinue = true;
}

然后是FlowLineProcesser接口:

FlowLineProcesser是一個接口,類似于一個流水線。定義了兩步操作,分別對應(yīng)兩個方法fistStep()和finalStep()。其中兩個方法的返回值都是String,firstStep接受到得line是真正從文件中讀取到的行,它將line經(jīng)過自己的處理后,返回處理后的line給finalStep。所以,finalStep中得line其實(shí)是firstStep處理后的結(jié)果。但是最終真正返回給主處理流程的line,正是finalStep處理后的返回值。

public interface FlowLineProcesser{
  /**
   * 
   * @param line 讀取到的行
   * @param lineNumber 行號,從1開始
   * @param status 控制器
   * @return
   * @time 2017年1月22日 下午5:02:02
   */
  String firstStep(String line,int lineNumber,Status status);

  /**
   * @tips 
   * @param line 讀取到的行(是firstStep()處理后的結(jié)果)
   * @param lineNumber 行號,從1開始
   * @param status 控制器
   * @return
   * @time 2017年1月22日 下午5:02:09
   */
  String finalStep(String line,int lineNumber,Status status);
}

現(xiàn)在,可以來看一下如何去實(shí)現(xiàn)文本的抽取了:

所有讀取的行,都臨時存到一個stringbuilder中去。firstStep先進(jìn)行一次處理,得到返回值后傳遞給finalStep,再次處理后,將得到的結(jié)果保存下來。如果最后的結(jié)果是null,則不會保存。

  public String extractAsString(FlowLineProcesser method) {

    try {
      checkIn();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }

    final StringBuilder builder = new StringBuilder();

    this.mMethod = method;

    new IteratorFile(){
      Status status = new Status();
      @Override
      public boolean doWhat(String line, int currentIndex) {
        String lineAfterProcess = "";

        if(!status.overFirstStep){
          lineAfterProcess = mMethod.firstStep(line, currentIndex,status);
        }

        if(!status.shouldContinue){
          return false;
        }

        if(!status.overFinalStep){
          lineAfterProcess = mMethod.finalStep(lineAfterProcess,currentIndex,status);
        }

        if(lineAfterProcess!=null){
          builder.append(lineAfterProcess);
          builder.append(getLineStr());//換行符被寫死在這里了
        }

        if(!status.shouldContinue){
          return false;
        }
        return true;
    }

    }.from(in).iterator2Line();

    return builder.toString();

  }

當(dāng)要抽取的文本太大的時候,可以采用生成新文件的方式。與返回string的流程基本一致。

  public File extractAsFile(FlowLineProcesser method) {

    try {
      checkIn();
      checkOut();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }

    this.mMethod = method;
    File file = initOutFile();
    if(file==null){
      return null;
    }

    FileWriter fileWriter = null;
    try {
      fileWriter = new FileWriter(file);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }

    final BufferedWriter writer = new BufferedWriter(fileWriter);

    IteratorFile itfile = new IteratorFile(){
      Status status = new Status();
      @Override
      public boolean doWhat(String line, int currentIndex) {
        String lineAfterProcess = "";

        if(!status.overFirstStep){
          lineAfterProcess = mMethod.firstStep(line, currentIndex,status);
        }

        if(!status.shouldContinue){
          return false;
        }

        if(!status.overFinalStep){
          lineAfterProcess = mMethod.finalStep(lineAfterProcess,currentIndex,status);
        }

        if(lineAfterProcess!=null){
          try {
            writer.write(lineAfterProcess);
            writer.newLine();//TODO 換行符在此給寫死了
          } catch (IOException e) {
            e.printStackTrace();
            return false;
          }
        }

        if(!status.shouldContinue){
          return false;
        }
        return true;

      }
    };

    itfile.from(in).iterator2Line();

    if(writer!=null){
      try {
        writer.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    try {
      fileWriter.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return getTempFile();

  }

好啦,介紹到此就要結(jié)束啦,我們下次再聊~

代碼包供您下載哦!—>代碼包

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 深入講解Java?synchronized的核心原理

    深入講解Java?synchronized的核心原理

    這篇文章主要為大家詳細(xì)介紹了Java中synchronized的核心原理以及簡單的用法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-07-07
  • java中的PriorityQueue類過程詳解

    java中的PriorityQueue類過程詳解

    這篇文章主要介紹了java中的PriorityQueue類,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Spring中的Filter過濾器詳解

    Spring中的Filter過濾器詳解

    這篇文章主要介紹了Spring中的Filter過濾器詳解,Filter 程序是一個實(shí)現(xiàn)了特殊接口的 Java 類,與 Servlet 類似,也是由 Servlet 容器進(jìn)行調(diào)用和執(zhí)行的,需要的朋友可以參考下
    2023-08-08
  • Java實(shí)現(xiàn)Dbhelper支持大數(shù)據(jù)增刪改

    Java實(shí)現(xiàn)Dbhelper支持大數(shù)據(jù)增刪改

    這篇文章主要介紹了Java實(shí)現(xiàn)Dbhelper支持大數(shù)據(jù)增刪改功能的實(shí)現(xiàn)過程,感興趣的小伙伴們可以參考一下
    2016-01-01
  • 淺談Java并發(fā)之同步器設(shè)計(jì)

    淺談Java并發(fā)之同步器設(shè)計(jì)

    這篇文章主要介紹Java并發(fā)之同步器設(shè)計(jì),本文以記錄方式并發(fā)編程中同步器設(shè)計(jì)的一些共性特征。并簡單介紹了Java中的AQS,需要的朋友可以參考一下文章的詳細(xì)內(nèi)容
    2021-10-10
  • Spring boot通過AOP防止API重復(fù)請求代碼實(shí)例

    Spring boot通過AOP防止API重復(fù)請求代碼實(shí)例

    這篇文章主要介紹了Spring boot通過AOP防止API重復(fù)請求代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • springboot項(xiàng)目快速搭建的方法步驟

    springboot項(xiàng)目快速搭建的方法步驟

    這篇文章主要介紹了springboot項(xiàng)目快速搭建的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • springboot整合vue項(xiàng)目(小試牛刀)

    springboot整合vue項(xiàng)目(小試牛刀)

    這篇文章主要介紹了springboot整合vue項(xiàng)目(小試牛刀),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • Eclipse中maven異常Updating Maven Project的統(tǒng)一解決方案

    Eclipse中maven異常Updating Maven Project的統(tǒng)一解決方案

    今天小編就為大家分享一篇關(guān)于Eclipse中maven異常Updating Maven Project的統(tǒng)一解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Java函數(shù)式接口Supplier接口實(shí)例詳解

    Java函數(shù)式接口Supplier接口實(shí)例詳解

    這篇文章主要介紹了Java函數(shù)式接口Supplier接口實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02

最新評論