mirror of
https://github.com/octopusYan/dayz-mod-translator.git
synced 2026-02-04 18:41:56 +08:00
Compare commits
10 Commits
v0.1.2
...
aa5ad064a8
| Author | SHA1 | Date | |
|---|---|---|---|
| aa5ad064a8 | |||
| 36bd572e38 | |||
| 2b8f30b72d | |||
| a4ff07ce7b | |||
| 3ed4979aa9 | |||
| 16aa917d09 | |||
| 5214485a36 | |||
| 5a9226901f | |||
| 223abc7ac5 | |||
| 1294fb5414 |
@ -8,9 +8,6 @@
|
|||||||
<br>
|
<br>
|
||||||
[](https://github.com/octopusYan/dayz-mod-translator)
|
[](https://github.com/octopusYan/dayz-mod-translator)
|
||||||

|

|
||||||
<br>
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|||||||
38
pom.xml
38
pom.xml
@ -30,15 +30,16 @@
|
|||||||
|
|
||||||
<junit.version>5.11.0</junit.version>
|
<junit.version>5.11.0</junit.version>
|
||||||
<javafx.version>21.0.4</javafx.version>
|
<javafx.version>21.0.4</javafx.version>
|
||||||
<slf4j.version>2.0.16</slf4j.version>
|
<slf4j.version>2.0.17</slf4j.version>
|
||||||
<logback.version>1.5.7</logback.version>
|
<logback.version>1.5.25</logback.version>
|
||||||
<fastjson.version>2.0.52</fastjson.version>
|
<fastjson.version>2.0.52</fastjson.version>
|
||||||
<common-lang3.version>3.16.0</common-lang3.version>
|
<common-lang3.version>3.20.0</common-lang3.version>
|
||||||
<common-io.version>2.17.0</common-io.version>
|
<common-io.version>2.21.0</common-io.version>
|
||||||
<common-exec.version>1.4.0</common-exec.version>
|
<common-exec.version>1.6.0</common-exec.version>
|
||||||
<lombok.version>1.18.32</lombok.version>
|
<lombok.version>1.18.42</lombok.version>
|
||||||
<jackson.version>2.15.4</jackson.version>
|
<jackson.version>3.0.3</jackson.version>
|
||||||
<ikonli.version>12.3.1</ikonli.version>
|
<ikonli.version>12.4.0</ikonli.version>
|
||||||
|
<jjwt.version>0.13.0</jjwt.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -66,11 +67,13 @@
|
|||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>${slf4j.version}</version>
|
<version>${slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
@ -124,10 +127,12 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- jackson -->
|
<!-- jackson -->
|
||||||
|
<!-- Source: https://mvnrepository.com/artifact/tools.jackson.dataformat/jackson-dataformat-yaml -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
<groupId>tools.jackson.dataformat</groupId>
|
||||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||||
<version>${jackson.version}</version>
|
<version>${jackson.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Hutool -->
|
<!-- Hutool -->
|
||||||
@ -135,7 +140,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
<version>5.8.33</version>
|
<version>5.8.43</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- https://kordamp.org/ikonli/ -->
|
<!-- https://kordamp.org/ikonli/ -->
|
||||||
@ -151,6 +156,19 @@
|
|||||||
<groupId>org.kordamp.ikonli</groupId>
|
<groupId>org.kordamp.ikonli</groupId>
|
||||||
<artifactId>ikonli-feather-pack</artifactId>
|
<artifactId>ikonli-feather-pack</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- jwt -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|||||||
@ -12,14 +12,15 @@ import cn.octopusyan.dmt.model.Translate;
|
|||||||
import cn.octopusyan.dmt.model.UpgradeConfig;
|
import cn.octopusyan.dmt.model.UpgradeConfig;
|
||||||
import cn.octopusyan.dmt.translate.TranslateApi;
|
import cn.octopusyan.dmt.translate.TranslateApi;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import tools.jackson.core.JacksonException;
|
||||||
|
import tools.jackson.databind.ObjectMapper;
|
||||||
|
import tools.jackson.dataformat.yaml.YAMLMapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -38,7 +39,12 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
public class ConfigManager {
|
public class ConfigManager {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ConfigManager.class);
|
private static final Logger logger = LoggerFactory.getLogger(ConfigManager.class);
|
||||||
public static ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
|
|
||||||
|
private static final ObjectMapper objectMapper = YAMLMapper.builder()
|
||||||
|
//对象的所有字段全部列入序列化
|
||||||
|
.changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL))
|
||||||
|
.changeDefaultPropertyInclusion(incl -> incl.withContentInclusion(JsonInclude.Include.NON_NULL))
|
||||||
|
.build();
|
||||||
|
|
||||||
public static final UpgradeConfig upgradeConfig = new UpgradeConfig();
|
public static final UpgradeConfig upgradeConfig = new UpgradeConfig();
|
||||||
public static final String DEFAULT_THEME = new PrimerLight().getName();
|
public static final String DEFAULT_THEME = new PrimerLight().getName();
|
||||||
@ -53,11 +59,6 @@ public class ConfigManager {
|
|||||||
|
|
||||||
private static ConfigModel configModel;
|
private static ConfigModel configModel;
|
||||||
|
|
||||||
static {
|
|
||||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
|
||||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void load() {
|
public static void load() {
|
||||||
configModel = loadConfig(Constants.CONFIG_FILE_PATH, ConfigModel.class);
|
configModel = loadConfig(Constants.CONFIG_FILE_PATH, ConfigModel.class);
|
||||||
if (configModel == null)
|
if (configModel == null)
|
||||||
@ -89,7 +90,7 @@ public class ConfigManager {
|
|||||||
public static void save() {
|
public static void save() {
|
||||||
try {
|
try {
|
||||||
objectMapper.writeValue(new File(Constants.CONFIG_FILE_PATH), configModel);
|
objectMapper.writeValue(new File(Constants.CONFIG_FILE_PATH), configModel);
|
||||||
} catch (IOException e) {
|
} catch (JacksonException e) {
|
||||||
logger.error("save config error", e);
|
logger.error("save config error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
package cn.octopusyan.dmt.common.manager.http;
|
package cn.octopusyan.dmt.common.manager.http;
|
||||||
|
|
||||||
import cn.octopusyan.dmt.common.enums.ProxySetup;
|
import cn.octopusyan.dmt.common.enums.ProxySetup;
|
||||||
import cn.octopusyan.dmt.common.manager.http.response.BodyHandler;
|
import cn.octopusyan.dmt.common.manager.http.response.DownloadBodyHandler;
|
||||||
import cn.octopusyan.dmt.common.util.JsonUtil;
|
import cn.octopusyan.dmt.common.util.JsonUtil;
|
||||||
import cn.octopusyan.dmt.model.ProxyInfo;
|
import cn.octopusyan.dmt.model.ProxyInfo;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import tools.jackson.databind.JsonNode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -111,8 +111,15 @@ public class HttpUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String postForm(String uri, JsonNode header, JsonNode param) throws IOException, InterruptedException {
|
public String postForm(String uri, JsonNode header, JsonNode param) throws IOException, InterruptedException {
|
||||||
|
return postForm(uri, header, param, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String postForm(String uri, JsonNode header, JsonNode param, JsonNode body) throws IOException, InterruptedException {
|
||||||
HttpRequest.Builder request = getRequest(uri + createFormParams(param), header)
|
HttpRequest.Builder request = getRequest(uri + createFormParams(param), header)
|
||||||
.POST(HttpRequest.BodyPublishers.noBody());
|
.POST(body == null ?
|
||||||
|
HttpRequest.BodyPublishers.noBody() :
|
||||||
|
HttpRequest.BodyPublishers.ofString(JsonUtil.toJsonString(body))
|
||||||
|
);
|
||||||
|
|
||||||
HttpResponse<String> response = httpClient.send(request.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
HttpResponse<String> response = httpClient.send(request.build(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
return response.body();
|
return response.body();
|
||||||
@ -130,7 +137,7 @@ public class HttpUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 下载处理器
|
// 下载处理器
|
||||||
var handler = BodyHandler.create(
|
var handler = DownloadBodyHandler.create(
|
||||||
Path.of(savePath),
|
Path.of(savePath),
|
||||||
StandardOpenOption.CREATE, StandardOpenOption.WRITE
|
StandardOpenOption.CREATE, StandardOpenOption.WRITE
|
||||||
);
|
);
|
||||||
@ -172,8 +179,8 @@ public class HttpUtil {
|
|||||||
for (Map.Entry<String, JsonNode> property : params.properties()) {
|
for (Map.Entry<String, JsonNode> property : params.properties()) {
|
||||||
String key = property.getKey();
|
String key = property.getKey();
|
||||||
JsonNode value = params.get(key);
|
JsonNode value = params.get(key);
|
||||||
if (value.isTextual()) {
|
if (value.isString()) {
|
||||||
String value_ = URLEncoder.encode(String.valueOf(value.asText()), StandardCharsets.UTF_8);
|
String value_ = URLEncoder.encode(String.valueOf(value.asString()), StandardCharsets.UTF_8);
|
||||||
formParams.append("&").append(key).append("=").append(value_);
|
formParams.append("&").append(key).append("=").append(value_);
|
||||||
} else if (value.isNumber()) {
|
} else if (value.isNumber()) {
|
||||||
formParams.append("&").append(key).append("=").append(value);
|
formParams.append("&").append(key).append("=").append(value);
|
||||||
|
|||||||
@ -21,16 +21,16 @@ import java.util.function.Consumer;
|
|||||||
* @author octopus_yan
|
* @author octopus_yan
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class BodyHandler implements HttpResponse.BodyHandler<Path> {
|
public class DownloadBodyHandler implements HttpResponse.BodyHandler<Path> {
|
||||||
private final HttpResponse.BodyHandler<Path> handler;
|
private final HttpResponse.BodyHandler<Path> handler;
|
||||||
private BiConsumer<Long, Long> consumer;
|
private BiConsumer<Long, Long> consumer;
|
||||||
|
|
||||||
private BodyHandler(HttpResponse.BodyHandler<Path> handler) {
|
private DownloadBodyHandler(HttpResponse.BodyHandler<Path> handler) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BodyHandler create(Path directory, OpenOption... openOptions) {
|
public static DownloadBodyHandler create(Path directory, OpenOption... openOptions) {
|
||||||
return new BodyHandler(HttpResponse.BodyHandlers.ofFileDownload(directory, openOptions));
|
return new DownloadBodyHandler(HttpResponse.BodyHandlers.ofFileDownload(directory, openOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1,18 +1,20 @@
|
|||||||
package cn.octopusyan.dmt.common.util;
|
package cn.octopusyan.dmt.common.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import tools.jackson.core.JacksonException;
|
||||||
|
import tools.jackson.core.type.TypeReference;
|
||||||
|
import tools.jackson.databind.DeserializationFeature;
|
||||||
|
import tools.jackson.databind.JsonNode;
|
||||||
|
import tools.jackson.databind.ObjectMapper;
|
||||||
|
import tools.jackson.databind.SerializationFeature;
|
||||||
|
import tools.jackson.databind.cfg.DateTimeFeature;
|
||||||
|
import tools.jackson.databind.json.JsonMapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jackson 封装工具类
|
* Jackson 封装工具类
|
||||||
@ -21,25 +23,26 @@ import java.text.SimpleDateFormat;
|
|||||||
*/
|
*/
|
||||||
public class JsonUtil {
|
public class JsonUtil {
|
||||||
private static final Logger log = LoggerFactory.getLogger(JsonUtil.class);
|
private static final Logger log = LoggerFactory.getLogger(JsonUtil.class);
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 时间日期格式
|
* 时间日期格式
|
||||||
*/
|
*/
|
||||||
private static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
private static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
static {
|
private static final ObjectMapper objectMapper = JsonMapper.builder()
|
||||||
//对象的所有字段全部列入序列化
|
//对象的所有字段全部列入序列化
|
||||||
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
|
.changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL))
|
||||||
//取消默认转换timestamps形式
|
.changeDefaultPropertyInclusion(incl -> incl.withContentInclusion(JsonInclude.Include.NON_NULL))
|
||||||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
//取消默认转换timestamps形式
|
||||||
//忽略空Bean转json的错误
|
.disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
//忽略空Bean转json的错误
|
||||||
//所有的日期格式都统一为以下的格式,即yyyy-MM-dd HH:mm:ss
|
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
|
||||||
objectMapper.setDateFormat(new SimpleDateFormat(STANDARD_FORMAT));
|
//忽略 在json字符串中存在,但在java对象中不存在对应属性的情况。防止错误
|
||||||
//忽略 在json字符串中存在,但在java对象中不存在对应属性的情况。防止错误
|
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
//所有的日期格式都统一为以下的格式,即yyyy-MM-dd HH:mm:ss
|
||||||
}
|
.defaultDateFormat(new SimpleDateFormat(STANDARD_FORMAT))
|
||||||
|
.defaultTimeZone(TimeZone.getDefault())
|
||||||
|
.build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Json字符串 转 JavaBean
|
* Json字符串 转 JavaBean
|
||||||
@ -53,7 +56,7 @@ public class JsonUtil {
|
|||||||
T t = null;
|
T t = null;
|
||||||
try {
|
try {
|
||||||
t = objectMapper.readValue(jsonString, clazz);
|
t = objectMapper.readValue(jsonString, clazz);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
@ -71,7 +74,7 @@ public class JsonUtil {
|
|||||||
T t = null;
|
T t = null;
|
||||||
try {
|
try {
|
||||||
t = objectMapper.readValue(file, clazz);
|
t = objectMapper.readValue(file, clazz);
|
||||||
} catch (IOException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
@ -89,7 +92,7 @@ public class JsonUtil {
|
|||||||
T t = null;
|
T t = null;
|
||||||
try {
|
try {
|
||||||
t = objectMapper.readValue(jsonArray, reference);
|
t = objectMapper.readValue(jsonArray, reference);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
@ -106,7 +109,7 @@ public class JsonUtil {
|
|||||||
String jsonString = null;
|
String jsonString = null;
|
||||||
try {
|
try {
|
||||||
jsonString = objectMapper.writeValueAsString(object);
|
jsonString = objectMapper.writeValueAsString(object);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return jsonString;
|
return jsonString;
|
||||||
@ -122,7 +125,7 @@ public class JsonUtil {
|
|||||||
byte[] bytes = null;
|
byte[] bytes = null;
|
||||||
try {
|
try {
|
||||||
bytes = objectMapper.writeValueAsBytes(object);
|
bytes = objectMapper.writeValueAsBytes(object);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
@ -137,7 +140,7 @@ public class JsonUtil {
|
|||||||
public static void objectToFile(File file, Object object) {
|
public static void objectToFile(File file, Object object) {
|
||||||
try {
|
try {
|
||||||
objectMapper.writeValue(file, object);
|
objectMapper.writeValue(file, object);
|
||||||
} catch (Exception e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,7 +156,7 @@ public class JsonUtil {
|
|||||||
JsonNode jsonNode = null;
|
JsonNode jsonNode = null;
|
||||||
try {
|
try {
|
||||||
jsonNode = objectMapper.readTree(jsonString);
|
jsonNode = objectMapper.readTree(jsonString);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return jsonNode;
|
return jsonNode;
|
||||||
@ -179,7 +182,7 @@ public class JsonUtil {
|
|||||||
String jsonString = null;
|
String jsonString = null;
|
||||||
try {
|
try {
|
||||||
jsonString = objectMapper.writeValueAsString(jsonNode);
|
jsonString = objectMapper.writeValueAsString(jsonNode);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JacksonException e) {
|
||||||
log.error("失败:{}", e.getMessage());
|
log.error("失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
return jsonString;
|
return jsonString;
|
||||||
|
|||||||
@ -89,6 +89,9 @@ public class MainController extends BaseController<MainViewModel> {
|
|||||||
fileChooser.getExtensionFilters().add(extFilter);
|
fileChooser.getExtensionFilters().add(extFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File historySelectFolder;
|
||||||
|
private File historySaveFolder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pane getRootPanel() {
|
public Pane getRootPanel() {
|
||||||
return root;
|
return root;
|
||||||
@ -150,6 +153,8 @@ public class MainController extends BaseController<MainViewModel> {
|
|||||||
* 打开文件选择器
|
* 打开文件选择器
|
||||||
*/
|
*/
|
||||||
public void selectFile() {
|
public void selectFile() {
|
||||||
|
if (historySelectFolder != null)
|
||||||
|
fileChooser.setInitialDirectory(historySelectFolder);
|
||||||
selectFile(fileChooser.showOpenDialog(getWindow()));
|
selectFile(fileChooser.showOpenDialog(getWindow()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,11 +252,15 @@ public class MainController extends BaseController<MainViewModel> {
|
|||||||
public void onPackOver(File packFile) {
|
public void onPackOver(File packFile) {
|
||||||
// 选择文件保存地址
|
// 选择文件保存地址
|
||||||
fileChooser.setInitialFileName(packFile.getName());
|
fileChooser.setInitialFileName(packFile.getName());
|
||||||
|
if (historySaveFolder != null)
|
||||||
|
fileChooser.setInitialDirectory(historySaveFolder);
|
||||||
File file = fileChooser.showSaveDialog(getWindow());
|
File file = fileChooser.showSaveDialog(getWindow());
|
||||||
|
|
||||||
if (file == null)
|
if (file == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
historySaveFolder = file.getParentFile();
|
||||||
|
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
//文件已存在,则删除覆盖文件
|
//文件已存在,则删除覆盖文件
|
||||||
FileUtils.deleteQuietly(file);
|
FileUtils.deleteQuietly(file);
|
||||||
@ -322,6 +331,10 @@ public class MainController extends BaseController<MainViewModel> {
|
|||||||
* 打开文件
|
* 打开文件
|
||||||
*/
|
*/
|
||||||
private void selectFile(File file) {
|
private void selectFile(File file) {
|
||||||
|
if (file != null) {
|
||||||
|
// 设置选择文件记录
|
||||||
|
historySelectFolder = file.getParentFile();
|
||||||
|
}
|
||||||
viewModel.selectFile(file);
|
viewModel.selectFile(file);
|
||||||
viewModel.unpack();
|
viewModel.unpack();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import cn.octopusyan.dmt.common.util.JsonUtil;
|
|||||||
import cn.octopusyan.dmt.model.UpgradeConfig;
|
import cn.octopusyan.dmt.model.UpgradeConfig;
|
||||||
import cn.octopusyan.dmt.task.base.BaseTask;
|
import cn.octopusyan.dmt.task.base.BaseTask;
|
||||||
import cn.octopusyan.dmt.task.listener.DefaultTaskListener;
|
import cn.octopusyan.dmt.task.listener.DefaultTaskListener;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import org.apache.commons.lang3.Strings;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import tools.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查更新任务
|
* 检查更新任务
|
||||||
@ -28,10 +28,10 @@ public class UpgradeTask extends BaseTask<UpgradeTask.UpgradeListener> {
|
|||||||
JsonNode response = JsonUtil.parseJsonObject(responseStr);
|
JsonNode response = JsonUtil.parseJsonObject(responseStr);
|
||||||
|
|
||||||
// TODO 校验返回内容
|
// TODO 校验返回内容
|
||||||
String newVersion = response.get("tag_name").asText();
|
String newVersion = response.get("tag_name").asString();
|
||||||
|
|
||||||
if (listener != null)
|
if (listener != null)
|
||||||
listener.onChecked(!StringUtils.equals(upgradeConfig.getVersion(), newVersion), newVersion);
|
listener.onChecked(!Strings.CS.equals(upgradeConfig.getVersion(), newVersion), newVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import lombok.Getter;
|
|||||||
public enum TranslateApi {
|
public enum TranslateApi {
|
||||||
FREE_BAIDU("free_baidu", "百度", false),
|
FREE_BAIDU("free_baidu", "百度", false),
|
||||||
FREE_GOOGLE("free_google", "谷歌", false),
|
FREE_GOOGLE("free_google", "谷歌", false),
|
||||||
|
FREE_MICROSOFT("free_microsoft", "微软", false),
|
||||||
BAIDU("baidu", "百度(需认证)", true),
|
BAIDU("baidu", "百度(需认证)", true),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,7 @@ package cn.octopusyan.dmt.translate.factory;
|
|||||||
import cn.octopusyan.dmt.model.WordItem;
|
import cn.octopusyan.dmt.model.WordItem;
|
||||||
import cn.octopusyan.dmt.translate.DelayWord;
|
import cn.octopusyan.dmt.translate.DelayWord;
|
||||||
import cn.octopusyan.dmt.translate.TranslateApi;
|
import cn.octopusyan.dmt.translate.TranslateApi;
|
||||||
import cn.octopusyan.dmt.translate.processor.AbstractTranslateProcessor;
|
import cn.octopusyan.dmt.translate.processor.*;
|
||||||
import cn.octopusyan.dmt.translate.processor.BaiduTranslateProcessor;
|
|
||||||
import cn.octopusyan.dmt.translate.processor.FreeBaiduTranslateProcessor;
|
|
||||||
import cn.octopusyan.dmt.translate.processor.FreeGoogleTranslateProcessor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -38,6 +35,7 @@ public class TranslateFactoryImpl implements TranslateFactory {
|
|||||||
private void initProcessor() {
|
private void initProcessor() {
|
||||||
processorList.addAll(Arrays.asList(
|
processorList.addAll(Arrays.asList(
|
||||||
new FreeGoogleTranslateProcessor(),
|
new FreeGoogleTranslateProcessor(),
|
||||||
|
new FreeMicrosoftTranslateProcessor(),
|
||||||
new FreeBaiduTranslateProcessor(),
|
new FreeBaiduTranslateProcessor(),
|
||||||
new BaiduTranslateProcessor()
|
new BaiduTranslateProcessor()
|
||||||
));
|
));
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package cn.octopusyan.dmt.translate.processor;
|
|||||||
import cn.octopusyan.dmt.common.util.JsonUtil;
|
import cn.octopusyan.dmt.common.util.JsonUtil;
|
||||||
import cn.octopusyan.dmt.translate.ApiKey;
|
import cn.octopusyan.dmt.translate.ApiKey;
|
||||||
import cn.octopusyan.dmt.translate.TranslateApi;
|
import cn.octopusyan.dmt.translate.TranslateApi;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import tools.jackson.databind.JsonNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -59,7 +59,7 @@ public class BaiduTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
throw new RuntimeException(String.valueOf(errorMsg));
|
throw new RuntimeException(String.valueOf(errorMsg));
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.get("trans_result").get(0).get("dst").asText();
|
return json.get("trans_result").get(0).get("dst").asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSign(String appid, String q, String salt) {
|
private String getSign(String appid, String q, String salt) {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package cn.octopusyan.dmt.translate.processor;
|
|||||||
import cn.octopusyan.dmt.common.manager.http.CookieManager;
|
import cn.octopusyan.dmt.common.manager.http.CookieManager;
|
||||||
import cn.octopusyan.dmt.common.util.JsonUtil;
|
import cn.octopusyan.dmt.common.util.JsonUtil;
|
||||||
import cn.octopusyan.dmt.translate.TranslateApi;
|
import cn.octopusyan.dmt.translate.TranslateApi;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import tools.jackson.databind.JsonNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.HttpCookie;
|
import java.net.HttpCookie;
|
||||||
@ -23,6 +23,7 @@ public class FreeBaiduTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
header.put("Origin", "https://fanyi.baidu.com");
|
header.put("Origin", "https://fanyi.baidu.com");
|
||||||
|
header.put("acs-token", "");
|
||||||
header.put("Referer", "https://fanyi.baidu.com");
|
header.put("Referer", "https://fanyi.baidu.com");
|
||||||
header.put("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
|
header.put("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
|
||||||
header.put("Accept","*/*");
|
header.put("Accept","*/*");
|
||||||
@ -37,11 +38,6 @@ public class FreeBaiduTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
return "https://fanyi.baidu.com/transapi";
|
return "https://fanyi.baidu.com/transapi";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int qps() {
|
|
||||||
return source().getDefaultQps();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 翻译处理
|
* 翻译处理
|
||||||
*
|
*
|
||||||
@ -61,7 +57,7 @@ public class FreeBaiduTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
JsonNode json = JsonUtil.parseJsonObject(resp);
|
JsonNode json = JsonUtil.parseJsonObject(resp);
|
||||||
|
|
||||||
if (!json.has("data")) {
|
if (!json.has("data")) {
|
||||||
String errorMsg = json.get("errmsg").asText();
|
String errorMsg = json.get("errmsg").asString();
|
||||||
if("访问出现异常,请刷新后重试!".equals(errorMsg) && !header.containsKey("Cookie")) {
|
if("访问出现异常,请刷新后重试!".equals(errorMsg) && !header.containsKey("Cookie")) {
|
||||||
checkCookie();
|
checkCookie();
|
||||||
return customTranslate(source);
|
return customTranslate(source);
|
||||||
@ -69,14 +65,12 @@ public class FreeBaiduTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
throw new RuntimeException(errorMsg);
|
throw new RuntimeException(errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.get("data").get(0).get("dst").asText();
|
return json.get("data").get(0).get("dst").asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCookie() throws IOException, InterruptedException {
|
private void checkCookie() throws IOException, InterruptedException {
|
||||||
// 短时大量请求会被ban,需要添加验证cookie
|
// 短时大量请求会被ban,需要添加验证cookie
|
||||||
|
|
||||||
if (header.containsKey("Cookie")) return;
|
|
||||||
|
|
||||||
List<HttpCookie> cookieList = CookieManager.getStore().get(URI.create("https://baidu.com"));
|
List<HttpCookie> cookieList = CookieManager.getStore().get(URI.create("https://baidu.com"));
|
||||||
boolean noneMatch = cookieList.stream()
|
boolean noneMatch = cookieList.stream()
|
||||||
.filter(cookie -> "ab_sr".equals(cookie.getName()) || "BAIDUID".equals(cookie.getName()))
|
.filter(cookie -> "ab_sr".equals(cookie.getName()) || "BAIDUID".equals(cookie.getName()))
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package cn.octopusyan.dmt.translate.processor;
|
|||||||
|
|
||||||
import cn.octopusyan.dmt.common.util.JsonUtil;
|
import cn.octopusyan.dmt.common.util.JsonUtil;
|
||||||
import cn.octopusyan.dmt.translate.TranslateApi;
|
import cn.octopusyan.dmt.translate.TranslateApi;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import tools.jackson.databind.JsonNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -25,11 +25,6 @@ public class FreeGoogleTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
return "https://translate.googleapis.com/translate_a/single";
|
return "https://translate.googleapis.com/translate_a/single";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int qps() {
|
|
||||||
return source().getDefaultQps();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 翻译处理
|
* 翻译处理
|
||||||
*
|
*
|
||||||
@ -39,22 +34,28 @@ public class FreeGoogleTranslateProcessor extends AbstractTranslateProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public String customTranslate(String source) throws IOException, InterruptedException {
|
public String customTranslate(String source) throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
Map<String, Object> header = new HashMap<>();
|
||||||
|
header.put("User-Agent", "Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0");
|
||||||
|
header.put("Referer", "https://translate.google.com/");
|
||||||
|
header.put("Host", "translate.googleapis.com");
|
||||||
|
|
||||||
Map<String, Object> form = new HashMap<>();
|
Map<String, Object> form = new HashMap<>();
|
||||||
form.put("client", "gtx");
|
form.put("client", "gtx");
|
||||||
form.put("dt", "t");
|
form.put("dt", "t");
|
||||||
form.put("sl", "auto");
|
form.put("sl", "auto");
|
||||||
form.put("tl", "zh-CN");
|
form.put("tl", "zh-CN");
|
||||||
form.put("q", source);
|
|
||||||
|
|
||||||
Map<String, Object> header = new HashMap<>();
|
Map<String, Object> body = new HashMap<>();
|
||||||
|
body.put("q", source);
|
||||||
|
|
||||||
StringBuilder retStr = new StringBuilder();
|
StringBuilder retStr = new StringBuilder();
|
||||||
// TODO 短时大量请求会被ban,需要浏览器验证添加cookie
|
// TODO 短时大量请求会被ban,需要浏览器验证添加cookie
|
||||||
|
|
||||||
String resp = httpUtil.get(url(), JsonUtil.parseJsonObject(header), JsonUtil.parseJsonObject(form));
|
String resp = httpUtil.postForm(url(), JsonUtil.parseJsonObject(header), JsonUtil.parseJsonObject(form), JsonUtil.parseJsonObject(body));
|
||||||
JsonNode json = JsonUtil.parseJsonObject(resp);
|
JsonNode json = JsonUtil.parseJsonObject(resp);
|
||||||
|
|
||||||
for (JsonNode o : json.get(0)) {
|
for (JsonNode o : json.get(0)) {
|
||||||
retStr.append(o.get(0).asText());
|
retStr.append(o.get(0).asString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return retStr.toString();
|
return retStr.toString();
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
package cn.octopusyan.dmt.translate.processor;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.octopusyan.dmt.common.util.JsonUtil;
|
||||||
|
import cn.octopusyan.dmt.translate.TranslateApi;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import tools.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微软 免费翻译接口
|
||||||
|
*
|
||||||
|
* @author octopus_yan@foxmail.com
|
||||||
|
*/
|
||||||
|
public class FreeMicrosoftTranslateProcessor extends AbstractTranslateProcessor {
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
public FreeMicrosoftTranslateProcessor() {
|
||||||
|
super(TranslateApi.FREE_MICROSOFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String url() {
|
||||||
|
return "https://api.cognitive.microsofttranslator.com/translate";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译处理
|
||||||
|
*
|
||||||
|
* @param source 待翻译单词
|
||||||
|
* @return 翻译结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String customTranslate(String source) throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
Map<String, Object> form = new HashMap<>();
|
||||||
|
form.put("api-version", "3.0");
|
||||||
|
form.put("to", "zh-Hans");
|
||||||
|
form.put("textType", "plain");
|
||||||
|
|
||||||
|
Map<String, Object> header = new HashMap<>();
|
||||||
|
header.put("Authorization", STR."Bearer \{oauth()}");
|
||||||
|
|
||||||
|
String body = STR."""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Text": "\{source}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
""";
|
||||||
|
|
||||||
|
StringBuilder retStr = new StringBuilder();
|
||||||
|
|
||||||
|
String resp = httpUtil.postForm(url(), JsonUtil.parseJsonObject(header), JsonUtil.parseJsonObject(form), JsonUtil.parseJsonObject(body));
|
||||||
|
JsonNode json = JsonUtil.parseJsonObject(resp);
|
||||||
|
|
||||||
|
for (JsonNode o : json.get(0)) {
|
||||||
|
retStr.append(o.get(0).asString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retStr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized String oauth() throws IOException, InterruptedException {
|
||||||
|
if (token == null) {
|
||||||
|
token = httpUtil.get("https://edge.microsoft.com/translate/auth", null, null);
|
||||||
|
} else {
|
||||||
|
// 解析,对应的签名或者加密方式
|
||||||
|
Claims claims = Jwts.parser()
|
||||||
|
// .verifyWith(key) // 指定用于验证的密钥
|
||||||
|
// .decryptWith(key) // 加密方式,指定用于解密的密钥。
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token) // 签名方式
|
||||||
|
// .parseEncryptedClaims(token) // 加密方式,对 JWT 进行解密,并获取其声明部分。
|
||||||
|
.getPayload(); // 获取解密后的声明(claims)内容。
|
||||||
|
if(DateUtil.compare(DateUtil.date(), claims.getExpiration()) > 0) {
|
||||||
|
token = null;
|
||||||
|
return oauth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
package cn.octopusyan.dmt.utils;
|
package cn.octopusyan.dmt.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.csv.*;
|
||||||
import cn.octopusyan.dmt.common.config.Constants;
|
import cn.octopusyan.dmt.common.config.Constants;
|
||||||
import cn.octopusyan.dmt.common.config.Context;
|
import cn.octopusyan.dmt.common.config.Context;
|
||||||
import cn.octopusyan.dmt.common.util.ProcessesUtil;
|
import cn.octopusyan.dmt.common.util.ProcessesUtil;
|
||||||
import cn.octopusyan.dmt.model.WordCsvItem;
|
import cn.octopusyan.dmt.model.WordCsvItem;
|
||||||
import cn.octopusyan.dmt.model.WordItem;
|
import cn.octopusyan.dmt.model.WordItem;
|
||||||
import cn.octopusyan.dmt.utils.csv.*;
|
|
||||||
import cn.octopusyan.dmt.view.ConsoleLog;
|
import cn.octopusyan.dmt.view.ConsoleLog;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.LineIterator;
|
import org.apache.commons.io.LineIterator;
|
||||||
@ -37,8 +37,8 @@ public class PBOUtil {
|
|||||||
|
|
||||||
private static final ProcessesUtil processesUtil = ProcessesUtil.init(Constants.BIN_DIR_PATH);
|
private static final ProcessesUtil processesUtil = ProcessesUtil.init(Constants.BIN_DIR_PATH);
|
||||||
|
|
||||||
private static final String UNPACK_COMMAND = STR."\"\{Constants.PBOC_FILE}\" unpack -o \"\{Constants.TMP_DIR_PATH}\" {}";
|
private static final String UNPACK_COMMAND = STR."\"\{Constants.PBOC_FILE}\" unpack {} -o \"\{Constants.TMP_DIR_PATH}\"";
|
||||||
private static final String PACK_COMMAND = STR."\"\{Constants.PBOC_FILE}\" pack -o {} {}";
|
private static final String PACK_COMMAND = STR."\"\{Constants.PBOC_FILE}\" pack {} -o {}";
|
||||||
private static final String CFG_COMMAND = STR."\"\{Constants.CFG_CONVERT_FILE}\" {} -dst {} {}";
|
private static final String CFG_COMMAND = STR."\"\{Constants.CFG_CONVERT_FILE}\" {} -dst {} {}";
|
||||||
|
|
||||||
private static final String FILE_NAME_STRING_TABLE = "stringtable.csv";
|
private static final String FILE_NAME_STRING_TABLE = "stringtable.csv";
|
||||||
@ -122,7 +122,7 @@ public class PBOUtil {
|
|||||||
FileUtils.deleteQuietly(packFile);
|
FileUtils.deleteQuietly(packFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
String command = ProcessesUtil.format(PACK_COMMAND, Constants.TMP_DIR_PATH, unpackPath);
|
String command = ProcessesUtil.format(PACK_COMMAND, unpackPath, Constants.TMP_DIR_PATH);
|
||||||
consoleLog.debug(STR."pack command ==> [\{command}]");
|
consoleLog.debug(STR."pack command ==> [\{command}]");
|
||||||
|
|
||||||
boolean exec = processesUtil.exec(command);
|
boolean exec = processesUtil.exec(command);
|
||||||
@ -146,6 +146,10 @@ public class PBOUtil {
|
|||||||
return wordItems;
|
return wordItems;
|
||||||
|
|
||||||
List<File> files = new ArrayList<>(FileUtils.listFiles(file, FILE_NAME_LIST, true));
|
List<File> files = new ArrayList<>(FileUtils.listFiles(file, FILE_NAME_LIST, true));
|
||||||
|
List<String> names = files.stream().map(File::getName).toList();
|
||||||
|
if(names.contains("config.bin") && names.contains("config.cpp")) {
|
||||||
|
files = files.stream().filter(item -> "config.cpp".equals(item.getName())).toList();
|
||||||
|
}
|
||||||
for (File item : files) {
|
for (File item : files) {
|
||||||
wordItems.addAll(findWordByFile(item));
|
wordItems.addAll(findWordByFile(item));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,307 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.core.lang.Assert;
|
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV文件读取器基础类,提供灵活的文件、路径中的CSV读取,一次构造可多次调用读取不同数据,参考:FastCSV
|
|
||||||
*
|
|
||||||
* @author Looly
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public class CsvBaseReader implements Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认编码
|
|
||||||
*/
|
|
||||||
protected static final Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
|
||||||
|
|
||||||
private final CsvReadConfig config;
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------- Constructor start
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造,使用默认配置项
|
|
||||||
*/
|
|
||||||
public CsvBaseReader() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param config 配置项
|
|
||||||
*/
|
|
||||||
public CsvBaseReader(CsvReadConfig config) {
|
|
||||||
this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig::defaultConfig);
|
|
||||||
}
|
|
||||||
//--------------------------------------------------------------------------------------------- Constructor end
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置字段分隔符,默认逗号','
|
|
||||||
*
|
|
||||||
* @param fieldSeparator 字段分隔符,默认逗号','
|
|
||||||
*/
|
|
||||||
public void setFieldSeparator(char fieldSeparator) {
|
|
||||||
this.config.setFieldSeparator(fieldSeparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置 文本分隔符,文本包装符,默认双引号'"'
|
|
||||||
*
|
|
||||||
* @param textDelimiter 文本分隔符,文本包装符,默认双引号'"'
|
|
||||||
*/
|
|
||||||
public void setTextDelimiter(char textDelimiter) {
|
|
||||||
this.config.setTextDelimiter(textDelimiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置是否首行做为标题行,默认false
|
|
||||||
*
|
|
||||||
* @param containsHeader 是否首行做为标题行,默认false
|
|
||||||
*/
|
|
||||||
public void setContainsHeader(boolean containsHeader) {
|
|
||||||
this.config.setContainsHeader(containsHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置是否跳过空白行,默认true
|
|
||||||
*
|
|
||||||
* @param skipEmptyRows 是否跳过空白行,默认true
|
|
||||||
*/
|
|
||||||
public void setSkipEmptyRows(boolean skipEmptyRows) {
|
|
||||||
this.config.setSkipEmptyRows(skipEmptyRows);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置每行字段个数不同时是否抛出异常,默认false
|
|
||||||
*
|
|
||||||
* @param errorOnDifferentFieldCount 每行字段个数不同时是否抛出异常,默认false
|
|
||||||
*/
|
|
||||||
public void setErrorOnDifferentFieldCount(boolean errorOnDifferentFieldCount) {
|
|
||||||
this.config.setErrorOnDifferentFieldCount(errorOnDifferentFieldCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取CSV文件,默认UTF-8编码
|
|
||||||
*
|
|
||||||
* @param file CSV文件
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read(File file) throws IORuntimeException {
|
|
||||||
return read(file, DEFAULT_CHARSET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从字符串中读取CSV数据
|
|
||||||
*
|
|
||||||
* @param csvStr CSV字符串
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
*/
|
|
||||||
public CsvData readFromStr(String csvStr) {
|
|
||||||
return read(new StringReader(csvStr));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从字符串中读取CSV数据
|
|
||||||
*
|
|
||||||
* @param csvStr CSV字符串
|
|
||||||
* @param rowHandler 行处理器,用于一行一行的处理数据
|
|
||||||
*/
|
|
||||||
public void readFromStr(String csvStr, CsvRowHandler rowHandler) {
|
|
||||||
read(parse(new StringReader(csvStr)), true, rowHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取CSV文件
|
|
||||||
*
|
|
||||||
* @param file CSV文件
|
|
||||||
* @param charset 文件编码,默认系统编码
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read(File file, Charset charset) throws IORuntimeException {
|
|
||||||
return read(Objects.requireNonNull(file.toPath(), "file must not be null"), charset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取CSV文件,默认UTF-8编码
|
|
||||||
*
|
|
||||||
* @param path CSV文件
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read(Path path) throws IORuntimeException {
|
|
||||||
return read(path, DEFAULT_CHARSET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取CSV文件
|
|
||||||
*
|
|
||||||
* @param path CSV文件
|
|
||||||
* @param charset 文件编码,默认系统编码
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read(Path path, Charset charset) throws IORuntimeException {
|
|
||||||
Assert.notNull(path, "path must not be null");
|
|
||||||
return read(FileUtil.getReader(path, charset));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Reader中读取CSV数据,读取后关闭Reader
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read(Reader reader) throws IORuntimeException {
|
|
||||||
return read(reader, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Reader中读取CSV数据
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @param close 读取结束是否关闭Reader
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read(Reader reader, boolean close) throws IORuntimeException {
|
|
||||||
final CsvParser csvParser = parse(reader);
|
|
||||||
final List<CsvRow> rows = new ArrayList<>();
|
|
||||||
read(csvParser, close, rows::add);
|
|
||||||
final List<String> header = config.headerLineNo > -1 ? csvParser.getHeader() : null;
|
|
||||||
|
|
||||||
return new CsvData(header, rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Reader中读取CSV数据,结果为Map,读取后关闭Reader。<br>
|
|
||||||
* 此方法默认识别首行为标题行。
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public List<Map<String, String>> readMapList(Reader reader) throws IORuntimeException {
|
|
||||||
// 此方法必须包含标题
|
|
||||||
this.config.setContainsHeader(true);
|
|
||||||
|
|
||||||
final List<Map<String, String>> result = new ArrayList<>();
|
|
||||||
read(reader, (row) -> result.add(row.getFieldMap()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Reader中读取CSV数据并转换为Bean列表,读取后关闭Reader。<br>
|
|
||||||
* 此方法默认识别首行为标题行。
|
|
||||||
*
|
|
||||||
* @param <T> Bean类型
|
|
||||||
* @param reader Reader
|
|
||||||
* @param clazz Bean类型
|
|
||||||
* @return Bean列表
|
|
||||||
*/
|
|
||||||
public <T> List<T> read(Reader reader, Class<T> clazz) {
|
|
||||||
// 此方法必须包含标题
|
|
||||||
this.config.setContainsHeader(true);
|
|
||||||
|
|
||||||
final List<T> result = new ArrayList<>();
|
|
||||||
read(reader, (row) -> result.add(row.toBean(clazz)));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从字符串中读取CSV数据并转换为Bean列表,读取后关闭Reader。<br>
|
|
||||||
* 此方法默认识别首行为标题行。
|
|
||||||
*
|
|
||||||
* @param <T> Bean类型
|
|
||||||
* @param csvStr csv字符串
|
|
||||||
* @param clazz Bean类型
|
|
||||||
* @return Bean列表
|
|
||||||
*/
|
|
||||||
public <T> List<T> read(String csvStr, Class<T> clazz) {
|
|
||||||
// 此方法必须包含标题
|
|
||||||
this.config.setContainsHeader(true);
|
|
||||||
|
|
||||||
final List<T> result = new ArrayList<>();
|
|
||||||
read(new StringReader(csvStr), (row) -> result.add(row.toBean(clazz)));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Reader中读取CSV数据,读取后关闭Reader
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @param rowHandler 行处理器,用于一行一行的处理数据
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public void read(Reader reader, CsvRowHandler rowHandler) throws IORuntimeException {
|
|
||||||
read(reader, true, rowHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Reader中读取CSV数据,读取后关闭Reader
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @param close 读取结束是否关闭Reader
|
|
||||||
* @param rowHandler 行处理器,用于一行一行的处理数据
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public void read(Reader reader, boolean close, CsvRowHandler rowHandler) throws IORuntimeException {
|
|
||||||
read(parse(reader), close, rowHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------- Private method start
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取CSV数据,读取后关闭Parser
|
|
||||||
*
|
|
||||||
* @param csvParser CSV解析器
|
|
||||||
* @param close 读取结束是否关闭{@link CsvParser}
|
|
||||||
* @param rowHandler 行处理器,用于一行一行的处理数据
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
private void read(CsvParser csvParser, boolean close, CsvRowHandler rowHandler) throws IORuntimeException {
|
|
||||||
try {
|
|
||||||
while (csvParser.hasNext()) {
|
|
||||||
rowHandler.handle(csvParser.next());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if(close){
|
|
||||||
IoUtil.close(csvParser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建 {@link CsvParser}
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @return CsvParser
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
protected CsvParser parse(Reader reader) throws IORuntimeException {
|
|
||||||
return new CsvParser(reader, this.config);
|
|
||||||
}
|
|
||||||
//--------------------------------------------------------------------------------------------- Private method start
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import cn.hutool.core.text.csv.CsvWriter;
|
|
||||||
import cn.hutool.core.util.CharUtil;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV基础配置项,此配置项可用于读取和写出CSV,定义了包括字段分隔符、文本包装符等符号
|
|
||||||
*
|
|
||||||
* @param <T> 继承子类类型,用于this返回
|
|
||||||
* @author looly
|
|
||||||
* @since 4.0.5
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class CsvConfig<T extends CsvConfig<T>> implements Serializable {
|
|
||||||
private static final long serialVersionUID = -8069578249066158459L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字段分隔符,默认逗号','
|
|
||||||
*/
|
|
||||||
protected char fieldSeparator = CharUtil.COMMA;
|
|
||||||
/**
|
|
||||||
* 文本包装符,默认双引号'"'
|
|
||||||
*/
|
|
||||||
protected char textDelimiter = CharUtil.DOUBLE_QUOTES;
|
|
||||||
/**
|
|
||||||
* 注释符号,用于区分注释行,默认'#'
|
|
||||||
*/
|
|
||||||
protected Character commentCharacter = '#';
|
|
||||||
/**
|
|
||||||
* 标题别名
|
|
||||||
*/
|
|
||||||
protected Map<String, String> headerAlias = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置字段分隔符,默认逗号','
|
|
||||||
*
|
|
||||||
* @param fieldSeparator 字段分隔符,默认逗号','
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public T setFieldSeparator(final char fieldSeparator) {
|
|
||||||
this.fieldSeparator = fieldSeparator;
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置 文本分隔符,文本包装符,默认双引号'"'
|
|
||||||
*
|
|
||||||
* @param textDelimiter 文本分隔符,文本包装符,默认双引号'"'
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public T setTextDelimiter(char textDelimiter) {
|
|
||||||
this.textDelimiter = textDelimiter;
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置注释无效<br>
|
|
||||||
* 当写出CSV时,{@link CsvWriter#writeComment(String)}将抛出异常<br>
|
|
||||||
* 当读取CSV时,注释行按照正常行读取
|
|
||||||
*
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.14
|
|
||||||
*/
|
|
||||||
public T disableComment() {
|
|
||||||
return setCommentCharacter(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置 注释符号,用于区分注释行,{@code null}表示忽略注释
|
|
||||||
*
|
|
||||||
* @param commentCharacter 注释符号,用于区分注释行
|
|
||||||
* @return this
|
|
||||||
* @since 5.5.7
|
|
||||||
*/
|
|
||||||
public T setCommentCharacter(Character commentCharacter) {
|
|
||||||
this.commentCharacter = commentCharacter;
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置标题行的别名Map
|
|
||||||
*
|
|
||||||
* @param headerAlias 别名Map
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.10
|
|
||||||
*/
|
|
||||||
public T setHeaderAlias(Map<String, String> headerAlias) {
|
|
||||||
this.headerAlias = headerAlias;
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 增加标题别名
|
|
||||||
*
|
|
||||||
* @param header 标题
|
|
||||||
* @param alias 别名
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.10
|
|
||||||
*/
|
|
||||||
public T addHeaderAlias(String header, String alias) {
|
|
||||||
this.headerAlias.put(header, alias);
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 去除标题别名
|
|
||||||
*
|
|
||||||
* @param header 标题
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.10
|
|
||||||
*/
|
|
||||||
public T removeHeaderAlias(String header) {
|
|
||||||
this.headerAlias.remove(header);
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV数据,包括头部信息和行数据,参考:FastCSV
|
|
||||||
*
|
|
||||||
* @author Looly
|
|
||||||
*/
|
|
||||||
public class CsvData implements Iterable<CsvRow>, Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final List<String> header;
|
|
||||||
private final List<CsvRow> rows;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param header 头信息, 可以为null
|
|
||||||
* @param rows 行
|
|
||||||
*/
|
|
||||||
public CsvData(final List<String> header, final List<CsvRow> rows) {
|
|
||||||
this.header = header;
|
|
||||||
this.rows = rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 总行数
|
|
||||||
*
|
|
||||||
* @return 总行数
|
|
||||||
*/
|
|
||||||
public int getRowCount() {
|
|
||||||
return this.rows.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取头信息列表,如果无头信息为{@code Null},返回列表为只读列表
|
|
||||||
*
|
|
||||||
* @return the header row - might be {@code null} if no header exists
|
|
||||||
*/
|
|
||||||
public List<String> getHeader() {
|
|
||||||
if(null == this.header){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(this.header);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定行,从0开始
|
|
||||||
*
|
|
||||||
* @param index 行号
|
|
||||||
* @return 行数据
|
|
||||||
* @throws IndexOutOfBoundsException if index is out of range
|
|
||||||
*/
|
|
||||||
public CsvRow getRow(final int index) {
|
|
||||||
return this.rows.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有行
|
|
||||||
*
|
|
||||||
* @return 所有行
|
|
||||||
*/
|
|
||||||
public List<CsvRow> getRows() {
|
|
||||||
return this.rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<CsvRow> iterator() {
|
|
||||||
return this.rows.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CsvData{" +
|
|
||||||
"header=" + header +
|
|
||||||
", rows=" + rows +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,489 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import ch.qos.logback.core.CoreConstants;
|
|
||||||
import cn.hutool.core.collection.ComputeIter;
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
|
||||||
import cn.hutool.core.text.StrBuilder;
|
|
||||||
import cn.hutool.core.util.CharUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV行解析器,参考:FastCSV
|
|
||||||
*
|
|
||||||
* @author Looly
|
|
||||||
*/
|
|
||||||
public final class CsvParser extends ComputeIter<CsvRow> implements Closeable, Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final int DEFAULT_ROW_CAPACITY = 10;
|
|
||||||
|
|
||||||
private final Reader reader;
|
|
||||||
private final CsvReadConfig config;
|
|
||||||
|
|
||||||
private final Buffer buf = new Buffer(IoUtil.DEFAULT_LARGE_BUFFER_SIZE);
|
|
||||||
/**
|
|
||||||
* 前一个特殊分界字符
|
|
||||||
*/
|
|
||||||
private int preChar = -1;
|
|
||||||
/**
|
|
||||||
* 是否在引号包装内
|
|
||||||
*/
|
|
||||||
private boolean inQuotes;
|
|
||||||
/**
|
|
||||||
* 连续双引号计数
|
|
||||||
*/
|
|
||||||
private int continuousCount = 0;
|
|
||||||
/**
|
|
||||||
* 当前读取字段
|
|
||||||
*/
|
|
||||||
private final StrBuilder currentField = new StrBuilder(512);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标题行
|
|
||||||
*/
|
|
||||||
private CsvRow header;
|
|
||||||
/**
|
|
||||||
* 当前行号
|
|
||||||
*/
|
|
||||||
private long lineNo = -1;
|
|
||||||
/**
|
|
||||||
* 引号内的行数
|
|
||||||
*/
|
|
||||||
private long inQuotesLineCount;
|
|
||||||
/**
|
|
||||||
* 第一行字段数,用于检查每行字段数是否一致
|
|
||||||
*/
|
|
||||||
private int firstLineFieldCount = -1;
|
|
||||||
/**
|
|
||||||
* 最大字段数量,用于初始化行,减少扩容
|
|
||||||
*/
|
|
||||||
private int maxFieldCount;
|
|
||||||
/**
|
|
||||||
* 是否读取结束
|
|
||||||
*/
|
|
||||||
private boolean finished;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV解析器
|
|
||||||
*
|
|
||||||
* @param reader Reader
|
|
||||||
* @param config 配置,null则为默认配置
|
|
||||||
*/
|
|
||||||
public CsvParser(final Reader reader, CsvReadConfig config) {
|
|
||||||
this.reader = Objects.requireNonNull(reader, "reader must not be null");
|
|
||||||
this.config = ObjectUtil.defaultIfNull(config, CsvReadConfig::defaultConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取头部字段列表,如果headerLineNo < 0,抛出异常
|
|
||||||
*
|
|
||||||
* @return 头部列表
|
|
||||||
* @throws IllegalStateException 如果不解析头部或者没有调用nextRow()方法
|
|
||||||
*/
|
|
||||||
public List<String> getHeader() {
|
|
||||||
if (config.headerLineNo < 0) {
|
|
||||||
throw new IllegalStateException("No header available - header parsing is disabled");
|
|
||||||
}
|
|
||||||
if (lineNo < config.beginLineNo) {
|
|
||||||
throw new IllegalStateException("No header available - call nextRow() first");
|
|
||||||
}
|
|
||||||
return header.getRawList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CsvRow computeNext() {
|
|
||||||
return nextRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取下一行数据
|
|
||||||
*
|
|
||||||
* @return CsvRow
|
|
||||||
* @throws IORuntimeException IO读取异常
|
|
||||||
*/
|
|
||||||
public CsvRow nextRow() throws IORuntimeException {
|
|
||||||
List<String> currentFields;
|
|
||||||
int fieldCount;
|
|
||||||
while (false == finished) {
|
|
||||||
currentFields = readLine();
|
|
||||||
fieldCount = currentFields.size();
|
|
||||||
if (fieldCount < 1) {
|
|
||||||
// 空List表示读取结束
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取范围校验
|
|
||||||
if(lineNo < config.beginLineNo){
|
|
||||||
// 未达到读取起始行,继续
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(lineNo > config.endLineNo){
|
|
||||||
// 超出结束行,读取结束
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 跳过空行
|
|
||||||
if (config.skipEmptyRows && fieldCount == 1 && currentFields.get(0).isEmpty()) {
|
|
||||||
// [""]表示空行
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查每行的字段数是否一致
|
|
||||||
if (config.errorOnDifferentFieldCount) {
|
|
||||||
if (firstLineFieldCount < 0) {
|
|
||||||
firstLineFieldCount = fieldCount;
|
|
||||||
} else if (fieldCount != firstLineFieldCount) {
|
|
||||||
throw new IORuntimeException(String.format("Line %d has %d fields, but first line has %d fields", lineNo, fieldCount, firstLineFieldCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录最大字段数
|
|
||||||
if (fieldCount > maxFieldCount) {
|
|
||||||
maxFieldCount = fieldCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
//初始化标题
|
|
||||||
if (lineNo == config.headerLineNo && null == header) {
|
|
||||||
initHeader(currentFields);
|
|
||||||
// 作为标题行后,此行跳过,下一行做为第一行
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CsvRow(lineNo, null == header ? null : header.headerMap, currentFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前行做为标题行
|
|
||||||
*
|
|
||||||
* @param currentFields 当前行字段列表
|
|
||||||
*/
|
|
||||||
private void initHeader(final List<String> currentFields) {
|
|
||||||
final Map<String, Integer> localHeaderMap = new LinkedHashMap<>(currentFields.size());
|
|
||||||
for (int i = 0; i < currentFields.size(); i++) {
|
|
||||||
String field = currentFields.get(i);
|
|
||||||
if (MapUtil.isNotEmpty(this.config.headerAlias)) {
|
|
||||||
// 自定义别名
|
|
||||||
field = ObjectUtil.defaultIfNull(this.config.headerAlias.get(field), field);
|
|
||||||
}
|
|
||||||
if (StrUtil.isNotEmpty(field) && false == localHeaderMap.containsKey(field)) {
|
|
||||||
localHeaderMap.put(field, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header = new CsvRow(this.lineNo, Collections.unmodifiableMap(localHeaderMap), Collections.unmodifiableList(currentFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取一行数据,如果读取结束,返回size为0的List<br>
|
|
||||||
* 空行是size为1的List,唯一元素是""
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 行号要考虑注释行和引号包装的内容中的换行
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return 一行数据
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
private List<String> readLine() throws IORuntimeException {
|
|
||||||
// 矫正行号
|
|
||||||
// 当一行内容包含多行数据时,记录首行行号,但是读取下一行时,需要把多行内容的行数加上
|
|
||||||
if(inQuotesLineCount > 0){
|
|
||||||
this.lineNo += this.inQuotesLineCount;
|
|
||||||
this.inQuotesLineCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<String> currentFields = new ArrayList<>(maxFieldCount > 0 ? maxFieldCount : DEFAULT_ROW_CAPACITY);
|
|
||||||
|
|
||||||
final StrBuilder currentField = this.currentField;
|
|
||||||
final Buffer buf = this.buf;
|
|
||||||
int preChar = this.preChar;//前一个特殊分界字符
|
|
||||||
int copyLen = 0; //拷贝长度
|
|
||||||
boolean inComment = false;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (false == buf.hasRemaining()) {
|
|
||||||
// 此Buffer读取结束,开始读取下一段
|
|
||||||
if (copyLen > 0) {
|
|
||||||
buf.appendTo(currentField, copyLen);
|
|
||||||
// 此处无需mark,read方法会重置mark
|
|
||||||
}
|
|
||||||
if (buf.read(this.reader) < 0) {
|
|
||||||
// CSV读取结束
|
|
||||||
finished = true;
|
|
||||||
|
|
||||||
if (currentField.hasContent() || preChar == config.fieldSeparator) {
|
|
||||||
//剩余部分作为一个字段
|
|
||||||
addField(currentFields, currentField.toStringAndReset());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//重置
|
|
||||||
copyLen = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
final char c = buf.get();
|
|
||||||
|
|
||||||
// 注释行标记
|
|
||||||
if(preChar < 0 || preChar == CharUtil.CR || preChar == CharUtil.LF){
|
|
||||||
// 判断行首字符为指定注释字符的注释开始,直到遇到换行符
|
|
||||||
// 行首分两种,1是preChar < 0表示文本开始,2是换行符后紧跟就是下一行的开始
|
|
||||||
// issue#IA8WE0 如果注释符出现在包装符内,被认为是普通字符
|
|
||||||
if((false == inQuotes) && null != this.config.commentCharacter && c == this.config.commentCharacter){
|
|
||||||
inComment = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 注释行处理
|
|
||||||
if(inComment){
|
|
||||||
if (c == CharUtil.CR || c == CharUtil.LF) {
|
|
||||||
// 注释行以换行符为结尾
|
|
||||||
lineNo++;
|
|
||||||
inComment = false;
|
|
||||||
}
|
|
||||||
// 跳过注释行中的任何字符
|
|
||||||
buf.mark();
|
|
||||||
preChar = c;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inQuotes) {
|
|
||||||
//引号内,作为内容,直到引号结束
|
|
||||||
if (c == config.textDelimiter) {
|
|
||||||
if(buf.canRead(1) && buf.read(1) == CharUtil.DOUBLE_QUOTES) {
|
|
||||||
continuousCount++;
|
|
||||||
} else if(continuousCount != 0 && (continuousCount + 1) % 2 == 0) {
|
|
||||||
continuousCount = 0;
|
|
||||||
} else {
|
|
||||||
inQuotes = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(continuousCount != 0) continuousCount = 0;
|
|
||||||
// 字段内容中新行
|
|
||||||
if (isLineEnd(c, preChar)) {
|
|
||||||
inQuotesLineCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 普通字段字符
|
|
||||||
copyLen++;
|
|
||||||
} else {
|
|
||||||
// 非引号内
|
|
||||||
if (c == config.fieldSeparator) {
|
|
||||||
//一个字段结束
|
|
||||||
if (copyLen > 0) {
|
|
||||||
buf.appendTo(currentField, copyLen);
|
|
||||||
copyLen = 0;
|
|
||||||
}
|
|
||||||
buf.mark();
|
|
||||||
addField(currentFields, currentField.toStringAndReset());
|
|
||||||
} else if (c == config.textDelimiter && isFieldBegin(preChar)) {
|
|
||||||
// 引号开始且出现在字段开头
|
|
||||||
inQuotes = true;
|
|
||||||
copyLen++;
|
|
||||||
} else if (c == CharUtil.CR) {
|
|
||||||
// \r,直接结束
|
|
||||||
if (copyLen > 0) {
|
|
||||||
buf.appendTo(currentField, copyLen);
|
|
||||||
}
|
|
||||||
buf.mark();
|
|
||||||
addField(currentFields, currentField.toStringAndReset());
|
|
||||||
preChar = c;
|
|
||||||
break;
|
|
||||||
} else if (c == CharUtil.LF) {
|
|
||||||
// \n
|
|
||||||
if (preChar != CharUtil.CR) {
|
|
||||||
if (copyLen > 0) {
|
|
||||||
buf.appendTo(currentField, copyLen);
|
|
||||||
}
|
|
||||||
buf.mark();
|
|
||||||
addField(currentFields, currentField.toStringAndReset());
|
|
||||||
preChar = c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// 前一个字符是\r,已经处理过这个字段了,此处直接跳过
|
|
||||||
buf.mark();
|
|
||||||
} else {
|
|
||||||
// 普通字符
|
|
||||||
copyLen++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preChar = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore fields
|
|
||||||
this.preChar = preChar;
|
|
||||||
|
|
||||||
lineNo++;
|
|
||||||
return currentFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将字段加入字段列表并自动去包装和去转义
|
|
||||||
*
|
|
||||||
* @param currentFields 当前的字段列表(即为行)
|
|
||||||
* @param field 字段
|
|
||||||
*/
|
|
||||||
private void addField(List<String> currentFields, String field) {
|
|
||||||
final char textDelimiter = this.config.textDelimiter;
|
|
||||||
|
|
||||||
// 忽略多余引号后的换行符
|
|
||||||
field = StrUtil.trim(field, 1, (c-> c == CharUtil.LF || c == CharUtil.CR));
|
|
||||||
// 去除手写csv列值前后的缩进符
|
|
||||||
field = field.replaceAll("\t+\"|\"\t+", "\"");
|
|
||||||
|
|
||||||
if(StrUtil.isWrap(field, textDelimiter)){
|
|
||||||
field = StrUtil.sub(field, 1, field.length() - 1);
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc4180#section-2
|
|
||||||
// 第七条规则,只有包装内的包装符需要转义
|
|
||||||
field = StrUtil.replace(field, String.valueOf(textDelimiter) + textDelimiter, String.valueOf(textDelimiter));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.config.trimField){
|
|
||||||
// issue#I49M0C@Gitee
|
|
||||||
field = StrUtil.trim(field);
|
|
||||||
}
|
|
||||||
currentFields.add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否行结束符
|
|
||||||
*
|
|
||||||
* @param c 符号
|
|
||||||
* @param preChar 前一个字符
|
|
||||||
* @return 是否结束
|
|
||||||
* @since 5.7.4
|
|
||||||
*/
|
|
||||||
private boolean isLineEnd(char c, int preChar) {
|
|
||||||
return (c == CharUtil.CR || c == CharUtil.LF) && preChar != CharUtil.CR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过前一个字符,判断是否字段开始,几种情况:
|
|
||||||
* <ul>
|
|
||||||
* <li>正文开头,无前字符</li>
|
|
||||||
* <li>缩进</li>
|
|
||||||
* <li>字段分隔符,即上个字段结束</li>
|
|
||||||
* <li>换行符,即新行开始</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param preChar 前字符
|
|
||||||
* @return 是否字段开始
|
|
||||||
*/
|
|
||||||
private boolean isFieldBegin(final int preChar) {
|
|
||||||
return preChar == -1
|
|
||||||
|| preChar == CoreConstants.TAB
|
|
||||||
|| preChar == config.fieldSeparator
|
|
||||||
|| preChar == CharUtil.LF
|
|
||||||
|| preChar == CharUtil.CR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 内部Buffer
|
|
||||||
*
|
|
||||||
* @author looly
|
|
||||||
*/
|
|
||||||
private static class Buffer implements Serializable{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
final char[] buf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标记位置,用于读数据
|
|
||||||
*/
|
|
||||||
private int mark;
|
|
||||||
/**
|
|
||||||
* 当前位置
|
|
||||||
*/
|
|
||||||
private int position;
|
|
||||||
/**
|
|
||||||
* 读取的数据长度,一般小于buf.length,-1表示无数据
|
|
||||||
*/
|
|
||||||
private int limit;
|
|
||||||
|
|
||||||
Buffer(int capacity) {
|
|
||||||
buf = new char[capacity];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否还有未读数据
|
|
||||||
*
|
|
||||||
* @return 是否还有未读数据
|
|
||||||
*/
|
|
||||||
public final boolean hasRemaining() {
|
|
||||||
return position < limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取到缓存<br>
|
|
||||||
* 全量读取,会重置Buffer中所有数据
|
|
||||||
*
|
|
||||||
* @param reader {@link Reader}
|
|
||||||
*/
|
|
||||||
int read(Reader reader) {
|
|
||||||
int length;
|
|
||||||
try {
|
|
||||||
length = reader.read(this.buf);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IORuntimeException(e);
|
|
||||||
}
|
|
||||||
this.mark = 0;
|
|
||||||
this.position = 0;
|
|
||||||
this.limit = length;
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 先获取当前字符,再将当前位置后移一位<br>
|
|
||||||
* 此方法不检查是否到了数组末尾,请自行使用{@link #hasRemaining()}判断。
|
|
||||||
*
|
|
||||||
* @return 当前位置字符
|
|
||||||
* @see #hasRemaining()
|
|
||||||
*/
|
|
||||||
char get() {
|
|
||||||
return this.buf[this.position++];
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean canRead(int position) {
|
|
||||||
return (this.position + position - 1) < limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
char read(int position) {
|
|
||||||
return this.buf[this.position + position - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标记位置记为下次读取位置
|
|
||||||
*/
|
|
||||||
void mark() {
|
|
||||||
this.mark = this.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将数据追加到{@link StrBuilder},追加结束后需手动调用{@link #mark()} 重置读取位置
|
|
||||||
*
|
|
||||||
* @param builder {@link StrBuilder}
|
|
||||||
* @param length 追加的长度
|
|
||||||
* @see #mark()
|
|
||||||
*/
|
|
||||||
void appendTo(StrBuilder builder, int length) {
|
|
||||||
builder.append(this.buf, this.mark, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,118 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV读取配置项
|
|
||||||
*
|
|
||||||
* @author looly
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CsvReadConfig extends CsvConfig<CsvReadConfig> implements Serializable {
|
|
||||||
private static final long serialVersionUID = 5396453565371560052L;
|
|
||||||
|
|
||||||
/** 指定标题行号,-1表示无标题行 */
|
|
||||||
protected long headerLineNo = -1;
|
|
||||||
/** 是否跳过空白行,默认true */
|
|
||||||
protected boolean skipEmptyRows = true;
|
|
||||||
/** 每行字段个数不同时是否抛出异常,默认false */
|
|
||||||
protected boolean errorOnDifferentFieldCount;
|
|
||||||
/** 定义开始的行(包括),此处为原始文件行号 */
|
|
||||||
protected long beginLineNo;
|
|
||||||
/** 结束的行(包括),此处为原始文件行号 */
|
|
||||||
protected long endLineNo = Long.MAX_VALUE-1;
|
|
||||||
/** 每个字段是否去除两边空白符 */
|
|
||||||
protected boolean trimField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认配置
|
|
||||||
*
|
|
||||||
* @return 默认配置
|
|
||||||
*/
|
|
||||||
public static CsvReadConfig defaultConfig() {
|
|
||||||
return new CsvReadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置是否首行做为标题行,默认false<br>
|
|
||||||
* 当设置为{@code true}时,默认标题行号是{@link #beginLineNo},{@code false}为-1,表示无行号
|
|
||||||
*
|
|
||||||
* @param containsHeader 是否首行做为标题行,默认false
|
|
||||||
* @return this
|
|
||||||
* @see #setHeaderLineNo(long)
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setContainsHeader(boolean containsHeader) {
|
|
||||||
return setHeaderLineNo(containsHeader ? beginLineNo : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置标题行行号,默认-1,表示无标题行<br>
|
|
||||||
*
|
|
||||||
* @param headerLineNo 标题行行号,-1表示无标题行
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.23
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setHeaderLineNo(long headerLineNo) {
|
|
||||||
this.headerLineNo = headerLineNo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置是否跳过空白行,默认true
|
|
||||||
*
|
|
||||||
* @param skipEmptyRows 是否跳过空白行,默认true
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setSkipEmptyRows(boolean skipEmptyRows) {
|
|
||||||
this.skipEmptyRows = skipEmptyRows;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置每行字段个数不同时是否抛出异常,默认false
|
|
||||||
*
|
|
||||||
* @param errorOnDifferentFieldCount 每行字段个数不同时是否抛出异常,默认false
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setErrorOnDifferentFieldCount(boolean errorOnDifferentFieldCount) {
|
|
||||||
this.errorOnDifferentFieldCount = errorOnDifferentFieldCount;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置开始的行(包括),默认0,此处为原始文件行号
|
|
||||||
*
|
|
||||||
* @param beginLineNo 开始的行号(包括)
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.4
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setBeginLineNo(long beginLineNo) {
|
|
||||||
this.beginLineNo = beginLineNo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置结束的行(包括),默认不限制,此处为原始文件行号
|
|
||||||
*
|
|
||||||
* @param endLineNo 结束的行号(包括)
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.4
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setEndLineNo(long endLineNo) {
|
|
||||||
this.endLineNo = endLineNo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置每个字段是否去除两边空白符<br>
|
|
||||||
* 如果字段以{@link #textDelimiter}包围,则保留两边空格
|
|
||||||
*
|
|
||||||
* @param trimField 去除两边空白符
|
|
||||||
* @return this
|
|
||||||
* @since 5.7.13
|
|
||||||
*/
|
|
||||||
public CsvReadConfig setTrimField(boolean trimField) {
|
|
||||||
this.trimField = trimField;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,153 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV文件读取器,参考:FastCSV
|
|
||||||
*
|
|
||||||
* @author Looly
|
|
||||||
* @since 4.0.1
|
|
||||||
*/
|
|
||||||
public class CsvReader extends CsvBaseReader implements Iterable<CsvRow>, Closeable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final Reader reader;
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------- Constructor start
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造,使用默认配置项
|
|
||||||
*/
|
|
||||||
public CsvReader() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param config 配置项
|
|
||||||
*/
|
|
||||||
public CsvReader(CsvReadConfig config) {
|
|
||||||
this((Reader) null, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造,默认{@link #DEFAULT_CHARSET}编码
|
|
||||||
*
|
|
||||||
* @param file CSV文件路径,null表示不设置路径
|
|
||||||
* @param config 配置项,null表示默认配置
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public CsvReader(File file, CsvReadConfig config) {
|
|
||||||
this(file, DEFAULT_CHARSET, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造,默认{@link #DEFAULT_CHARSET}编码
|
|
||||||
*
|
|
||||||
* @param path CSV文件路径,null表示不设置路径
|
|
||||||
* @param config 配置项,null表示默认配置
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public CsvReader(Path path, CsvReadConfig config) {
|
|
||||||
this(path, DEFAULT_CHARSET, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param file CSV文件路径,null表示不设置路径
|
|
||||||
* @param charset 编码
|
|
||||||
* @param config 配置项,null表示默认配置
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public CsvReader(File file, Charset charset, CsvReadConfig config) {
|
|
||||||
this(FileUtil.getReader(file, charset), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param path CSV文件路径,null表示不设置路径
|
|
||||||
* @param charset 编码
|
|
||||||
* @param config 配置项,null表示默认配置
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public CsvReader(Path path, Charset charset, CsvReadConfig config) {
|
|
||||||
this(FileUtil.getReader(path, charset), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param reader {@link Reader},null表示不设置默认reader
|
|
||||||
* @param config 配置项,null表示默认配置
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public CsvReader(Reader reader, CsvReadConfig config) {
|
|
||||||
super(config);
|
|
||||||
this.reader = reader;
|
|
||||||
}
|
|
||||||
//--------------------------------------------------------------------------------------------- Constructor end
|
|
||||||
/**
|
|
||||||
* 读取CSV文件,此方法只能调用一次<br>
|
|
||||||
* 调用此方法的前提是构造中传入文件路径或Reader
|
|
||||||
*
|
|
||||||
* @return {@link CsvData},包含数据列表和行信息
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
*/
|
|
||||||
public CsvData read() throws IORuntimeException {
|
|
||||||
return read(this.reader, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取CSV数据,此方法只能调用一次<br>
|
|
||||||
* 调用此方法的前提是构造中传入文件路径或Reader
|
|
||||||
*
|
|
||||||
* @param rowHandler 行处理器,用于一行一行的处理数据
|
|
||||||
* @throws IORuntimeException IO异常
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
public void read(CsvRowHandler rowHandler) throws IORuntimeException {
|
|
||||||
read(this.reader, false, rowHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据Reader创建{@link Stream},以便使用stream方式读取csv行
|
|
||||||
*
|
|
||||||
* @return {@link Stream}
|
|
||||||
* @since 5.7.14
|
|
||||||
*/
|
|
||||||
public Stream<CsvRow> stream() {
|
|
||||||
return StreamSupport.stream(spliterator(), false)
|
|
||||||
.onClose(() -> {
|
|
||||||
try {
|
|
||||||
close();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
throw new IORuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<CsvRow> iterator() {
|
|
||||||
return parse(this.reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
IoUtil.close(this.reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,262 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
|
||||||
import cn.hutool.core.lang.Assert;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV中一行的表示
|
|
||||||
*
|
|
||||||
* @author Looly
|
|
||||||
*/
|
|
||||||
public final class CsvRow implements List<String> {
|
|
||||||
|
|
||||||
/** 原始行号 */
|
|
||||||
private final long originalLineNumber;
|
|
||||||
|
|
||||||
final Map<String, Integer> headerMap;
|
|
||||||
final List<String> fields;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造
|
|
||||||
*
|
|
||||||
* @param originalLineNumber 对应文件中的第几行
|
|
||||||
* @param headerMap 标题Map
|
|
||||||
* @param fields 数据列表
|
|
||||||
*/
|
|
||||||
public CsvRow(long originalLineNumber, Map<String, Integer> headerMap, List<String> fields) {
|
|
||||||
Assert.notNull(fields, "fields must be not null!");
|
|
||||||
this.originalLineNumber = originalLineNumber;
|
|
||||||
this.headerMap = headerMap;
|
|
||||||
this.fields = fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取原始行号,多行情况下为首行行号。忽略注释行
|
|
||||||
*
|
|
||||||
* @return the original line number 行号
|
|
||||||
*/
|
|
||||||
public long getOriginalLineNumber() {
|
|
||||||
return originalLineNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取标题对应的字段内容
|
|
||||||
*
|
|
||||||
* @param name 标题名
|
|
||||||
* @return 字段值,null表示无此字段值
|
|
||||||
* @throws IllegalStateException CSV文件无标题行抛出此异常
|
|
||||||
*/
|
|
||||||
public String getByName(String name) {
|
|
||||||
Assert.notNull(this.headerMap, "No header available!");
|
|
||||||
|
|
||||||
final Integer col = headerMap.get(name);
|
|
||||||
if (col != null) {
|
|
||||||
return get(col);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取本行所有字段值列表
|
|
||||||
*
|
|
||||||
* @return 字段值列表
|
|
||||||
*/
|
|
||||||
public List<String> getRawList() {
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取标题与字段值对应的Map
|
|
||||||
*
|
|
||||||
* @return 标题与字段值对应的Map
|
|
||||||
* @throws IllegalStateException CSV文件无标题行抛出此异常
|
|
||||||
*/
|
|
||||||
public Map<String, String> getFieldMap() {
|
|
||||||
if (headerMap == null) {
|
|
||||||
throw new IllegalStateException("No header available");
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, String> fieldMap = new LinkedHashMap<>(headerMap.size(), 1);
|
|
||||||
String key;
|
|
||||||
Integer col;
|
|
||||||
String val;
|
|
||||||
for (final Map.Entry<String, Integer> header : headerMap.entrySet()) {
|
|
||||||
key = header.getKey();
|
|
||||||
col = headerMap.get(key);
|
|
||||||
val = null == col ? null : get(col);
|
|
||||||
fieldMap.put(key, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fieldMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一行数据转换为Bean对象
|
|
||||||
*
|
|
||||||
* @param <T> Bean类型
|
|
||||||
* @param clazz bean类
|
|
||||||
* @return Bean
|
|
||||||
* @since 5.3.6
|
|
||||||
*/
|
|
||||||
public <T> T toBean(Class<T> clazz){
|
|
||||||
return BeanUtil.toBeanIgnoreError(getFieldMap(), clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取字段格式
|
|
||||||
*
|
|
||||||
* @return 字段格式
|
|
||||||
*/
|
|
||||||
public int getFieldCount() {
|
|
||||||
return fields.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return this.fields.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.fields.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object o) {
|
|
||||||
return this.fields.contains(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<String> iterator() {
|
|
||||||
return this.fields.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] toArray() {
|
|
||||||
return this.fields.toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T[] toArray(T[] a) {
|
|
||||||
//noinspection SuspiciousToArrayCall
|
|
||||||
return this.fields.toArray(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean add(String e) {
|
|
||||||
return this.fields.add(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean remove(Object o) {
|
|
||||||
return this.fields.remove(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsAll(Collection<?> c) {
|
|
||||||
return this.fields.containsAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(Collection<? extends String> c) {
|
|
||||||
return this.fields.addAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(int index, Collection<? extends String> c) {
|
|
||||||
return this.fields.addAll(index, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeAll(Collection<?> c) {
|
|
||||||
return this.fields.removeAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean retainAll(Collection<?> c) {
|
|
||||||
return this.fields.retainAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
this.fields.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String get(int index) {
|
|
||||||
return index >= fields.size() ? null : fields.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String set(int index, String element) {
|
|
||||||
return this.fields.set(index, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(int index, String element) {
|
|
||||||
this.fields.add(index, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String remove(int index) {
|
|
||||||
return this.fields.remove(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int indexOf(Object o) {
|
|
||||||
return this.fields.indexOf(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int lastIndexOf(Object o) {
|
|
||||||
return this.fields.lastIndexOf(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ListIterator<String> listIterator() {
|
|
||||||
return this.fields.listIterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ListIterator<String> listIterator(int index) {
|
|
||||||
return this.fields.listIterator(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> subList(int fromIndex, int toIndex) {
|
|
||||||
return this.fields.subList(fromIndex, toIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
final StringBuilder sb = new StringBuilder("CsvRow{");
|
|
||||||
sb.append("originalLineNumber=");
|
|
||||||
sb.append(originalLineNumber);
|
|
||||||
sb.append(", ");
|
|
||||||
|
|
||||||
sb.append("fields=");
|
|
||||||
if (headerMap != null) {
|
|
||||||
sb.append('{');
|
|
||||||
for (final Iterator<Map.Entry<String, String>> it = getFieldMap().entrySet().iterator(); it.hasNext();) {
|
|
||||||
|
|
||||||
final Map.Entry<String, String> entry = it.next();
|
|
||||||
sb.append(entry.getKey());
|
|
||||||
sb.append('=');
|
|
||||||
if (entry.getValue() != null) {
|
|
||||||
sb.append(entry.getValue());
|
|
||||||
}
|
|
||||||
if (it.hasNext()) {
|
|
||||||
sb.append(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.append('}');
|
|
||||||
} else {
|
|
||||||
sb.append(fields.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append('}');
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV的行处理器,实现此接口用于按照行处理数据
|
|
||||||
*
|
|
||||||
* @author Looly
|
|
||||||
* @since 5.0.4
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface CsvRowHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理行数据
|
|
||||||
*
|
|
||||||
* @param row 行数据
|
|
||||||
*/
|
|
||||||
void handle(CsvRow row);
|
|
||||||
}
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
package cn.octopusyan.dmt.utils.csv;
|
|
||||||
|
|
||||||
import cn.hutool.core.text.csv.CsvWriteConfig;
|
|
||||||
import cn.hutool.core.text.csv.CsvWriter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSV工具
|
|
||||||
*
|
|
||||||
* @author looly
|
|
||||||
* @since 4.0.5
|
|
||||||
*/
|
|
||||||
public class CsvUtil {
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------------- Reader
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV读取器,调用此方法创建的Reader须自行指定读取的资源
|
|
||||||
*
|
|
||||||
* @param config 配置, 允许为空.
|
|
||||||
* @return {@link CsvReader}
|
|
||||||
*/
|
|
||||||
public static CsvReader getReader(CsvReadConfig config) {
|
|
||||||
return new CsvReader(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV读取器,调用此方法创建的Reader须自行指定读取的资源
|
|
||||||
*
|
|
||||||
* @return {@link CsvReader}
|
|
||||||
*/
|
|
||||||
public static CsvReader getReader() {
|
|
||||||
return new CsvReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV读取器
|
|
||||||
*
|
|
||||||
* @param reader {@link Reader}
|
|
||||||
* @param config 配置, {@code null}表示默认配置
|
|
||||||
* @return {@link CsvReader}
|
|
||||||
* @since 5.7.14
|
|
||||||
*/
|
|
||||||
public static CsvReader getReader(Reader reader, CsvReadConfig config) {
|
|
||||||
return new CsvReader(reader, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV读取器
|
|
||||||
*
|
|
||||||
* @param reader {@link Reader}
|
|
||||||
* @return {@link CsvReader}
|
|
||||||
* @since 5.7.14
|
|
||||||
*/
|
|
||||||
public static CsvReader getReader(Reader reader) {
|
|
||||||
return getReader(reader, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------------- Writer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器),使用默认配置,覆盖已有文件(如果存在)
|
|
||||||
*
|
|
||||||
* @param filePath File CSV文件路径
|
|
||||||
* @param charset 编码
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(String filePath, Charset charset) {
|
|
||||||
return new CsvWriter(filePath, charset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器),使用默认配置,覆盖已有文件(如果存在)
|
|
||||||
*
|
|
||||||
* @param file File CSV文件
|
|
||||||
* @param charset 编码
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(File file, Charset charset) {
|
|
||||||
return new CsvWriter(file, charset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器),使用默认配置
|
|
||||||
*
|
|
||||||
* @param filePath File CSV文件路径
|
|
||||||
* @param charset 编码
|
|
||||||
* @param isAppend 是否追加
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(String filePath, Charset charset, boolean isAppend) {
|
|
||||||
return new CsvWriter(filePath, charset, isAppend);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器),使用默认配置
|
|
||||||
*
|
|
||||||
* @param file File CSV文件
|
|
||||||
* @param charset 编码
|
|
||||||
* @param isAppend 是否追加
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(File file, Charset charset, boolean isAppend) {
|
|
||||||
return new CsvWriter(file, charset, isAppend);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器)
|
|
||||||
*
|
|
||||||
* @param file File CSV文件
|
|
||||||
* @param charset 编码
|
|
||||||
* @param isAppend 是否追加
|
|
||||||
* @param config 写出配置,null则使用默认配置
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(File file, Charset charset, boolean isAppend, CsvWriteConfig config) {
|
|
||||||
return new CsvWriter(file, charset, isAppend, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器)
|
|
||||||
*
|
|
||||||
* @param writer Writer
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(Writer writer) {
|
|
||||||
return new CsvWriter(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取CSV生成器(写出器)
|
|
||||||
*
|
|
||||||
* @param writer Writer
|
|
||||||
* @param config 写出配置,null则使用默认配置
|
|
||||||
* @return {@link CsvWriter}
|
|
||||||
*/
|
|
||||||
public static CsvWriter getWriter(Writer writer, CsvWriteConfig config) {
|
|
||||||
return new CsvWriter(writer, config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,16 +12,17 @@ module cn.octopusyan.dmt {
|
|||||||
requires static lombok;
|
requires static lombok;
|
||||||
requires atlantafx.base;
|
requires atlantafx.base;
|
||||||
requires com.fasterxml.jackson.annotation;
|
requires com.fasterxml.jackson.annotation;
|
||||||
requires com.fasterxml.jackson.databind;
|
requires tools.jackson.databind;
|
||||||
requires com.fasterxml.jackson.dataformat.yaml;
|
requires tools.jackson.dataformat.yaml;
|
||||||
requires java.prefs;
|
requires java.prefs;
|
||||||
requires org.kordamp.ikonli.javafx;
|
requires org.kordamp.ikonli.javafx;
|
||||||
requires org.kordamp.ikonli.feather;
|
requires org.kordamp.ikonli.feather;
|
||||||
requires java.management;
|
requires java.management;
|
||||||
requires cn.hutool.core;
|
requires cn.hutool.core;
|
||||||
|
requires jjwt.api;
|
||||||
|
|
||||||
exports cn.octopusyan.dmt;
|
exports cn.octopusyan.dmt;
|
||||||
exports cn.octopusyan.dmt.model to com.fasterxml.jackson.databind;
|
exports cn.octopusyan.dmt.model to tools.jackson.databind;
|
||||||
opens cn.octopusyan.dmt.model to javafx.base;
|
opens cn.octopusyan.dmt.model to javafx.base;
|
||||||
opens cn.octopusyan.dmt.common.base to javafx.fxml;
|
opens cn.octopusyan.dmt.common.base to javafx.fxml;
|
||||||
opens cn.octopusyan.dmt.controller to javafx.fxml;
|
opens cn.octopusyan.dmt.controller to javafx.fxml;
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user