1. 程式人生 > >Java學習不走彎路教程(3.從文件內容查詢開始)

Java學習不走彎路教程(3.從文件內容查詢開始)

輔助 pass 多說 font sys sta case index exe

一. 前言
在前兩章教程中,分別介紹了DOS環境搭建和Eclipse環境搭建。
本章將帶大家實現用簡單SQL語句查詢文件。
註:
1.本文針對初學Java的同學訓練學習思路,請不要太糾結於細節問題。
2.本文旨在達到拋磚引玉的效果,希望大家擴展本例子,以學到更多知識的精髓。

二. 寫給初學Java的同學
在介紹本章內容之前,首先介紹一下Java的學習方法。
相信大家在看本文的時候已經已經拿到了各種Java學習路徑,大體都是一樣。
我想說的是,不要讓知識的學習成為負擔,Java技術種類繁多,是無論如何也學不完的。
正確的學習方法是興趣驅動,實例驅動。
即通過一個簡單的實例,不斷加入所學知識進行擴展,最終擴展為一個大項目,達到系統學習,學以致用的效果。


三. 步入正題
話不多說,大家自己理解,下面步入正題:

練習1.
概要:本地有一個csv格式文件,用SQL語句查出結果。
前提:本地有一個csv格式文件
  例:abc.csv放在c:/temp文件夾下,內容如下:

id,username,password
1,abc,aaa
2,def,bbb
3,xyz,ccc

輸入:文件路徑和sql語句

  例:

GetFile gf = new GetFile("c:/temp/");
gf.queryFile("select * from abc.csv");

輸出:查詢結果

  例:

1,abc,aaa
2,def,bbb
3,xyz,ccc

請先自己動手想辦法實現,實在做不出來再往下看,否則失去了練習的意義。

四. 實現步驟

首先,我們需要解析SQL語句。是的,標準的寫法是把SQL轉化成語法樹(AST),然後再解析這顆語法樹。
但這篇文章面對的是初學者,我的目的是訓練字符串的拆分,拼接,文件讀取等基本操作,以及分析問題的方法。
所以,把問題簡單化。


首先確認程序功能的邊界:
下面是這個程序支持的最復雜的SQL語句(只支持單層的and條件):

select id,username from abc.csv where username=abc and password=aaa

所以,我們首先需要檢查SQL的正確性:
?必須包含select,from
?可以包含where
?且循序必須為select,from,where
代碼如下:

    /**
     * @author http://www.java123.vip
     * @param sql
     * @return
     */
    private boolean checkSql(String sql) {
        int selectPos = sql.indexOf("select");
        int fromPos = sql.indexOf("from");
        int wherePos = sql.indexOf("where");
        
        if(selectPos != 0 
                || fromPos <= selectPos
                || (wherePos != -1 && wherePos <= fromPos)) {
            return false;
        }else {
            return true;
        }
    }

然後,我們需要把上述語句的如下元素解析出來:

查詢項目:id,username
查詢文件:abc.csv
查詢條件:username=abc,password=aaa
做一個類來存儲解析結果:

package vip.java123.fileview;

/**
 * 
 * @author http://www.java123.vip
 *
 */
public class SqlParseResult {
    public String[] fields;
    public String fileName;
    public String[] whereConditions;
    
    public void clearSpace() {
        for(int i = 0; i < fields.length; i ++) {
            fields[i] = fields[i].trim();
        }
        
        fileName = fileName.trim();
        
        for(int i = 0; i < whereConditions.length; i ++) {
            whereConditions[i] = whereConditions[i].trim();
        }
    }
}

解析的函數如下

    /**
     * @author http://www.java123.vip
     * @param sql
     * @return
     */
    private SqlParseResult parseSql(String sql) {
        int selectPos = sql.indexOf("select");
        int fromPos = sql.indexOf("from");
        int wherePos = sql.indexOf("where");
        
        String columnStr = sql.substring(selectPos + "select".length(), fromPos).trim();
        String fileNameStr;
        String whereStr;
        if(wherePos == -1) {
            fileNameStr = sql.substring(fromPos + "from".length());
            whereStr = "";
        }else {
            fileNameStr = sql.substring(fromPos + "from".length(),wherePos);
            whereStr = sql.substring(wherePos + "where".length());
        }
        
        
        SqlParseResult spr = new SqlParseResult();
        spr.fields = columnStr.split(",");
        spr.fileName = fileNameStr;
        if(wherePos == -1) {
            spr.whereConditions = new String[] {};
        }else {
            spr.whereConditions = whereStr.split("and");
        }
        
        spr.clearSpace();
        
        return spr;
    }
    

(處理where後面的條件)接下來,對於讀取的一行數據,我們需要檢查該行數據是否符合查詢條件,做個函數如下:
header為第一行數據用逗號分割後的數組(文件頭)

    /**
     * @author http://www.java123.vip
     * @param line
     * @param whereConditions
     * @param header
     * @return
     */
    private boolean checkRow(String line, String[] whereConditions, String[] header) {
        // username=abc  password=aaa
        String[] lineColumns = line.split(",");
        for(int i = 0; i < whereConditions.length; i ++) {
            String key = whereConditions[i].split("=")[0];
            String value = whereConditions[i].split("=")[1];
            
            String checkValue = lineColumns[getHeaderIndex(header,key)];
            if(!value.equals(checkValue)) {
                return false;
            }
        }
        return true;
    }
    


做個輔助函數,根據列名,返回列名的位置

    /**
     * @author http://www.java123.vip
     * @param header
     * @param headerName
     * @return
     */
    private int getHeaderIndex(String[] header,String headerName) {
        for(int i = 0; i < header.length; i ++) {
            if(header[i].equals(headerName)) {
                return i;
            }
        }
        
        return -1;
    }
    

(處理select後面的列名)如果數據符合查詢條件,則根據select關鍵字後面的列名(fields)過濾查詢結果

    /**
     * @author http://www.java123.vip
     * @param line
     * @param fields
     * @param header
     * @return
     */
    private String selectLine(String line, String[] fields, String[] header) {
        
        if(fields[0].equals("*")) {
            return line;
        }
        
        StringBuffer result = new StringBuffer();
        
        String[] lineColumns = line.split(",");
        for(int i = 0; i < fields.length; i ++) {
            int columnIndex = this.getHeaderIndex(header, fields[i]);
            result.append(lineColumns[columnIndex]);
            
            if(i != fields.length -1) {
                result.append(",");
            }
        }
        return result.toString();
    }

把上面的函數拼接在一起,主函數如下:

    /**
     * @author http://www.java123.vip
     * @param sql
     * @return
     * @throws Exception
     */
    public String queryFile(String sql) throws Exception{
        sql = sql.toLowerCase().trim();
        
        if(!checkSql(sql)) {
            return null;
        }
        SqlParseResult spr = parseSql(sql);
        
        File f = new File(basePath + spr.fileName);
        
        FileInputStream fis = new FileInputStream(f);
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);
        
        boolean readHeader = true;
        String[] header = null;
        
        StringBuffer result = new StringBuffer();
        String line = null;
        while((line = br.readLine()) != null) {
            if(readHeader) {
                header = line.split(",");
                readHeader = false;
            }else {
                if(checkRow(line,spr.whereConditions,header)) {
                    
                    result.append(selectLine(line,spr.fields,header));
                    result.append("\n");
                }
            }
            
        }
        
        br.close();
        isr.close();
        fis.close();
        
        return result.toString();
    }

最後,測試我們的程序:

    /**
     * @author http://www.java123.vip
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        
        GetFile gf = new GetFile("c:/temp/");
        
        String sql1 = "select * from abc.csv ";
        String sql2 = "select id from abc.csv ";
        String sql3 = "select id,username from abc.csv where id=2 ";
        String sql4 = "select id,username from abc.csv where username=abc and password=aaa ";
        String sql5 = "select id,username from abc.csv where username=abc and password=bbb ";
        
        System.out.println("Execute:"+sql1);
        System.out.println(gf.queryFile(sql1));
        
        System.out.println("Execute:"+sql2);
        System.out.println(gf.queryFile(sql2));
        
        System.out.println("Execute:"+sql3);
        System.out.println(gf.queryFile(sql3));
        
        System.out.println("Execute:"+sql4);
        System.out.println(gf.queryFile(sql4));
        
        System.out.println("Execute:"+sql5);
        System.out.println(gf.queryFile(sql5));
        
    }

輸出結果如下:

Execute:select * from abc.csv 
1,abc,aaa
2,def,bbb
3,xyz,ccc

Execute:select id from abc.csv 
1
2
3

Execute:select id,username from abc.csv where id=2 
2,def

Execute:select id,username from abc.csv where username=abc and password=aaa 
1,abc

Execute:select id,username from abc.csv where username=abc and password=bbb

完整程序請大家從[這裏]下載

五. 後續
本例為通過簡單的SQL語句查詢本地存在的文件,大家可以擴展此程序,讓其支持更復雜的SQL,結果排序,索引等功能。
後續章節我將在此程序的基礎上,把CSV文件放到另外一臺電腦,然後讓其支持JDBC接口。
最後換成數據庫,並且一步一步實現ORM,Service,HTTP查詢等功能。

如有問題,大家來我的網站進行提問。
https://www.java123.vip/qa

版權聲明:本教程版權歸java123.vip所有,禁止任何形式的轉載與引用。

原帖發表於:https://www.cnblogs.com/java123vip/p/9719828.html

Java學習不走彎路教程(3.從文件內容查詢開始)