做數據分析必須了解的獲取數據與清洗數據技巧
每個數據科學家都需要處理存儲在磁盤中的數據,這些數據涉及的格式有ASCII文本、PDF、XML、JSON等。此外,數據還可以存儲在數據庫表格中。在對數據進行分析之前,數據科學家首先要做的是從這些數據源獲取各種格式的數據,并對這些數據進行清洗,去除其中的噪聲。今天推薦的圖書是《Java數據科學指南》一書,并從中節選第一章內容,從本文中我們將學習這些內容,即了解如何從不同數據源獲取各種格式的數據。
在這一過程中,我們將用到外部Java庫(Java歸檔文件,簡稱JAR文件),這些庫的使用不僅限于本文,還貫穿于《Java數據科學指南》一書。這些庫由不同開發者或組織開發,方便了大家的使用。編寫代碼時,我們會用到Eclipse IDE工具,它是Windows平臺下最好的集成開發環境,全書都會使用它。接下來,我們將講解如何導入任意一個外部JAR文件,以下各個部分將指導你把外部JAR文件導入到項目中,跟隨步驟動手去做即可。
對于一個Eclipse項目,你可以采用如下方法添加JAR文件:首先依次單擊“Project|Build Path|Configure Build Path”,在Libraries選項卡中,單擊“Add External JARs”,選擇你想添加到項目的外部JAR文件,如圖1-1所示。
圖1-1
這部分內容(以及后面各部分內容)是為那些想從復雜目錄結構中提取文件路徑與名稱的數據科學家準備的,以方便進一步進行后續分析。這里的復雜目錄結構是指在一個根目錄下包含大量目錄與文件。
準備工作
開始之前,需要做如下準備工作。
1.創建復雜的目錄結構(目錄層數你自己決定)。
2.在其中一些目錄中創建文本文件,而在另一些目錄中留空。
操作步驟
1.首先編寫一個static方法,即listFiles(File rootDir),它帶有一個File類型的參數,該參數可以是根目錄或起始目錄。這個方法將返回一系列文件,這些文件存在于參數所指定的根目錄(以及其他所有下級子目錄)中。
1 ? ? public static Set
2.然后,創建一個HashSet對象,用來包含文件信息。
1 ? ? Set
3.在創建好HashSet對象之后,要檢查參數指定的根目錄及其子目錄是否為null。當為null時,直接把HashSet對象返回即可,不需要進行進一步處理。
1 ? ? if (rootDir == null || rootDir.listFiles() == null){
2 ? ? ? ? ? ? ? ? ?return fileSet;
3 ? ? ? ?}
4.接著,檢查根目錄中的每個目錄(或文件),判斷它是文件還是目錄。如果是文件,就把它添加到HashSet中;如果是一個目錄,就遞歸地調用本方法,并把當前目錄路徑與名稱傳遞給它。
1 ? ? for (File fileOrDir : rootDir.listFiles()) {
2 ? ? ? ? ? ? ? ? ?if (fileOrDir.isFile()){
3 ? ? ? ? ? ? ? ? ? ?fileSet.add(fileOrDir);
4 ? ? ? ? ? ? ? ? ?}
5 ? ? ? ? ? ? ? ? ?else{
6 ? ? ? ? ? ? ? ? ? ?fileSet.addAll(listFiles(fileOrDir));
7 ? ? ? ? ? ? ? ? ?}
8 ? ? ? ? ?}
5.最后,把HashSet返回給該方法的調用者。
1 ? ? return fileSet;
2 ? ? ? ?}
listFiles(File rootDir)方法的完整代碼如下,包含執行該方法所需要的類與驅動方法。
1import java.io.File;
2import java.util.HashSet;
3import java.util.Set;
4public class TestRecursiveDirectoryTraversal {
5 ? public static void main(String[] args){
6 ? ? ?System.out.println(listFiles(new File("Path for root
7 ? ? ? ? ?directory")).size());
8 ? }
9 ? public static Set
10 ? ? ?Set
11 ? ? ?if(rootDir == null || rootDir.listFiles()==null){
12 ? ? ? ? ?return fileSet;
13 ? ? ?}
14 ? ? ?for (File fileOrDir : rootDir.listFiles()) {
15 ? ? ? ? ? ?if (fileOrDir.isFile()){
16 ? ? ? ? ? ? ?fileSet.add(fileOrDir);
17 ? ? ? ? ? ?}
18 ? ? ? ? ? ?else{
19 ? ? ? ? ? ? ?fileSet.addAll(listFiles(fileOrDir));
20 ? ? ? ? ? ?}
21 ? ? ?}
22 ? ? ?return fileSet;
23 ? }
24}
{:--}
請注意,代碼中的HashSet用來存儲文件路徑與名稱。這意味著我們不會有任何重復項,這是因為Java中的Set這種數據結構不包含重復項。
1.3 使用Apache Commons IO從多層目錄中提取所有文件名
你可以使用前面一部分演示的操作步驟,采用遞歸方法把多層目錄中的文件名列出來。除此之外,我們還有另外一種更簡單、更方便的方法來完成它,那就是使用Apache Commons IO,并且只需編寫少量代碼即可。
準備工作
開始之前,需要做如下準備。
1.本部分會用到一個名稱為Commons IO的Java庫,它來自于Apache基金會。全書中,我們會使用Commons IO 2.5版本,請從Commons官網下載JAR文件。
2.在Eclipse中,把下載的JAR文件包含到你的項目中(作為外部JAR文件)。
操作步驟
1.創建listFiles方法,它帶有一個參數,用來指定層級目錄的根目錄。
1 ? ? public void listFiles(String rootDir){
2.創建一個文件對象,并把根目錄名傳遞給它。
1 ? ? File dir = new File(rootDir);
3.Apache Commons庫的FileUtils類中包含一個名稱為listFiles()方法。使用這個方法提取所有文件名,并且把它們放入一個帶有
1 ? ? List
2 ? ? ? TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
4.我們可以像下面這樣把文件名顯示在標準輸出中。由于我們把文件名放入了一個列表之中,所以我們可以通過某種方法對這些文件中的數據進行進一步處理。
1 ? ? for (File file : files) {
2 ? ? ? ?System.out.println("file: " + file.getAbsolutePath());
3 ? ? }
5.關閉方法。
1 ? ? }
完整代碼包括方法代碼、類代碼,以及驅動方法,如下所示:
1import java.io.File;
2import java.util.List;
3import org.apache.commons.io.FileUtils;
4import org.apache.commons.io.filefilter.TrueFileFilter;
5public class FileListing{
6 ? public static void main (String[] args){
7 ? ? ?FileListing fileListing = new FileListing();
8 ? ? ?fileListing.listFiles("Path for the root directory here");
9 ? }
10 ? public void listFiles(String rootDir){
11 ? ? ?File dir = new File(rootDir);
12 ? ? ?List
13 ? ? ? ?TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
14 ? ? ?for (File file : files) {
15 ? ? ? ? System.out.println("file: " + file.getAbsolutePath());
16 ? ? ?}
17 ? }
如果你想把帶有一些特定擴展名的文件列出來,還可以使用Apache Commons庫中的listFiles方法。但是這個方法的參數有些不同,它擁有3個參數,分別為文件目錄、擴展名(String[])、遞歸與否。在這個庫中還有一個有趣的方法,即listFilesAndDirs(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter),如果你想把文件與目錄全部列出來,可以使用它。
1.4使用Java 8從文本文件一次性讀取所有內容
在許多場合下,數據科學家所擁有的數據是文本格式的。我們有很多方法可以用來讀取文本文件的內容,這些方法各具優缺點:一些方法執行起來耗時、耗內存,而另一些方法執行速度很快,也不需要消耗太多計算機內存;一些方法可以把全部文本內容一次性讀出,而另一些方法則只能一行行地讀取文本文件。至于到底要選擇哪種方法,則取決于你所面對的任務,以及你決定采用何種方法來處理這個任務。
在這部分中,我們將演示使用Java 8把文本文件的全部內容一次性讀出來的方法。
操作步驟
1.首先,創建一個String對象,用來保存待讀取的文本文件的目錄與名稱。
1 ? ? String file = "C:/dummy.txt";
2.使用Paths類的get()方法,可以得到待讀文件的路徑。get()方法的參數是String對象,用來指定文件名,它的輸出作為lines()方法的輸入。lines()方法包含于Files類之中,用來讀取一個文件的所有行,并且返回Stream,也就是說,這個方法的輸出定向到一個Stream變量。因為我們的dummy.txt文件中包含字符串數據,所以把Stream變量的泛型設置為String。
整個讀取過程需要放入一個try...catch塊中,用來應對讀取過程中可能發生的異常,比如當試圖讀取的文件不存在或已損壞時,就會拋出異常。
下面代碼用來把dummy.txt文件中的內容全部顯示出來。在stream變量中包含著文本文件的所有行,所以需要使用它的forEach()方法顯示出每行內容。
1 ? try (Stream
2 ? stream.forEach(System.out::println); } catch (IOException e) {
3 ? System.out.println("Error reading " + file.getAbsolutePath());
4 ? }
1.5使用Apache Commons IO從文本文件一次性讀取所有內容
在上一節中我們學習了使用Java8從文本文件中一次性讀取所有內容,其實我們也可以使用Apache Commons IO API一次性讀取文本文件的所有內容。
準備工作
開始之前,需要做如下準備。
1.本部分,我們會用到一個名為Apache Commons IO的Java庫。
2.在Eclipse中,把下載好的JAR文件包含到你的項目中。
操作方法
1.假設你要讀取的文件為dummy.txt,它位于C:/目錄之下。首先,需要創建一個文件對象,用來訪問這個文件,如下所示:
1 ? ? File file = new File("C:/dummy.txt");
2.接著,創建一個字符串對象,用來保存文件中的文本內容。這里我們要使用readFileToString()方法,它來自于Apache Commons IO庫,是FileUtils類的一個成員方法。調用這個方法的方式有很多,但是現在,你只需知道我們要傳遞兩個參數給它,第一個參數是file對象,用來指定要讀取的文件,第二個參數是文件的編碼,在示例中,我們將其設置為UTF-8。
1 ? ? String text = FileUtils.readFileToString(file, "UTF-8");
3.只要使用上面兩行代碼,我們就可以讀取文本文件內容,并將它們存入一個String變量中。但是,你可不是一個普通的數據科學家,你比其他人要聰明得多。所以,你在上面兩行代碼的前后又添加了幾行代碼,用來處理Java方法拋出的異常,比如你試圖讀取的文件不存在或者已經損壞,就會觸發異常。為此,我們需要把上面兩行代碼放入到一個try...catch塊之中,如下所示:
1 ? ? File file = new File("C:/dummy.txt");
2 ? ? try {
3 ? ? String text = FileUtils.readFileToString(file, "UTF-8");
4 ? ? } catch (IOException e) {
5 ? ? System.out.println("Error reading " + file.getAbsolutePath());
6 ? ? }
1.6 使用Apache Tika提取PDF文本
在解析與提取數據時,最難搞的文件類型之一是PDF文件。有些PDF文件甚至無法解析,因為它們有密碼保護,而其他一些則包含著掃描的文本與圖像。所以,這種動態文件類型有時會成為數據科學家的夢魘。本部分演示如何使用Apache Tika從PDF文件提取文本,當然前提是PDF文件沒有被加密,也沒有密碼保護,而只包含非掃描的文本。
準備知識
開始之前,需要先做如下準備。
1.下載Apache Tika 1.10 JAR文件,并且將其作為外部Java庫包含到你的Eclipse項目中。
2.把任意一個未鎖定的PDF文件保存到C:/目錄之下,并且命名為testPDF.pdf。
操作步驟
1.創建一個名稱為convertPdf(String)的方法,它帶有一個字符串參數,用來指定PDF文件名稱。
1 ? ? public void convertPDF(String fileName){
2.創建一個輸入流,用來以字節流的形式包含PDF數據。
1 ? ? InputStream stream = null;
3.創建一個try塊,如下所示:
1 ? ? try{
4.把文件指派給剛剛創建好的stream。
1 ? ? stream = new FileInputStream(fileName);
5.在Apache Tika包中包含著許多不同的解析器。如果你不知道該選用哪一個,或者說你還有其他類型的文檔需要轉換,那么你應該使用AutoDetectParser解析器,如下所示:
1 ? ? AutoDetectParser parser = new AutoDetectParser();
6.創建一個handler,用來處理文件的正文內容。請注意,創建時需要把構造函數的參數設為-1。通常,Apache Tika會對處理的文件進行限制,要求它至多包含100 000個字符。使用-1讓這個handler忽略這個限制。
1 ? ? BodyContentHandler handler = new BodyContentHandler(-1);
7.創建一個metadata對象。
1 ? ? Metadata metadata = new Metadata();
8.調用解析器對象的parser()方法,并把上面創建的這些對象傳遞給它。
1 ? ? parser.parse(stream, handler, metadata, new ParseContext());
9.使用handler對象的tostring()方法,獲取從文件中提取的正文文本。
1 ? ? System.out.println(handler.toString());
10.關閉try塊,并且添加catch與finally塊。最后,關閉整個方法,如下所示:
1 ? ? }catch (Exception e) {
2 ? ? ? ? ? ? ? ? ? e.printStackTrace();
3 ? ? ? ? ? ? ?}finally {
4 ? ? ? ? ? ? ? ? ?if (stream != null)
5 ? ? ? ? ? ? ? ? ? ? ? try {
6 ? ? ? ? ? ? ? ? ? ? ? ? ? ?stream.close();
7 ? ? ? ? ? ? ? ? ? ? ? } catch (IOException e) {
8 ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("Error closing stream");
9 ? ? ? ? ? ? ? ? ? ? ? }
10 ? ? ? ? ? ? ? ? ?}
11 ? ? }
下面代碼包含convertPdf(String)方法的完整代碼,以及相應的類與驅動方法。在調用convertPdf(String)方法時,你需要提供待轉換的PDF文件的路徑與名稱,即把該方法的參數指定為C:/testPDF.pdf。
1import java.io.FileInputStream;
2import java.io.IOException;
3import java.io.InputStream;
4import org.apache.tika.metadata.Metadata;
5import org.apache.tika.parser.AutoDetectParser;
6import org.apache.tika.parser.ParseContext;
7import org.apache.tika.sax.BodyContentHandler;
8public class TestTika {
9 ? ? public static void main(String args[]) throws Exception {
10 ? ? ? ? ?TestTika tika = new TestTika();
11 ? ? ? ? ?tika.convertPdf("C:/testPDF.pdf");
12 ? ? }
13 ? ? public void convertPdf(String fileName){
14 ? ? ? ? ?InputStream stream = null;
15 ? ? ? ? ?try {
16 ? ? ? ? ? ? ?stream = new FileInputStream(fileName);
17 ? ? ? ? ? ? ?AutoDetectParser parser = new AutoDetectParser();
18 ? ? ? ? ? ? ?BodyContentHandler handler = new BodyContentHandler(-1);
19 ? ? ? ? ? ? ?Metadata metadata = new Metadata();
20 ? ? ? ? ? ? ?parser.parse(stream, handler, metadata, new
21 ? ? ? ? ? ? ? ? ?ParseContext());
22 ? ? ? ? ? ? ?System.out.println(handler.toString());
23 ? ? ? ? ?}catch (Exception e) {
24 ? ? ? ? ? ? ?e.printStackTrace();
25 ? ? ? ? ?}finally {
26 ? ? ? ? ? ? ?if (stream != null)
27 ? ? ? ? ? ? ? ? ? try {
28 ? ? ? ? ? ? ? ? ? ? ? ?stream.close();
29 ? ? ? ? ? ? ? ? ? } catch (IOException e) {
30 ? ? ? ? ? ? ? ? ? ? ? ?System.out.println("Error closing stream");
31 ? ? ? ? ? ? ? ? ? }
32 ? ? ? ? ?}
33 ? ? }
34}
1.7 使用正則表達式清洗ASCII文本文件
ASCII文本文件中通常會包含一些非必要的字符,這些字符通常產生于轉換過程中,比如把PDF轉換為文本或把HTML轉換為文本的過程中。并且,這些字符常常被看作噪聲,它們是數據處理的主要障礙之一。本部分,我們學習使用正則表達式為ASCII文本數據清洗一些噪聲的方法。
操作步驟
1.創建一個名為cleanText(String)的方法,它帶有一個String類型的參數,用來指定要清洗的文本。
1 ? ? public String cleanText(String text){
2.在你的方法中,添加如下幾行代碼,而后把清洗后的文本返回,并關閉方法。在如下代碼中,第一行代碼用來去掉非ASCII字符,緊接的一行用來把連續的空格字符替換為單個空格字符。第三行用來清除所有ASCII控制字符。第四行用來去除ASCII非打印字符。最后一行用來從Unicode移除非打印字符。
1 ? ? text = text.replaceAll("[^p{ASCII}]","");
2 ? ? text = text.replaceAll("s+", " ");
3 ? ? text = text.replaceAll("p{Cntrl}", "");
4 ? ? text = text.replaceAll("[^p{Print}]", "");
5 ? ? text = text.replaceAll("p{C}", "");
6 ? ? return text;
7 ? ? }
以下代碼是方法的完整代碼,包含相應類與驅動方法。
1public class CleaningData {
2 ? public static void main(String[] args) throws Exception {
3 ? ? ?CleaningData clean = new CleaningData();
4 ? ? ?String text = "Your text here you have got from some file";
5 ? ? ?String cleanedText = clean.cleanText(text);
6 ? ? ?//清洗文本處理
7 ? }
8 ? public String cleanText(String text){
9 ? ? ?text = text.replaceAll("[^p{ASCII}]","");
10 ? ? ? ?text = text.replaceAll("s+", " ");
11 ? ? ? ?text = text.replaceAll("p{Cntrl}", "");
12 ? ? ? ?text = text.replaceAll("[^p{Print}]", "");
13 ? ? ? ?text = text.replaceAll("p{C}", "");
14 ? ? ? ?return text;
15 ? }
16}
1.8 使用Univocity解析CSV文件
對數據科學家來說,另一種經常處理的文件格式是CSV(逗號分隔)文件,在這種文件中數據之間通過逗號進行分隔。CSV文件非常流行,因為大部分電子表格應用程序都可以讀取它,比如MS Excel。
本部分,我們將學習解析CSV文件,以及處理所提取的數據點的方法。
準備工作
開始之前,需要先做如下準備。
1.下載Univocity JAR文件,并將其作為外部庫添加到你的Eclipse項目中。
2.使用Notepad創建一個CSV文件,它包含如下數據。創建好之后,把文件的擴展名修改為.csv,并把它保存到C盤之下,即C:/testCSV.csv。
1 ? ? Year,Make,Model,Description,Price
2 ? ? 1997,Ford,E350,"ac, abs, moon",3000.00
3 ? ? 1999,Chevy,"Venture ""Extended Edition""","",4900.00
4 ? ? 1996,Jeep,Grand Cherokee,"MUST SELL!
5 ? ? air, moon roof, loaded",4799.00
6 ? ? 1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00
7 ? ? ,,"Venture ""Extended Edition""","",4900.00
操作步驟
1.創建一個名為parseCsv(String)的方法,它帶有一個String類型的參數,用來指定待解析的文件名。
1 ? ? public void parseCsv(String fileName){
2.而后創建一個配置對象,該對象用來提供多種配置選項。
1 ? ? CsvParserSettings parserSettings = new CsvParserSettings();
3.借助于配置對象,你可以打開解析器的自動檢測功能,讓它自動偵測輸入中包含何種行分隔符序列。
1 ? ? parserSettings.setLineSeparatorDetectionEnabled(true);
4.創建一個RowListProcessor對象,用來把每個解析的行存儲在列表中。
1RowListProcessor rowProcessor = new RowListProcessor();
5.你可以使用RowProcessor來配置解析器,以對每個解析行的值進行處理。你可以在com.univocity.parsers.common.processor包中找到更多RowProcessors,但是你也可以自己創建。
1 ? ? parserSettings.setRowProcessor(rowProcessor);
6.如果待解析的CSV文件包含標題頭,你可以把第一個解析行看作文件中每個列的標題。
1 ? ? parserSettings.setHeaderExtractionEnabled(true);
7.接下來,使用給定的配置創建一個parser實例。
1 ? ? CsvParser parser = new CsvParser(parserSettings);
8.parser實例的parse()方法用來解析文件,并把每個經過解析的行指定給前面定義的RowProcessor。
1 ? ? parser.parse(new File(fileName));
9.如果解析中包含標題,則可使用如下代碼獲取這些標題。
1 ? ? String[] headers = rowProcessor.getHeaders();
10.隨后,你可以很容易地處理這個字符串數組,以獲取這些標題值。
11.另一方面,我們在列表中可以找到行值。只要使用一個for循環即可把列表打印出來,如下所示。
1 ? ? List
2 ? ? for (int i = 0; i < rows.size(); i++){
3 ? ? ? ?System.out.println(Arrays.asList(rows.get(i)));
4 ? ? }
12.最后,關閉方法。
1 ? ? }
整個方法的完整代碼如下所示:
1import java.io.File;
2import java.util.Arrays;
3import java.util.List;
4import com.univocity.parsers.common.processor.RowListProcessor;
5import com.univocity.parsers.csv.CsvParser;
6import com.univocity.parsers.csv.CsvParserSettings;
7public class TestUnivocity {
8 ? ? ?public void parseCSV(String fileName){
9 ? ? ? ? ?CsvParserSettings parserSettings = new CsvParserSettings();
10 ? ? ? ? ?parserSettings.setLineSeparatorDetectionEnabled(true);
11 ? ? ? ? ?RowListProcessor rowProcessor = new RowListProcessor();
12 ? ? ? ? ?parserSettings.setRowProcessor(rowProcessor);
13 ? ? ? ? ?parserSettings.setHeaderExtractionEnabled(true);
14 ? ? ? ? ?CsvParser parser = new CsvParser(parserSettings);
15 ? ? ? ? ?parser.parse(new File(fileName));
16 ? ? ? ? ?String[] headers = rowProcessor.getHeaders();
17 ? ? ? ? ?List
18 ? ? ? ? ?for (int i = 0; i < rows.size(); i++){
19 ? ? ? ? ? ?System.out.println(Arrays.asList(rows.get(i)));
20 ? ? ? ? ?}
21 ? ? }
22 ? ? public static void main(String[] args){
23 ? ? ? ?TestUnivocity test = new TestUnivocity();
24 ? ? ? ?test.parseCSV("C:/testCSV.csv");
25 ? ? }
26}
有很多采用Java編寫的CSV解析器。但是,相比較而言,Univocity是執行速度最快的一個。
1.9 使用Univocity解析TSV文件
不同于CSV文件,TSV(制表符分隔)文件中所包含的數據通過TAB制表符進行分隔。本部分,我們將學習使用Univocity從TSV文件提取數據點的方法。
準備工作
開始之前,先做如下準備工作。
1.下載Univocity JAR文件,并將其作為外部庫包含到你的Eclipse項目中。
2.使用Notepad創建一個TSV文件,它包含如下數據。創建好之后,把文件的擴展名修改為.tsv,并把它保存到C盤之下,即C:/testTSV.tsv。
1Year ? Make ? ?Model ? Description Price
21997 ? Ford ? ?E350 ? ?ac, abs, moon ? 3000.00
31999 ? Chevy ? Venture "Extended Edition" ? ? ?4900.00
41996 ? Jeep ? ?Grand Cherokee ?MUST SELL!nair, moon roof, loaded 4799.00
51999 ? Chevy ? Venture "Extended Edition, Very Large" ? ? ?5000.00
6 ? ? ? Venture "Extended Edition" ? ? ?4900.00
1.創建一個名稱為parseTsv(String)的方法,它帶有一個String類型的參數,用來指定待解析的文件名。
1 ? ? public void parseTsv(String fileName){
2.本部分中TSV文件的行分隔符為換行符或n。為了把字符n設置為行分隔符,修改設置如下所示。
1 ? ? settings.getFormat().setLineSeparator("n");
3.使用這些設置,創建一個TSV解析器。
1 ? ? TsvParser parser = new TsvParser(settings);
4.使用如下代碼,把TSV文件中的所有行一次性解析出來。
1 ? ? List
5.遍歷列表對象,打印或處理數據行,代碼如下所示。
1 ? ? for (int i = 0; i < allRows.size(); i++){
2 ? ? ? ? ? ? ?System.out.println(Arrays.asList(allRows.get(i)));
3 ? ? ? ? ? ?{
6.最后,關閉方法。
1 ? ? }
下面代碼中包含整個方法的完整代碼,以及相應類與驅動方法。
1import java.io.File;
2import java.util.Arrays;
3import java.util.List;
4import com.univocity.parsers.tsv.TsvParser;
5import com.univocity.parsers.tsv.TsvParserSettings;
6public class TestTsv {
7 ? public void parseTsv(String fileName){
8 ? ? ? TsvParserSettings settings = new TsvParserSettings();
9 ? ? ? settings.getFormat().setLineSeparator("n");
10 ? ? ? TsvParser parser = new TsvParser(settings);
11 ? ? ? List
12 ? ? ? for (int i = 0; i < allRows.size(); i++){
13 ? ? ? ? System.out.println(Arrays.asList(allRows.get(i)));
14 ? ? ? }
15 ? ?}
16}
1.10 使用JDOM解析XML文件
通常,文本數據是沒有結構的,不同于文本數據,在XML文件中的數據是具有結構的。XML是組織數據的一種流行方式,借助它,我們可以非常方便地準備、傳遞以及利用數據。有很多方法可以用來解析XML文件的內容。本書中,我們將學習使用一個名為JDOM的Java庫來解析XML文件。
準備工作
開始之前,先做如下準備工作。
1.下載JDOM 2.06版本(JAR文件)。
2.在Eclipse中,創建一個項目,并把上面下載的JAR文件作為外部JAR文件包含進去。
3.打開notepad,新建一個名稱為dummyxml的文件,文件擴展名為.xml,文件包含的內容簡單如下:
1 ? ?
2 ? ?
3 ? ? ? ?
4 ? ? ? ? ?
5 ? ? ? ? ?
6 ? ? ? ?
7 ? ? ? ?
8 ? ? ? ? ?
9 ? ? ? ? ?
10 ? ? ? ?
11 ? ? ?
操作步驟
1.創建一個名稱為builder的SAXBuilder對象。
1 ? ? SAXBuilder builder = new SAXBuilder();
2.接下來,你需要創建一個File對象,用來指向待解析的XML文件。如果你已經把XML文件保存到C盤之下,則將其放入如下代碼片段中。
1 ? ? File file = new File("c:/dummyxml.xml");
3.在try語句塊中,需要創建一個Document對象,它表示你的XML文件。
1 ? ? try {
2 ? ? ? Document document = (Document) builder.build(file);
4.在解析呈現樹狀結構的XML文件時,需要知道文件的根元素,以便開始遍歷整個樹(換言之,開始進行系統的解析)。因此,需要創建一個Element類型的rootNode對象,用來保存根元素,在我們的示例中,它對應于
1 ? ? Element rootNode = document.getRootElement();
5.接著,獲取根節點下所有名稱為author的子節點。由于調用getChildren()方法所得到的是子節點列表,所以還需要有一個列表變量來存儲它們。
1 ? ? List list = rootNode.getChildren("author");
6.然后,使用for循環遍歷整個子節點列表,以獲取列表中的項目元素。每個元素都存儲在Element類型的node變量中。這個變量有一個名稱為getChildText()的方法,其參數為子元素名稱,返回子元素的文本內容,如果指定的子元素不存在,就返回null。這個方法用起來非常方便,因為調用getChild().getText()可能會拋出NullPointerException異常。
1 ? ? for (int i = 0; i < list.size(); i++) {
2 ? ? ? ?Element node = (Element) list.get(i);
3 ? ? System.out.println("First Name : " +
4 ? ? ? node.getChildText("firstname"));
5 ? ? System.out.println("Last Name : " +
6 ? ? ? node.getChildText("lastname"));
7 ? ? }
7.最后,關閉try語句塊,并添加如下catch語句塊處理可能遇到的異常。
1 ? ? } catch (IOException io) {
2 ? ? ? ? ? System.out.println(io.getMessage());
3 ? ? } catch (JDOMException jdomex) {
4 ? ? ? ? ? System.out.println(jdomex.getMessage());
5 ? ? }
完整代碼如下:
1import java.io.File;
2import java.io.IOException;
3import java.util.List;
4import org.jdom2.Document;
5import org.jdom2.Element;
6import org.jdom2.JDOMException;
7import org.jdom2.input.SAXBuilder;
8public class TestJdom {
9 ? public static void main(String[] args){
10 ? ? ?TestJdom test = new TestJdom();
11 ? ? ?test.parseXml("C:/dummyxml.com");
12 ? }
13 ? public void parseXml(String fileName){
14 ? ? ?SAXBuilder builder = new SAXBuilder();
15 ? ? ?File file = new File(fileName);
16 ? ? ?try {
17 ? ? ? ? Document document = (Document) builder.build(file);
18 ? ? ? ? Element rootNode = document.getRootElement();
19 ? ? ? ? List list = rootNode.getChildren("author");
20 ? ? ? ? for (int i = 0; i < list.size(); i++) {
21 ? ? ? ? ? ?Element node = (Element) list.get(i);
22 ? ? ? ? ? ?System.out.println("First Name : " +
23 ? ? ? ? ? ? ? ?node.getChildText("firstname"));
24 ? ? ? ? ? ?System.out.println("Last Name : " +
25 ? ? ? ? ? ? ? ?node.getChildText("lastname"));
26 ? ? ? ? }
27 ? ? ?} catch (IOException io) {
28 ? ? ? ? System.out.println(io.getMessage());
29 ? ? ?} catch (JDOMException jdomex) {
30 ? ? ? ? System.out.println(jdomex.getMessage());
31 ? ? ?}
32 ? }
33}
XML解析器類型多樣,每種解析器都各有優點。Dom Parser。這種解析器會把文檔的完整內容加載到內存中,并在內存中創建自己的層次樹。SAX Parser:這種解析器不會把整個文檔全部加載到內存中,文檔的解析基于事件觸發。JDOM Parser:JDOM解析器采用類似DOM解析器的方式解析文檔,但是更加便捷。StAX Parser:這種解析器采用類似于SAX解析器的方式處理文檔,但是效率更高。XPath Parser:這類解析器基于路徑表達式來解析文檔,經常與XSLT一起使用。DOM4J Parser:這是一個使用Java集合框架(該框架提供了對DOM、SAX、JAXP的支持)來解析XML、XPath、XSLT的Java庫。
本文摘自《Java數據科學指南》
《Java數據科學指南》
[加]魯什迪·夏姆斯(Rushdi Shams)??著
點擊封面購買紙書
學習MLlib、DL4j和Weka等開源庫,掌握實用的Java數據科學技能
本書旨在通過Java編程來引導讀者更好地完成數據科學任務。本書通過9章內容,詳細地介紹了數據獲取與清洗、索引的建立和檢索數據、統計分析、數據學習、信息的提取、大數據處理、深度學習、數據可視化等重要主題。
Java 數據挖掘
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。