解决CsvWriter:中文乱码末尾行多一行空格(/r)非第一列空字符串""显示null问题
Posted miaomiaokaixin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决CsvWriter:中文乱码末尾行多一行空格(/r)非第一列空字符串""显示null问题相关的知识,希望对你有一定的参考价值。
一:主要内容
- 解决CsvWriter存csv,csv文件打开后中文乱码问题
- 解决CsvWriter存csv,csv文件最后一行总是多一行空行的问题
- 解决CsvWriter存csv,csv文件不是第一列的时候,想存入""即空字符串无法存入显示null的问题
二:解决问题前:需要做的事情
因为网上的CsvWrite的jar包导入到我们的工程中是class文件,针对上面的问题是无法修改源码的,但是我们又想用这个工具来操作csv,所以可以在自己的工程中首先pom引用这个jar包
<dependency> <groupId>net.sourceforge.javacsv</groupId> <artifactId>javacsv</artifactId> <version>2.0</version> </dependency>
然后我们在自己的工程中创建一个类:CsvWriterExtend来继承CsvWriter,这样我们就能用网上CsvWriter这个jar中的方法,还能基于这个去修改代码,解决上诉的问题
public class CsvWriterExtend extends CsvWriter {}
三:优化解决CsvWriter工具存在的三个问题:中文乱码、末尾行多一行空格(/r)、非第一列空字符串""显示null问题
此处废话不多说,先上CsvWriterExtend类的代码,然后我们在代码中红色标注的地方会有说明,这段用来解决什么问题的,说明一下:CsvWriterExtend代码只是在CsvWriter类的基础上做了一些修改
1 /** 2 * Copyright (C), 2015-2019, XXX有限公司 3 * FileName: CsvWriterExtend 4 * Author: yml 5 * Date: 2019/5/17 15:42 6 * Description: 7 * History: 8 * <author> <time> <version> <desc> 9 * 俞美玲 2019.5.17 1.0.0 优化CsvWriter工具 10 */ 11 package com.test.csv.tool; 12 13 import com.csvreader.CsvWriter; 14 15 import java.io.*; 16 import java.nio.charset.Charset; 17 18 /** 19 * @创建人 yumeiling 20 * @创建时间 2019/5/17 21 * @描述 优化CsvWriter工具,解决了写入csv打开后中文乱码问题,解决了写入csv最后一行有/r换行的问题 22 * 23 */ 24 public class CsvWriterExtend extends CsvWriter { 25 private PrintWriter outputStream; 26 private String fileName; 27 private boolean firstColumn; 28 private boolean useCustomRecordDelimiter; 29 private Charset charset; 30 private CsvWriterExtend.UserSettings userSettings; 31 private boolean initialized; 32 private boolean closed; 33 public static final int ESCAPE_MODE_DOUBLED = 1; 34 public static final int ESCAPE_MODE_BACKSLASH = 2; 35 private String sheetFirstName; 36 37 public CsvWriterExtend(String var1, char var2, Charset var3,String sheetFirstName) { 38 super(var1,var2,var3); 39 this.outputStream = null; 40 this.fileName = null; 41 this.firstColumn = true; 42 this.useCustomRecordDelimiter = false; 43 this.charset = null; 44 this.userSettings = new CsvWriterExtend.UserSettings(); 45 this.initialized = false; 46 this.closed = false; 47 //1:这里加了一个csv表头的第一个名字字段,用来解决第一个问题:中文乱码问题 48 this.sheetFirstName = sheetFirstName; 49 if (var1 == null) { 50 throw new IllegalArgumentException("Parameter fileName can not be null."); 51 } else if (var3 == null) { 52 throw new IllegalArgumentException("Parameter charset can not be null."); 53 } else { 54 this.fileName = var1; 55 this.userSettings.Delimiter = var2; 56 this.charset = var3; 57 } 58 } 59 60 public CsvWriterExtend(Writer var1, char var2) { 61 super(var1,var2); 62 this.outputStream = null; 63 this.fileName = null; 64 this.firstColumn = true; 65 this.useCustomRecordDelimiter = false; 66 this.charset = null; 67 this.userSettings = new CsvWriterExtend.UserSettings(); 68 this.initialized = false; 69 this.closed = false; 70 if (var1 == null) { 71 throw new IllegalArgumentException("Parameter outputStream can not be null."); 72 } else { 73 this.outputStream = new PrintWriter(var1); 74 this.userSettings.Delimiter = var2; 75 this.initialized = true; 76 } 77 } 78 79 public CsvWriterExtend(OutputStream var1, char var2, Charset var3) { 80 this(new OutputStreamWriter(var1, var3), var2); 81 } 82 83 public char getDelimiter() { 84 return this.userSettings.Delimiter; 85 } 86 87 public void setDelimiter(char var1) { 88 this.userSettings.Delimiter = var1; 89 } 90 91 public char getRecordDelimiter() { 92 return this.userSettings.RecordDelimiter; 93 } 94 95 public void setRecordDelimiter(char var1) { 96 this.useCustomRecordDelimiter = true; 97 this.userSettings.RecordDelimiter = var1; 98 } 99 100 public char getTextQualifier() { 101 return this.userSettings.TextQualifier; 102 } 103 104 public void setTextQualifier(char var1) { 105 this.userSettings.TextQualifier = var1; 106 } 107 108 public boolean getUseTextQualifier() { 109 return this.userSettings.UseTextQualifier; 110 } 111 112 public void setUseTextQualifier(boolean var1) { 113 this.userSettings.UseTextQualifier = var1; 114 } 115 116 public int getEscapeMode() { 117 return this.userSettings.EscapeMode; 118 } 119 120 public void setEscapeMode(int var1) { 121 this.userSettings.EscapeMode = var1; 122 } 123 124 public void setComment(char var1) { 125 this.userSettings.Comment = var1; 126 } 127 128 public char getComment() { 129 return this.userSettings.Comment; 130 } 131 132 public boolean getForceQualifier() { 133 return this.userSettings.ForceQualifier; 134 } 135 136 public void setForceQualifier(boolean var1) { 137 this.userSettings.ForceQualifier = var1; 138 } 139 140 public void write(String var1, boolean var2) throws IOException { 141 this.checkClosed(); 142 this.checkInit(); 143 if (var1 == null) { 144 var1 = ""; 145 } 146 //2:这里加了一个判断条件,用来解决第一个问题:中文乱码问题 147 //加的目的是:如果是写入bom则bom后面不追加逗号,即在bom后面和第一个表头前面,即两者之间不追加逗号,sheetFirstName为第一个表头的名字根据实际传入 148 if (!this.firstColumn && !var1.contentEquals(sheetFirstName) ){ 149 this.outputStream.write(this.userSettings.Delimiter); 150 } 151 152 boolean var3 = this.userSettings.ForceQualifier; 153 if (!var2 && var1.length() > 0) { 154 var1 = var1.trim(); 155 } 156 157 if (!var3 && this.userSettings.UseTextQualifier && (var1.indexOf(this.userSettings.TextQualifier) > -1 || var1.indexOf(this.userSettings.Delimiter) > -1 || !this.useCustomRecordDelimiter && (var1.indexOf(10) > -1 || var1.indexOf(13) > -1) || this.useCustomRecordDelimiter && var1.indexOf(this.userSettings.RecordDelimiter) > -1 || this.firstColumn && var1.length() > 0 && var1.charAt(0) == this.userSettings.Comment || this.firstColumn && var1.length() == 0)) { 158 var3 = true; 159 } 160 161 if (this.userSettings.UseTextQualifier && !var3 && var1.length() > 0 && var2) { 162 char var4 = var1.charAt(0); 163 if (var4 == ‘ ‘ || var4 == ‘\\t‘) { 164 var3 = true; 165 } 166 167 if (!var3 && var1.length() > 1) { 168 char var5 = var1.charAt(var1.length() - 1); 169 if (var5 == ‘ ‘ || var5 == ‘\\t‘) { 170 var3 = true; 171 } 172 } 173 } 174 //3:这里加了一个if语句,是为了解决第三个问题:csv""显示null的问题 175 if(!this.firstColumn && var1.length()==0){ 176 var3=true; 177 } 178 if (var3) { 179 this.outputStream.write(this.userSettings.TextQualifier); 180 if (this.userSettings.EscapeMode == 2) { 181 var1 = replace(var1, "\\\\", "\\\\\\\\"); 182 var1 = replace(var1, "" + this.userSettings.TextQualifier, "\\\\" + this.userSettings.TextQualifier); 183 } else { 184 var1 = replace(var1, "" + this.userSettings.TextQualifier, "" + this.userSettings.TextQualifier + this.userSettings.TextQualifier); 185 } 186 } else if (this.userSettings.EscapeMode == 2) { 187 var1 = replace(var1, "\\\\", "\\\\\\\\"); 188 var1 = replace(var1, "" + this.userSettings.Delimiter, "\\\\" + this.userSettings.Delimiter); 189 if (this.useCustomRecordDelimiter) { 190 var1 = replace(var1, "" + this.userSettings.RecordDelimiter, "\\\\" + this.userSettings.RecordDelimiter); 191 } else { 192 var1 = replace(var1, "\\r", "\\\\\\r"); 193 var1 = replace(var1, "\\n", "\\\\\\n"); 194 } 195 196 if (this.firstColumn && var1.length() > 0 && var1.charAt(0) == this.userSettings.Comment) { 197 if (var1.length() > 1) { 198 var1 = "\\\\" + this.userSettings.Comment + var1.substring(1); 199 } else { 200 var1 = "\\\\" + this.userSettings.Comment; 201 } 202 } 203 } 204 205 206 this.outputStream.write(var1); 207 208 if (var3) { 209 this.outputStream.write(this.userSettings.TextQualifier); 210 } 211 212 this.firstColumn = false; 213 } 214 215 public void write(String var1) throws IOException { 216 this.write(var1, false); 217 } 218 219 public void writeComment(String var1) throws IOException { 220 this.checkClosed(); 221 this.checkInit(); 222 this.outputStream.write(this.userSettings.Comment); 223 this.outputStream.write(var1); 224 if (this.useCustomRecordDelimiter) { 225 this.outputStream.write(this.userSettings.RecordDelimiter); 226 } else { 227 this.outputStream.println(); 228 } 229 230 this.firstColumn = true; 231 } 232 233 public void writeRecord(String[] var1, boolean var2) throws IOException { 234 if (var1 != null && var1.length > 0) { 235 for(int var3 = 0; var3 < var1.length; ++var3) { 236 this.write(var1[var3], var2); 237 } 238 239 this.endRecord(); 240 } 241 242 } 243 244 public void writeRecord(String[] var1) throws IOException { 245 this.writeRecord(var1, false); 246 } 247 248 public void writeLastRecord(String[] var1) throws IOException { 249 this.writeLastRecord(var1, false); 250 } 251 252 //4:这里加了两个方法writeLastRecord和endLastRecord,用来解决第二个问题:某尾总是多一行空行的问题 253 public void writeLastRecord(String[] var1, boolean var2) throws IOException { 254 if (var1 != null && var1.length > 0) { 255 for(int var3 = 0; var3 < var1.length; ++var3) { 256 this.write(var1[var3], var2); 257 } 258 259 this.endLastRecord(); 260 } 261 262 } 263 public void endLastRecord() throws IOException { 264 this.checkClosed(); 265 this.checkInit(); 266 if (this.useCustomRecordDelimiter) { 267 this.outputStream.write(this.userSettings.RecordDelimiter); 268 } else {//主要在下面这一行,当执行这个方法来结尾的时候是不追加换行符的 269 this.outputStream.print(""); 270 } 271 272 this.firstColumn = true; 273 } 274 275 public void endRecord() throws IOException { 276 this.checkClosed(); 277 this.checkInit(); 278 if (this.useCustomRecordDelimiter) { 279 this.outputStream.write(this.userSettings.RecordDelimiter); 280 } else { 281 this.outputStream.println(); 282 } 283 284 this.firstColumn = true; 285 } 286 287 private void checkInit() throws IOException { 288 if (!this.initialized) { 289 if (this.fileName != null) { 290 this.outputStream = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.fileName), this.charset)); 291 } 292 293 this.initialized = true; 294 } 295 296 } 297 298 public void flush() { 299 this.outputStream.flush(); 300 } 301 302 public void close() { 303 if (!this.closed) { 304 this.close(true); 305 this.closed = true; 306 } 307 308 } 309 310 private void close(boolean var1) { 311 if (!this.closed) { 312 if (var1) { 313 this.charset = null; 314 } 315 316 try { 317 if (this.initialized) { 318 this.outputStream.close(); 319 } 320 } catch (Exception var3) { 321 ; 322 } 323 324 this.outputStream = null; 325 this.closed = true; 326 } 327 328 } 329 330 private void checkClosed() throws IOException { 331 if (this.closed) { 332 throw new IOException("This instance of the CsvWriter class has already been closed."); 333 } 334 } 335 336 protected void finalize() { 337 this.close(false); 338 } 339 340 public static String replace(String var0, String var1, String var2) { 341 int var3 = var1.length(); 342 int var4 = var0.indexOf(var1); 343 if (var4 <= -1) { 344 return var0; 345 } else { 346 StringBuffer var5 = new StringBuffer(); 347 348 int var6; 349 for(var6 = 0; var4 != -1; var4 = var0.indexOf(var1, var6)) { 350 var5.append(var0.substring(var6, var4)); 351 var5.append(var2); 352 var6 = var4 + var3; 353 } 354 355 var5.append(var0.substring(var6)); 356 return var5.toString(); 357 } 358 } 359 360 private class UserSettings { 361 public char TextQualifier = ‘"‘; 362 public boolean UseTextQualifier = true; 363 public char Delimiter = ‘,‘; 364 public char RecordDelimiter = 0; 365 public char Comment = ‘#‘; 366 public int EscapeMode = 1; 367 public boolean ForceQualifier = false; 368 369 public UserSettings() { 370 } 371 } 372 373 private class Letters { 374 public static final char LF = ‘\\n‘; 375 public static final char CR = ‘\\r‘; 376 public static final char QUOTE = ‘"‘; 377 public static final char COMMA = ‘,‘; 378 public static final char SPACE = ‘ ‘; 379 public static final char TAB = ‘\\t‘; 380 public static final char POUND = ‘#‘; 381 public static final char BACKSLASH = ‘\\\\‘; 382 public static final char NULL = ‘\\u0000‘; 383 384 private Letters() { 385 } 386 } 387 }
四:调用写好的CsvWriterExtend类,实现写csv功能
下面给出写csv的方法,如下红色部分是解决上诉三个问题的关键:中文乱码、末尾行多一行空格(/r)、非第一列空字符串""显示null问题
1 /** 2 * 写csv方法 3 */ 4 public static <T> void writeCSV(Collection<T> dataset, String csvFilePath, String[] csvHeaders) { 5 6 try { 7 //集合长度,和循环次数,当循环到最后一条记录时不在末尾插入换行符 8 int datasetLength = dataset.size(); 9 int loop=1; 10 // 定义路径,分隔符,编码d,第一个表头名称 11 //如果是写入bom则bom后面不追加逗号,即在bom后面和第一个表头前面,即两者之间不追加逗号,sheetFirstName为第一个表头的名字根据实际传入 12 CsvWriterExtend csvWriter = new CsvWriterExtend(csvFilePath, ‘,‘, Charset.forName("UTF-8"),"username"); // 写表头 13 //如果是写入bom解决文件乱码,则不在bom后面追加,号分隔符 14 csvWriter.write("\\ufeff"); 15 csvWriter.writeRecord(csvHeaders); // 写内容 16 // 遍历集合 17 Iterator<T> it = dataset.iterator(); 18 while (it.hasNext()) { 19 T t = (T) it.next(); 20 //获取类属性 21 Field[] fields = t.getClass().getDeclaredFields(); 22 String[] csvContent=new String[fields.length]; 23 for (short i = 0; i < fields.length; i++) { 24 Field field = fields[i]; 25 String fieldName = field.getName(); 26 String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); 27 try { 28 Class tCls = t.getClass(); 29 Method getMethod = tCls.getMethod(getMethodName,new Class[] {}); 30 Object value = getMethod.invoke(t, new Object[] {}); 31 if (value == null) { 32 continue; 33 } 34 //取值并赋给数组 35 String textvalue=value.toString(); 36 csvContent[i]=textvalue; 37 }catch (Exception e) { 38 e.getStackTrace(); 39 } 40 } 41 if(loop!=datasetLength){ 42 //迭代插入记录 43 csvWriter.writeRecord(csvContent); 44 }else{ 45 //插入最后一条记录 46 csvWriter.writeLastRecord(csvContent); 47 } 48 49 loop=loop+1; 50 for(String csvs:csvContent) { 51 System.out.println("记录数据:" + csvs); 52 } 53 } csvWriter.close(); 54 System.out.println("<--------CSV文件写入成功-------->"); 55 } catch (IOException e) { 56 e.printStackTrace(); 57 } 58 }
调用csv方法即执行类
1 public static void main(String[] args) throws Exception{ 2 //造测试数据 3 List<DataEntity> data = CreateDataModel.createUserData(); 4 String csvFilePath = "E://data.csv"; 5 //表头名称 6 //注册用户名、注册密码、登录用户名、登录密码、记住我、邮箱、分类名称、文章标题、文章路径、文章标签、文章内容、评论文章、期望结果 7 String[] csvHeaders = { "username", "password", "loginusername","loginpassword","remeber","email","cname","title","slug","tags","content","comment","expectresult" }; 8 CreateDataModel.writeCSV(data,csvFilePath,csvHeaders); 9 10 }
生成csv的效果如图:
四:备注
如果想看更详细的代码,可参考我的github地址,如上csv的生成已全部上传github中,地址如下:
https://github.com/mmkxyu/autotest.git
博文均为原创文章,转载请注明出处,感谢!
以上是关于解决CsvWriter:中文乱码末尾行多一行空格(/r)非第一列空字符串""显示null问题的主要内容,如果未能解决你的问题,请参考以下文章
为啥 csvwriter.writerow() 在每个字符后加一个逗号?