java提供了最基本的文件處理函數,可以簡單無結構的方式讀入小文本文件,如果遇到需要結構化、格式多樣、要求特殊的文件或內存裝不下的大文件,相應的代碼就會很復雜,可讀性和復用性也很難保障。
使用免費的集算器可以彌補這一不足。集算器封裝了豐富的結構化文件讀寫和計算函數,并提供JDBC接口。JAVA應用程序可以將集算器腳本文件當做數據庫存儲過程執行,傳入參數并用JDBC獲得返回結果。詳情參考集算器用作Java計算類庫的應用結構。
下面說明JAVA讀入文本的常見案例,以及集算器對應的解法。
讀入指定列
按列名讀入sOrder.txt中的3列:OrderID、Client、Amount。源數據如下:

集算器代碼:
| A | |
| 1 | =file(“D: //sOrder.txt”).import@t(OrderID,Client,Amount) |
結果:

1. @t表示將第1行讀為列名。文件不含列名時可用序號引用各列,比如讀入第1、2、4列,可用這句代碼:file(“D: //sOrder.txt”).import(#1,#2,#4),結果如下:

2. 如果要輸出計算列,比如將年份和OrderID拼成neWorderID,并和Client、Amount一同輸出,可用如下代碼:
| A | |
| 1 | =file(“D: //sOrder.txt”).import@t() |
| 2 | =A1.new(string(year(OrderDate))+”_”+string(OrderID):newOrder,Client,Amount) |
函數import默認讀入所有字段,new函數可創建新二維表,結果如下:

3. 默認分割符是tab,也可用其他字符,比如讀入以逗號為分隔符的csv文件,可用這句代碼file(“D: //sOrder.txt”).import@t(;”,”)。
4. 如果只輸出部分行,可以按行號指定,比如輸出2-100行,代碼是A1.to(2,100),從第3行開始輸出,代碼是A1.to(3,)。
5. 個別情況下會按列讀入,比如將OrderID,Client,Amount縱向拼成1列輸出,讀入數據后可用下面的代碼實現:create(all).record(A1.(OrderID)|A1.(Client)|A1.(Amount)) 。
讀取大文件
對于超過內存的大文件,可用集算器游標讀取文件,JAVA用JDBC流訪問。
集算器代碼:
| A | |
| 1 | =file(“D: //sOrder.txt”).cursor@t(OrderID,Client,Amount) |
1. 如果想加快文件的讀取速度,可以用多線程并行處理技術,只需簡單地添加@m選項,代碼即=file(“D: //sOrder.txt”).cursor@tm(OrderID,Client,Amount)。不過由于多線程并行讀入,這種用法將不能保證讀入數據的次序。
2. 有時候需要手工分段再并行計算,這時就要讀入某一段文件,用代碼可以實現:file(“D://sOrder.txt”).import@z@t(;,2:24)
@z表示將文件按字節數大致分為24部分,只讀取第2部分,集算器會自動取頭補尾,以保證取出的數據是整行。
如果分段后內存仍然裝不下,可以將import函數改為cursor,即輸出為游標。
按列寬讀入文件
文件data.txt無分隔符,如下:

需要按指定寬度讀成4列的二維表,并輸出到JAVA,id列取前3位,flag列取10-11位,d1列取14-24位,d2列取25-33位。如第1行的4列分別為:001、DT、100000000000、3210XXXX。
集算器代碼:
| A | |
| 1 | =file(“D://data.txt”).import@i() |
| 2 | =A1.new(mid(~,1,3):id,mid(~,10,2):flag,mid(~,14,11):d1,mid(~,25,9):d2) |
A1:@i表示文件只有一列時返回為序列(集合)。
A2:根據A1創建新二維表,mid函數可截取字符串,~表示每行數據。
結果:

文本含特殊字符
文件data.csv含有引號,有些引號影響了數據的正常使用,現在要去掉引號再輸出到JAVA,源數據如下:

集算器代碼:
| A | |
| 1 | =file(“d://data.csv”).import(;”,”) |
| 2 | =A1.new(replace(_1,”/”",”"):_1,replace(_2,”/”",”"):_2, replace(_3,”/”",”"):_3,replace(_4,”/”",”"):_4) |
結果:

文本含數學公式
需要將文本中的公式解析成表達式,計算后再輸出,源數據如下:

集算器代碼:
| A | |
| 1 | =file(“D://equations.txt”).import@i() |
| 2 | =As1.new(~:equations,eval(string(~)):result) |
函數eval可將字符串動態解析為表達式并執行。
結果:

多行記錄
下面文件每三行代表一條記錄,比如第一條記錄是:JFS 3 468.0 2009-08-13 39,現在需要將該文件輸出成二維表。

集算器代碼:
| A | |
| 1 | =file(“D://data.txt”).import@si() |
| 2 | =A1.group((#-1)/3) |
| 3 | =A2.new(~(1):OrderID, (line=~(2).array(“/t”))(1):Client,line(2):SellerId,line(3):Amount,~(3):OrderDate ) |
先將文件讀為序列,@s表示不拆分字段。再每三行分一組。”#”表示行號,“/”表示整除。最后根據每組結果創建新序表,~(1)表示當前組的第1個成員,函數array可將字符串拆分為序列,結果如下:

如果文件太大無法放入內存,應當用游標打開文件再分批計算。首先建立sub.dfx,作用是當有外部請求時就讀入一批數據并返回,直到文件結束,代碼如下:
| A | B | |
| 1 | =file(“D://data.txt”).cursor@si() | |
| 2 | for A1,3000 | =A2.group((#-1)/3) |
| 3 | =B2.new(~(1):OrderID, (line=~(2).array(“/t”))(1):Client,line(2):SellerId,line(3):Amount,~(3):OrderDate ) | |
| 4 | result B3 | |
循環A1,每次讀入3000條數據,并按照之前的算法處理。
B4表示將B3傳回主腳本。主腳本(也就是被JAVA調用的dfx文件)代碼如下:
| A | |
| 1 | =pcursor(“sub.dfx”) |
函數pcursor可以從sub.dfx請求數據并轉為游標輸出。
不定行記錄
文件data.txt中每條記錄屬于不定的多個行,但每個字段都有其固定標記,分別是”Object Type:”、”left:”、”top ”、”Line Color: ”直到行末的文本,第1條記錄即:Symbol1、14、11、RGB( 1 0 0 )。現在要將其讀為結構化二維表。

集算器代碼:
| A | |
| 1 | =file(“data.txt”).read() |
| 2 | =A1.array(“Object Type: “).to(2,) |
| 3 | =A2.new(~.array(“/r/n”)(1):OType,mid(~,s=pos(~,”left: “)+len(“left: “),pos(~,”/r/n”,s)-s):L,mid(~,s=pos(~,”top: “)+len(“top: “),pos(~,”/r/n”,s)-s):T,mid(~,s=pos(~,”Line Color: “)+len(“Line Color: “),if(r=pos(~,”/r/n”,s),r,len(~))-s+1):LColor)
|
Read函數可將文件讀為一個大字符串。之后用分隔符拆分字符串,去掉第一個空行。最后創建新序表,使用字符串函數array、pos、len、mid來找到所需字段。注意最后一行也許沒有回車換行,因此要進行if判斷。最終結果:

查找字段時使用了字符串函數,其實也可以用正則表達式。
如果文件太大內存裝不下,可以使用函數pcursor分批讀取。
記錄按標記分組
文件data.txt按組存放記錄,有list標記的是分組名(比如ARO、BDR、BSF),需要將分組名和組內字段拼在一起輸出。源數據如下:

集算器代碼:
| A | |
| 1 | =file(“mutiline2.txt”).import@si() |
| 2 | =A1.group@i(like(~,”list:*”)) |
| 3 | =A2.conj(~.to(2,).new(mid(A2.~(1),6):Client,(t=~.array(“/t”))(1):c1,t(2):c2,t(3):c3,t(4):c4)) |
先將文件讀為字符串序列,再按照記錄分隔標記分組,@i表示條件為真則分為新的一組,*是通配符。A2如下:

之后按序號取出字段,再合并各組記錄,結果如下:

新聞熱點
疑難解答