遊戲開發中,讀取策劃給的配置表是必不可少的,我在以前公司,策劃給的是xml表來讀取,如今公司策劃給的是CSV表來讀取,其實大同小異,也並非什麼難點,我就簡單分享下Java如何讀取XML文件和CSV文件。如下工具類可隨意拿去使用。java
若是是Maven項目,則依賴如下包(若是不是,則本身在項目添加配置相應jar包)node
<!-- dom4j begin --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- dom4j end --> <!--xml轉換 --> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.3</version> </dependency>
讀取XML文件應該是比較常見的,用dom4j就能夠很簡單的讀出來,首先準備一個XML文件,來映射出全部的XML資源文件的名字,資源文件以下:sql
dataConfig.xml <?xml version="1.0" encoding="UTF-8"?> <config> <file name="Test.xml" /> </config>
示例資源XML文件以下數據庫
Test.xml <?xml version="1.0" encoding="UTF-8"?> <dataset> <Test attr1="" attr2="" attr3="" /> </dataset>
Java項目中建一個類,變量名與XML中屬性名相同,Java代碼經過這個XML文件來找出全部的資源XML文件,而後將XML文件中的屬性值所有讀取到類中,再把類放進Map,加載到內存中,這樣,每次須要讀取配置表,就能夠直接從內存中讀取,快速且方便
如下是XmlDataLoader.java,做用是從數據表中讀取屬性值到內存數組
package com.hjc._36.util.xml; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hjc._36.core.GameInit; import com.hjc._36.util.csv.TempletService; /** * 1 全部的template類都必須在一個包中 2 經過讀取xml 遍歷全部的Element,經過Element.name和 package 獲得具體的 * className; 3 經過Element的 attr 獲取方法,而且根據parameterType 強制轉換類型 * * @author Administrator * */ public class XmlDataLoader { private String packageName; private String config; // 每一行就是一個配置文件名字 private static Logger logger = LoggerFactory.getLogger(XmlDataLoader.class); public static int ActivityMaxValue; public XmlDataLoader(String packageName, String config) { this.packageName = packageName; this.config = config; } /** * 調用load方法加載全部的配置文件 */ public void load() { SAXReader reader = new SAXReader(); try { Document doc = reader.read(new InputStreamReader(this.getClass() .getResourceAsStream(this.config))); List<?> nodes = doc.selectNodes("/config/file"); Map<String, List<?>> dataMap = new HashMap<String, List<?>>(); List<String> files = new LinkedList<String>(); for (Object n : nodes) { Element t = (Element) n; String f = t.attributeValue("name"); List<?> dataList = this.loadFile(f, true); for (Object o : dataList) { TempletService.getInstance().registerObject(o, dataMap); } files.add(f); } logger.info("讀取配置完畢,準備afterLoad"); TempletService.templetMap = dataMap; TempletService.getInstance().afterLoad(); logger.info("afterLoad 完畢"); } catch (DocumentException e) { e.printStackTrace(); } finally { } } private List<?> loadFile(String file, boolean exitWhenFail) { try { file = GameInit.confFileBasePath + file; logger.info("load file: {}", file); InputStream resourceAsStream = this.getClass().getResourceAsStream( file); if (resourceAsStream == null) { logger.error("文件不存在:" + file); if (exitWhenFail) { System.exit(0); } return null; } return loadFromStream(resourceAsStream); } catch (Exception e) { logger.error("載入文件出錯:" + file); e.printStackTrace(); System.exit(0); } finally { } return Collections.EMPTY_LIST; } public List<?> loadFromStream(InputStream resourceAsStream) throws UnsupportedEncodingException, DocumentException, InstantiationException, IllegalAccessException { SAXReader reader = new SAXReader(); Document doc = reader.read(new InputStreamReader(resourceAsStream, "utf-8")); Element dataSet = (Element) doc.selectNodes("/dataset").get(0); List<?> nodes = dataSet.elements(); // get clazz String className = this.packageName + ((Element) nodes.get(0)).getName(); try { Class<?> classObject = Class.forName(className); if (classObject == null) { logger.error("未找到類" + className); return null; } // Get all the declared fields Field[] fields = classObject.getDeclaredFields(); LinkedList<Field> fieldList = new LinkedList<Field>(); int length = fields.length; for (int i = -1; ++i < length;) { boolean isStaticField = Modifier.isStatic(fields[i] .getModifiers()); if (isStaticField) continue; boolean isTransientField = Modifier.isTransient(fields[i] .getModifiers()); if (isTransientField) continue; fieldList.add(fields[i]); } // Get all the declared fields of supper class Class<?> tmp = classObject; while ((tmp = tmp.getSuperclass()) != Object.class) { System.out.print("the extends class is" + tmp.getName()); fields = tmp.getDeclaredFields(); length = fields.length; if (length == 0) continue; for (int i = -1; ++i < length;) { boolean isStaticField = Modifier.isStatic(fields[i] .getModifiers()); if (isStaticField) continue; boolean isTransientField = Modifier.isTransient(fields[i] .getModifiers()); if (isTransientField) continue; fieldList.add(fields[i]); } } // The truly need to return object List<Object> instances = new ArrayList<Object>(nodes.size()); Object instance = null; String fieldName = null; String fieldValue = null; for (Object node : nodes) { if (node != null) { instance = classObject.newInstance(); boolean ok = false; Element row = (Element) node; for (Field field : fieldList) { fieldName = field.getName(); fieldValue = row.attributeValue(fieldName); if (fieldValue == null) continue; try { this.setField(instance, field, fieldValue); ok = true; } catch (Exception e) { logger.error("類名稱是" + className + "的屬性" + fieldName + "沒有被成功賦予靜態數據"); continue; } } if (ok) instances.add(instance); } } return instances; } catch (ClassNotFoundException e1) { e1.printStackTrace(); logger.error("未找到類" + className); return null; } } public void reload() { } /** * * @Title: setUnknowField * @Description: * @param ob * @param f * @param v * @throws IllegalArgumentException * @throws IllegalAccessException */ private void setField(Object obj, Field f, String v) throws IllegalArgumentException, IllegalAccessException { f.setAccessible(true); if (f.getType() == int.class) { f.setInt(obj, Integer.parseInt(v)); } else if (f.getType() == short.class) { f.setShort(obj, Short.parseShort(v)); } else if (f.getType() == byte.class) { f.setByte(obj, Byte.parseByte(v)); } else if (f.getType() == long.class) { f.setLong(obj, Long.parseLong(v)); } else if (f.getType() == double.class) { f.setDouble(obj, Double.parseDouble(v)); } else if (f.getType() == float.class) { f.setFloat(obj, Float.parseFloat(v)); } else if (f.getType() == Timestamp.class) { f.set(obj, Timestamp.valueOf(v)); } else { f.set(obj, f.getType().cast(v)); } } /** * Test Code * * @param args */ public static void main(String[] args) { XmlDataLoader dl = new XmlDataLoader("com.hjc._36.template.", "/dataConfig.xml"); dl.load(); } }
如上的測試方法中看出,調用方式很簡單,只需加載到映射XML文件,便可讀取全部的配置XML文件,調用load()方法後,配置表就所有加載到了內存,咱們經過如下類——TempletService.java來對這些配置數據進行管理:app
package com.hjc._36.util.csv; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 添加一個數據表須要作如下幾步 * 1.在包com.hjc._36.template下建立對應的模板類,類名與數據文件一致 * 2.在src/main/resources/xml/中添加模板數據文件 * 3.在src/main/resources/dataConfig.xml加入剛纔的模板數據文件 * * @author 何金成 * */ public class TempletService { public static Logger log = LoggerFactory.getLogger(TempletService.class); public static TempletService templetService = new TempletService(); /** * key:實體名 value:該實體下的全部模板數據 */ public static Map<String, List<?>> templetMap = new HashMap<String, List<?>>(); public TempletService() { } public static TempletService getInstance() { return templetService; } /** * 獲取該實體類下全部模板數據 * * @param beanName * @return */ @SuppressWarnings("unchecked") public static List listAll(String beanName) { return templetMap.get(beanName); } /** * @Title: registerObject * @Description: 註冊對象到對應類的List中 * @param o * @param dataMap * @return void * @throws */ public void registerObject(Object o, Map<String, List<?>> dataMap) { add(o.getClass().getSimpleName(), o, dataMap); } @SuppressWarnings("unchecked") private void add(String key, Object data, Map<String, List<?>> dataMap) { List list = dataMap.get(key); if (list == null) { list = new ArrayList(); dataMap.put(key, list); } list.add(data); } public void afterLoad() { // 加載後處理 // List tests = TempletService.listAll(Test.class.getSimpleName()); // for (Object object : tests) { // Test test = (Test)object; // System.out.print(test.getEquipLv()); // System.out.print(","+test.getLv1()); // System.out.print(","+test.getLv2()); // System.out.print(","+test.getLv3()); // System.out.print(","+test.getLv4()); // System.out.print(","+test.getLv5()); // System.out.print(","+test.getLv6()); // System.out.print(","+test.getLv7()); // System.out.print(","+test.getLv8()); // System.out.print(","+test.getLv9()); // System.out.println(","+test.getLv10()); // } } public void loadCanShu() { // 加載全局參數xml配置 } }
在load()中,變用到了上述類的registerObject方法來添加數據,在使用時,只須要調用listAll方法便可,在上面代碼中的測試代碼已經寫明瞭用法。dom
Java讀CSV文件與讀XML文件一模一樣,大體思路同上工具
dataConfig.xml <?xml version="1.0" encoding="UTF-8"?> <config> <file name="Test.csv" /> <file name="JunjichuUpgrade.csv" /> <file name="Card.csv" /> <file name="Equip.csv" /> <file name="Prop.csv" /> </config>
Test.csv以下:測試
Test.csv 等級,1星,2星,3星,4星,5星,6星,7星,8星,9星,10星,11星,12星,13星,14星,15星,16星,17星,18星,19星,20星,21星,22星,23星,24星,25星 EquipLv,Lv1,Lv2,Lv3,Lv4,Lv5,Lv6,Lv7,Lv8,Lv9,Lv10,Lv11,Lv12,Lv13,Lv14,Lv15,Lv16,Lv17,Lv18,Lv19,Lv20,Lv21,Lv22,Lv23,Lv24,Lv25 10,2,4,8,12,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 20,6,12,24,36,48,60,72,84,96,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 30,12,24,48,72,96,120,144,168,192,216,240,264,288,312,336,0,0,0,0,0,0,0,0,0,0 40,20,40,80,120,160,200,240,280,320,360,400,440,480,520,560,0,0,0,0,0,0,0,0,0,0 50,30,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960,1020,1080,1140,0,0,0,0,0 60,42,84,168,252,336,420,504,588,672,756,840,924,1008,1092,1176,1260,1344,1428,1512,1596,0,0,0,0,0 70,56,112,224,336,448,560,672,784,896,1008,1120,1232,1344,1456,1568,1680,1792,1904,2016,2128,0,0,0,0,0 80,72,144,288,432,576,720,864,1008,1152,1296,1440,1584,1728,1872,2016,2160,2304,2448,2592,2736,2880,3024,3168,3312,3456 90,90,180,360,540,720,900,1080,1260,1440,1620,1800,1980,2160,2340,2520,2700,2880,3060,3240,3420,3600,3780,3960,4140,4320 100,110,220,440,660,880,1100,1320,1540,1760,1980,2200,2420,2640,2860,3080,3300,3520,3740,3960,4180,4400,4620,4840,5060,5280
如上CSV表你們也能看出來,CSV文件中的數據是以必定規律呈現的,解析CSV即是根據CSV文件的這些特性來進行解析的,解析工具類CsvParser.java代碼以下:ui
package com.hjc._36.util.csv; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; //JAVA 操做 excel 中的 .csv文件格式 public class CsvParser { private BufferedReader bufferedreader = null; private List list = new ArrayList(); public CsvParser() { } public CsvParser(InputStream inStream) throws IOException { InputStreamReader isr = new InputStreamReader(inStream, "UTF-8"); bufferedreader = new BufferedReader(isr); String stemp; while ((stemp = bufferedreader.readLine()) != null) { list.add(stemp); } } public List getList() throws IOException { return list; } public List getListWithNoHeader() throws IOException { return list.subList(2, list.size()); } // 獲得csv文件的行數 public int getRowNum() { return list.size(); } // 獲得csv文件的列數 public int getColNum() { if (!list.toString().equals("[]")) { if (list.get(0).toString().contains(",")) { // csv文件中,每列之間的是用','來分隔的 return list.get(0).toString().split(",").length; } else if (list.get(0).toString().trim().length() != 0) { return 1; } else { return 0; } } else { return 0; } } // 取得指定行的值 public String getRow(int index) { if (this.list.size() != 0) return (String) list.get(index); else return null; } // 取得指定列的值 public String getCol(int index) { if (this.getColNum() == 0) { return null; } StringBuffer scol = new StringBuffer(); String temp = null; int colnum = this.getColNum(); if (colnum > 1) { for (Iterator it = list.iterator(); it.hasNext();) { temp = it.next().toString(); scol = scol.append(temp.split(",")[index] + ","); } } else { for (Iterator it = list.iterator(); it.hasNext();) { temp = it.next().toString(); scol = scol.append(temp + ","); } } String str = new String(scol.toString()); str = str.substring(0, str.length() - 1); return str; } // 取得指定行,指定列的值 public String getString(int row, int col) { String temp = null; int colnum = this.getColNum(); if (colnum > 1) { temp = list.get(row).toString().split(",")[col]; } else if (colnum == 1) { temp = list.get(row).toString(); } else { temp = null; } return temp; } public void CsvClose() throws IOException { this.bufferedreader.close(); } public List readCvs(String filename) throws IOException { CsvParser cu = new CsvParser(new FileInputStream(new File(filename))); List list = cu.getList(); return list; } public void createCsv(String biao, List list, String path) throws IOException { List tt = list; String data = ""; SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMdd"); Date today = new Date(); String dateToday = dataFormat.format(today); File file = new File(path + "resource/expert/" + dateToday + "importerrorinfo.csv"); if (!file.exists()) file.createNewFile(); else file.delete(); String str[]; StringBuilder sb = new StringBuilder(""); sb.append(biao); FileOutputStream writerStream = new FileOutputStream(file, true); BufferedWriter output = new BufferedWriter(new OutputStreamWriter( writerStream, "UTF-8")); for (Iterator itt = tt.iterator(); itt.hasNext();) { String fileStr = itt.next().toString(); // str = fileStr.split(","); // for (int i = 0; i <= str.length - 1; i++) { // 拆分紅數組 用於插入數據庫中 // System.out.print("str[" + i + "]=" + str[i] + " "); // } // System.out.println(""); sb.append(fileStr + "\r\n"); } output.write(sb.toString()); output.flush(); output.close(); } public static void main(String[] args) throws IOException { CsvParser test = new CsvParser(); List aaa = test.readCvs("D:/abc.csv"); for (int i = 0; i < aaa.size(); i++) { System.out.println(i + "---" + aaa.get(i)); String a = (String) aaa.get(i).toString().split(",")[0]; } System.out.println(aaa.size()); //String biao = "用戶名,姓名,排序,專家類型,業務專長,錯誤信息\n"; //test.createCsv(biao, aaa, "D:/"); // test.run("D:/abc.csv"); // HttpServletResponse response = null; // test.createCsv(response); // CSVReader reader = new CSVReader(new FileReader("D:/abc.csv")); // ColumnPositionMappingStrategy<Test> strat = new ColumnPositionMappingStrategy<Test>(); // strat.setType(Test.class); // String[] columns = new String[] {"EquipLv","Lv1","Lv2","Lv3","Lv4","Lv5","Lv6","Lv7","Lv8","Lv9","Lv10","Lv11","Lv12","Lv13","Lv14","Lv15"}; // the fields to bind do in your JavaBean // strat.setColumnMapping(columns); // CsvToBean<Test> csv = new CsvToBean<Test>(); // List<Test> list = csv.parse(strat, reader); // System.out.println(list.size()); // for (Test test : list) { // System.out.println(test.getEquipLv()); // System.out.print(test.getLv1()); // System.out.print(test.getLv2()); // System.out.print(test.getLv3()); // System.out.print(test.getLv4()); // System.out.print(test.getLv5()); // System.out.print(test.getLv6()); // System.out.print(test.getLv7()); // System.out.print(test.getLv8()); // System.out.print(test.getLv9()); // System.out.print(test.getLv10()); // System.out.print(test.getLv11()); // System.out.print(test.getLv12()); // System.out.print(test.getLv13()); // System.out.print(test.getLv14()); // System.out.print(test.getLv15()); // } } }
CSV解析與XML解析的區別僅限於此,XML使用SaxReader來進行解析,而CSV自定義解析方式(固然網上也有不少別得第三方解析工具),接下來的步驟與XML解析如出一轍,CsvDataLoader.java稍有不一樣的地方就是解析CSV文件的部分,代碼以下:
package com.hjc._36.util.csv; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hjc._36.core.GameInit; public class CsvDataLoader { public static Logger logger = LoggerFactory.getLogger(CsvDataLoader.class); private String packageName; private String config; // 每一行就是一個配置文件名字 public static int ActivityMaxValue; public CsvDataLoader(String packageName, String config) { this.packageName = packageName; this.config = config; } /** * 調用load方法加載全部的配置文件 */ public void load() { SAXReader reader = new SAXReader(); try { Document doc = reader.read(new InputStreamReader(this.getClass() .getResourceAsStream(this.config))); List<?> nodes = doc.selectNodes("/config/file"); Map<String, List<?>> dataMap = new HashMap<String, List<?>>(); List<String> files = new LinkedList<String>(); for (Object n : nodes) { Element t = (Element) n; String f = t.attributeValue("name"); List<?> dataList = this.loadFile(f, true); for (Object o : dataList) { TempletService.getInstance().registerObject(o, dataMap); } files.add(f); } logger.info("讀取配置完畢,準備afterLoad"); TempletService.templetMap = dataMap; TempletService.getInstance().afterLoad(); logger.info("afterLoad 完畢"); } catch (DocumentException e) { e.printStackTrace(); } finally { } } private List<?> loadFile(String file, boolean exitWhenFail) {// 讀文件 try { String clzName = file.replaceAll(".csv", ""); file = GameInit.confFileBasePath + file; logger.info("load file: {}", file); InputStream resourceAsStream = this.getClass().getResourceAsStream( file); if (resourceAsStream == null) { logger.error("文件不存在:" + file); if (exitWhenFail) { System.exit(0); } return null; } return loadFromStream(resourceAsStream, clzName); } catch (Exception e) { logger.error("載入文件出錯:" + file); e.printStackTrace(); System.exit(0); } finally { } return Collections.EMPTY_LIST; } public List<?> loadFromStream(InputStream resourceAsStream, String clzName) throws DocumentException, InstantiationException, IllegalAccessException, IOException {// 讀csv文件 CsvParser csvParser = new CsvParser(resourceAsStream); List<String> nodes = csvParser.getListWithNoHeader(); // get clazz String className = this.packageName + clzName; try { Class<?> classObject = Class.forName(className); if (classObject == null) { logger.error("未找到類" + className); return null; } // Get all the declared fields Field[] fields = classObject.getDeclaredFields(); LinkedList<Field> fieldList = new LinkedList<Field>(); int length = fields.length; for (int i = -1; ++i < length;) { boolean isStaticField = Modifier.isStatic(fields[i] .getModifiers()); if (isStaticField) continue; boolean isTransientField = Modifier.isTransient(fields[i] .getModifiers()); if (isTransientField) continue; fieldList.add(fields[i]); } // Get all the declared fields of supper class Class<?> tmp = classObject; while ((tmp = tmp.getSuperclass()) != Object.class) { System.out.print("the extends class is" + tmp.getName()); fields = tmp.getDeclaredFields(); length = fields.length; if (length == 0) continue; for (int i = -1; ++i < length;) { boolean isStaticField = Modifier.isStatic(fields[i] .getModifiers()); if (isStaticField) continue; boolean isTransientField = Modifier.isTransient(fields[i] .getModifiers()); if (isTransientField) continue; fieldList.add(fields[i]); } } // The truly need to return object List<Object> instances = new ArrayList<Object>(nodes.size()); Object instance = null; String fieldName = null; String fieldValue = null; for (String node : nodes) { if (node != null) { instance = classObject.newInstance(); boolean ok = false; // Element row = (Element) node; String[] values = node.split(",");// csv文件以英文逗號分割值 for (int i = 0; i < fieldList.size(); i++) { Field field = fieldList.get(i); fieldName = field.getName(); fieldValue = values[i]; if (fieldValue == null) continue; try { this.setField(instance, field, fieldValue); ok = true; } catch (Exception e) { logger.error("類名稱是" + className + "的屬性" + fieldName + "沒有被成功賦予靜態數據"); continue; } } if (ok) { instances.add(instance); } } } return instances; } catch (ClassNotFoundException e1) { e1.printStackTrace(); logger.error("未找到類" + className); return null; } } /** * * @Title: setUnknowField * @Description: * @param ob * @param f * @param v * @throws IllegalArgumentException * @throws IllegalAccessException */ private void setField(Object obj, Field f, String v) throws IllegalArgumentException, IllegalAccessException { f.setAccessible(true); if (f.getType() == int.class) { f.setInt(obj, Integer.parseInt(v)); } else if (f.getType() == short.class) { f.setShort(obj, Short.parseShort(v)); } else if (f.getType() == byte.class) { f.setByte(obj, Byte.parseByte(v)); } else if (f.getType() == long.class) { f.setLong(obj, Long.parseLong(v)); } else if (f.getType() == double.class) { f.setDouble(obj, Double.parseDouble(v)); } else if (f.getType() == float.class) { f.setFloat(obj, Float.parseFloat(v)); } else if (f.getType() == Timestamp.class) { f.set(obj, Timestamp.valueOf(v)); } else { f.set(obj, f.getType().cast(v)); } } /** * Test Code * * @param args */ public static void main(String[] args) { CsvDataLoader dl = new CsvDataLoader("com.hjc._36.template.", "/dataConfig.xml"); dl.load(); } }
最後load()加載事後,依然使用TempletService來進行管理,使用方法也相同,具體代碼參照上面。