Initial project
This commit is contained in:
250
src/main/java/com/imyeyu/utils/AsciiTable.java
Normal file
250
src/main/java/com/imyeyu/utils/AsciiTable.java
Normal file
@ -0,0 +1,250 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.bean.CallbackArgReturn;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @since 2024-12-18 10:10
|
||||
*/
|
||||
public class AsciiTable<T> {
|
||||
|
||||
/** 制表符水平边框 */
|
||||
private static final String BORDER_H = "─";
|
||||
|
||||
/** 制表符垂直边框 */
|
||||
private static final String BORDER_V = "│";
|
||||
|
||||
/** 制表符左上边框 */
|
||||
private static final String BORDER_TL = "┌";
|
||||
|
||||
/** 制表符上边框 */
|
||||
private static final String BORDER_T = "┬";
|
||||
|
||||
/** 制表符右上边框 */
|
||||
private static final String BORDER_TR = "┐";
|
||||
|
||||
/** 制表符左边框 */
|
||||
private static final String BORDER_L = "├";
|
||||
|
||||
/** 制表符十字边框 */
|
||||
private static final String BORDER_P = "┼";
|
||||
|
||||
/** 制表符右边框 */
|
||||
private static final String BORDER_R = "┤";
|
||||
|
||||
/** 制表符左下边框 */
|
||||
private static final String BORDER_BL = "└";
|
||||
|
||||
/** 制表符下边框 */
|
||||
private static final String BORDER_B = "┴";
|
||||
|
||||
/** 制表符右下边框 */
|
||||
private static final String BORDER_BR = "┘";
|
||||
|
||||
private final List<ColMapping<T>> colMappingList = new ArrayList<>();
|
||||
|
||||
private final List<Row> rowList = new ArrayList<>();
|
||||
|
||||
public String render(List<T> dataList) {
|
||||
try {
|
||||
|
||||
{
|
||||
// 表头
|
||||
Row header = new Row();
|
||||
for (int i = 0; i < colMappingList.size(); i++) {
|
||||
Row.Col col = new Row.Col();
|
||||
col.text = colMappingList.get(i).name;
|
||||
header.colList.add(col);
|
||||
}
|
||||
rowList.add(header);
|
||||
}
|
||||
{
|
||||
// 数据
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Row row = new Row();
|
||||
for (int j = 0; j < colMappingList.size(); j++) {
|
||||
Row.Col col = new Row.Col();
|
||||
Object objValue;
|
||||
|
||||
ColMapping<T> tColMapping = colMappingList.get(j);
|
||||
if (tColMapping.itemCallback == null) {
|
||||
objValue = Ref.getFieldValue(dataList.get(i), tColMapping.field, Object.class);
|
||||
} else {
|
||||
objValue = tColMapping.itemCallback.handler(dataList.get(i));
|
||||
}
|
||||
if (tColMapping.valueCallback != null) {
|
||||
objValue = tColMapping.valueCallback.handler(objValue.toString());
|
||||
}
|
||||
col.text = objValue.toString();
|
||||
row.colList.add(col);
|
||||
}
|
||||
rowList.add(row);
|
||||
}
|
||||
}
|
||||
// 对齐渲染
|
||||
Map<Integer, Integer> colMaxSize = new HashMap<>();
|
||||
for (int i = 0; i < rowList.size(); i++) {
|
||||
List<Row.Col> colList = rowList.get(i).colList;
|
||||
for (int j = 0; j < colList.size(); j++) {
|
||||
String colText = colList.get(j).text;
|
||||
if (!colMaxSize.containsKey(j)) {
|
||||
colMaxSize.put(j, colText.length());
|
||||
}
|
||||
colMaxSize.put(j, Math.max(colMaxSize.get(j), colText.length()));
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < rowList.size(); i++) {
|
||||
List<Row.Col> colList = rowList.get(i).colList;
|
||||
if (i == 0) {
|
||||
// 顶边
|
||||
sb.append(BORDER_TL);
|
||||
for (int j = 0; j < colList.size(); j++) {
|
||||
sb.append(BORDER_H.repeat(colMaxSize.get(j)));
|
||||
if (j == colList.size() - 1) {
|
||||
sb.append(BORDER_TR);
|
||||
} else {
|
||||
sb.append(BORDER_T);
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
// 数据
|
||||
for (int j = 0; j < colList.size(); j++) {
|
||||
String text = colList.get(j).text;
|
||||
if (j == 0) {
|
||||
sb.append(BORDER_V);
|
||||
}
|
||||
sb.append(Text.paddedSpaceEnd(text, colMaxSize.get(j))).append(BORDER_V);
|
||||
}
|
||||
sb.append('\n');
|
||||
if (i != rowList.size() - 1) {
|
||||
// 数据分割
|
||||
for (int j = 0; j < colList.size(); j++) {
|
||||
if (j == 0) {
|
||||
sb.append(BORDER_L);
|
||||
}
|
||||
sb.append(BORDER_H.repeat(colMaxSize.get(j)));
|
||||
if (j == colList.size() - 1) {
|
||||
sb.append(BORDER_R);
|
||||
} else {
|
||||
sb.append(BORDER_P);
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
} else {
|
||||
// 底边
|
||||
sb.append(BORDER_BL);
|
||||
for (int j = 0; j < colList.size(); j++) {
|
||||
sb.append(BORDER_H.repeat(colMaxSize.get(j)));
|
||||
if (j == colList.size() - 1) {
|
||||
sb.append(BORDER_BR);
|
||||
} else {
|
||||
sb.append(BORDER_B);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new TimiException(TimiCode.ERROR, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addHeader(String field) {
|
||||
addHeader(field.substring(0, 1).toUpperCase() + field.substring(1), field);
|
||||
}
|
||||
|
||||
public void addHeader(String name, String field) {
|
||||
TimiException.required(field, "not found field");
|
||||
|
||||
ColMapping<T> colMapping = new ColMapping<>();
|
||||
colMapping.name = name;
|
||||
colMapping.field = field;
|
||||
colMappingList.add(colMapping);
|
||||
}
|
||||
|
||||
public void addHeader(ColMapping<T> colMapping) {
|
||||
colMappingList.add(colMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-12-18 10:16
|
||||
*/
|
||||
public static class ColMapping<T> extends Row.Col {
|
||||
|
||||
private String name;
|
||||
|
||||
private String field;
|
||||
|
||||
private CallbackArgReturn<T, String> itemCallback;
|
||||
|
||||
private CallbackArgReturn<String, String> valueCallback;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public void setField(String field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public CallbackArgReturn<T, String> getItemCallback() {
|
||||
return itemCallback;
|
||||
}
|
||||
|
||||
public void setItemCallback(CallbackArgReturn<T, String> itemCallback) {
|
||||
this.itemCallback = itemCallback;
|
||||
}
|
||||
|
||||
public CallbackArgReturn<String, String> getValueCallback() {
|
||||
return valueCallback;
|
||||
}
|
||||
|
||||
public void setValueCallback(CallbackArgReturn<String, String> valueCallback) {
|
||||
this.valueCallback = valueCallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-12-18 10:10
|
||||
*/
|
||||
private static class Row {
|
||||
|
||||
List<Col> colList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-12-18 10:10
|
||||
*/
|
||||
private static class Col {
|
||||
|
||||
String text;
|
||||
}
|
||||
}
|
||||
}
|
||||
204
src/main/java/com/imyeyu/utils/Calc.java
Normal file
204
src/main/java/com/imyeyu/utils/Calc.java
Normal file
@ -0,0 +1,204 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 数学计算扩展
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2023-03-20 10:28
|
||||
*/
|
||||
public class Calc {
|
||||
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
/**
|
||||
* 是否为数字
|
||||
*
|
||||
* @param data 字符串
|
||||
* @return 为 true 是表示是数字
|
||||
*/
|
||||
public static boolean isNumber(String data) {
|
||||
try {
|
||||
Double.parseDouble(data);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向上取整返回整型
|
||||
*
|
||||
* @param v 数值
|
||||
* @return 取整结果
|
||||
*/
|
||||
public static int ceil(double v) {
|
||||
return (int) Math.ceil(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向下取整返回整型
|
||||
*
|
||||
* @param v 数值
|
||||
* @return 取整结果
|
||||
*/
|
||||
public static int floor(double v) {
|
||||
return (int) Math.floor(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 四舍五入返回整型
|
||||
*
|
||||
* @param v 数值
|
||||
* @return 结果
|
||||
*/
|
||||
public static int round(double v) {
|
||||
return (int) Math.round(v);
|
||||
}
|
||||
|
||||
public static double round(double v, int scale) {
|
||||
return round(v, scale, RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
public static double round(double v, int scale, RoundingMode mode) {
|
||||
BigDecimal bd = new BigDecimal(Double.toString(v));
|
||||
bd = bd.setScale(scale, mode);
|
||||
return bd.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 四舍五入返回长整型
|
||||
*
|
||||
* @param v 数值
|
||||
* @return 结果
|
||||
*/
|
||||
public static long roundLong(double v) {
|
||||
return Math.round(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围内取随机值 [min, max]
|
||||
*
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @return 随机值
|
||||
*/
|
||||
public static int random(int min, int max) {
|
||||
if (max < min) {
|
||||
throw new IllegalArgumentException("min must less than max");
|
||||
}
|
||||
return RANDOM.nextInt(max + 1 - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围内取随机值 [min, max]
|
||||
*
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @return 随机值
|
||||
*/
|
||||
public static long random(long min, long max) {
|
||||
if (max < min) {
|
||||
throw new IllegalArgumentException("min must less than max");
|
||||
}
|
||||
return RANDOM.nextLong(max + 1 - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围内取随机值 (min, max)
|
||||
*
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @return 随机值
|
||||
*/
|
||||
public static float random(float min, float max) {
|
||||
if (max < min) {
|
||||
throw new IllegalArgumentException("min must less than max");
|
||||
}
|
||||
return RANDOM.nextFloat(max - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围内取随机值 (min, max)
|
||||
*
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @return 随机值
|
||||
*/
|
||||
public static double random(double min, double max) {
|
||||
if (max < min) {
|
||||
throw new IllegalArgumentException("min must less than max");
|
||||
}
|
||||
return RANDOM.nextDouble(max - min) + min;
|
||||
}
|
||||
|
||||
public static boolean randomBoolean() {
|
||||
return random(0, 1) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两数差值
|
||||
*
|
||||
* @param v0 第一个数
|
||||
* @param v1 第二个数
|
||||
* @return 差值
|
||||
* @param <T> 类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Number> T between(T v0, T v1) {
|
||||
if (v0 instanceof BigDecimal bd0 && v1 instanceof BigDecimal bd1) {
|
||||
return (T) (Double) bd0.subtract(bd1).abs().doubleValue();
|
||||
}
|
||||
return (T) (Double) Math.abs(v0.doubleValue() - v1.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算一个数字是否在区间内
|
||||
*
|
||||
* @param min 区间最小值
|
||||
* @param max 区间最大值
|
||||
* @param number 判定值
|
||||
* @return true 时,number 在 min 和 max 之间
|
||||
*/
|
||||
public static boolean in(double min, double max, double number) {
|
||||
if (max < min) {
|
||||
throw new IllegalArgumentException("min must less than max");
|
||||
}
|
||||
return min <= number && number <= max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为正整数
|
||||
*
|
||||
* @param num 数字
|
||||
* @return true 为正整数
|
||||
*/
|
||||
public static boolean isN1(Long num) {
|
||||
if (num == null) {
|
||||
return false;
|
||||
}
|
||||
return 0 < num;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全限制数值范围,当入参值在最小值和最大值之间时返回原值,否则返回最小或最大值
|
||||
*
|
||||
* @param min 最小值
|
||||
* @param value 入参值
|
||||
* @param max 最大值
|
||||
* @return 限制结果
|
||||
*/
|
||||
public static Number range(Number min, Number value, Number max) {
|
||||
if (value.doubleValue() < min.doubleValue()) {
|
||||
return min;
|
||||
}
|
||||
if (max.doubleValue() < value.doubleValue()) {
|
||||
return max;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
355
src/main/java/com/imyeyu/utils/Collect.java
Normal file
355
src/main/java/com/imyeyu/utils/Collect.java
Normal file
@ -0,0 +1,355 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2023-08-07 11:50
|
||||
*/
|
||||
public class Collect {
|
||||
|
||||
/**
|
||||
* 数组元素移除
|
||||
*
|
||||
* @param array 源数组
|
||||
* @param items 移除项
|
||||
* @param <T> 数组数据类型
|
||||
* @return 移除结果数组
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] arrayRemove(T[] array, T... items) {
|
||||
if (array == null) {
|
||||
return null;
|
||||
}
|
||||
if (TimiJava.isEmpty(array) || TimiJava.isEmpty(items)) {
|
||||
return array;
|
||||
}
|
||||
Map<T, Integer> occurrences = new HashMap<>(array.length);
|
||||
for (T v : items) {
|
||||
occurrences.merge(v, 1, Integer::sum);
|
||||
}
|
||||
BitSet toRemove = new BitSet();
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
T key = array[i];
|
||||
Integer count = occurrences.get(key);
|
||||
if (count != null) {
|
||||
occurrences.put(key, count - 1);
|
||||
if (occurrences.get(key) == 0) {
|
||||
occurrences.remove(key);
|
||||
}
|
||||
toRemove.set(i);
|
||||
}
|
||||
}
|
||||
int srcLength = array.length;
|
||||
int removals = toRemove.cardinality();
|
||||
T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), srcLength - removals);
|
||||
int srcIndex = 0;
|
||||
int destIndex = 0;
|
||||
int count;
|
||||
int set;
|
||||
while ((set = toRemove.nextSetBit(srcIndex)) != -1) {
|
||||
count = set - srcIndex;
|
||||
if (count > 0) {
|
||||
System.arraycopy(array, srcIndex, result, destIndex, count);
|
||||
destIndex += count;
|
||||
}
|
||||
srcIndex = toRemove.nextClearBit(set);
|
||||
}
|
||||
count = srcLength - srcIndex;
|
||||
if (count > 0) {
|
||||
System.arraycopy(array, srcIndex, result, destIndex, count);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取出哈希表的键作为列表
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param <K> 键泛型
|
||||
* @param <V> 值泛型
|
||||
* @return 以哈希表键为类型的列表
|
||||
*/
|
||||
public static <K, V> List<K> mapKeys(Map<K, V> map) {
|
||||
if ((map != null) && (!map.isEmpty())) {
|
||||
return new ArrayList<>(map.keySet());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机哈希表
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param limit 数量限制
|
||||
* @param <K> 键泛型
|
||||
* @param <V> 值泛型
|
||||
* @return 随机结果
|
||||
*/
|
||||
public static <K, V> Map<K, V> randomMap(Map<K, V> map, int limit) {
|
||||
Map<K, V> result = new LinkedHashMap<>();
|
||||
List<K> list = mapKeys(map);
|
||||
list.sort((lhs, rhs) -> {
|
||||
int r1 = (int) (Math.random() * 10 + lhs.hashCode());
|
||||
int r2 = (int) (Math.random() * 10 + rhs.hashCode());
|
||||
return r1 - r2;
|
||||
});
|
||||
for (int i = 0, l = list.size(); i < l; i++) {
|
||||
if (result.size() < limit) {
|
||||
result.put(list.get(i), map.get(list.get(i)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据键排序哈希表
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param comparator 比较器
|
||||
* @param <K> 键泛型
|
||||
* @param <V> 值泛型
|
||||
* @return 排序结果
|
||||
*/
|
||||
public static <K, V> Map<K, V> sortMap(Map<K, V> map, Comparator<K> comparator) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
if (map.isEmpty()) {
|
||||
return map;
|
||||
}
|
||||
Map<K, V> r = new TreeMap<>(comparator);
|
||||
r.putAll(map);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数字值排序 Map(正序)
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param <K> 键类型
|
||||
* @return 排序结果列表
|
||||
*/
|
||||
public static <K> LinkedHashMap<K, Number> sortMapByNumberValueASC(Map<K, Number> map) {
|
||||
return sortMapByNumberValue(map, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数字值排序 Map(倒序)
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param <K> 键类型
|
||||
* @return 排序结果列表
|
||||
*/
|
||||
public static <K> LinkedHashMap<K, Number> sortMapByNumberValueDESC(Map<K, Number> map) {
|
||||
return sortMapByNumberValue(map, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数字值排序 Map
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param isASC true 为正序
|
||||
* @param <K> 键类型
|
||||
* @return 排序结果列表
|
||||
*/
|
||||
public static <K> LinkedHashMap<K, Number> sortMapByNumberValue(Map<K, Number> map, boolean isASC) {
|
||||
return sortMapByValue(map, (o1, o2) -> {
|
||||
if (isASC) {
|
||||
return Double.compare(o1.getValue().doubleValue(), o2.getValue().doubleValue());
|
||||
} else {
|
||||
return Double.compare(o2.getValue().doubleValue(), o1.getValue().doubleValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值排序 Map
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param comparator 比较器
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @return 排序结果列表
|
||||
*/
|
||||
public static <K, V> LinkedHashMap<K, V> sortMapByValue(Map<K, V> map, Comparator<Map.Entry<K, V>> comparator) {
|
||||
List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
|
||||
list.sort(comparator);
|
||||
|
||||
LinkedHashMap<K, V> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<K, V> entry : list) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字符串键排序哈希表
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param <V> 值泛型
|
||||
* @return 排序结果
|
||||
*/
|
||||
public static <V> Map<String, V> sortMapByStringKeyASC(Map<String, V> map) {
|
||||
return sortMap(map, String::compareTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数字键排序哈希表
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param <V> 值泛型
|
||||
* @return 排序结果
|
||||
*/
|
||||
public static <V> Map<Number, V> sortMapByNumberKeyASC(Map<Number, V> map) {
|
||||
return sortMap(map, Comparator.comparingDouble(Number::doubleValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数字键排序哈希表
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param <V> 值泛型
|
||||
* @return 排序结果
|
||||
*/
|
||||
public static <V> Map<Number, V> sortMapByNumberKeyDESC(Map<Number, V> map) {
|
||||
return sortMap(map, Comparator.comparingDouble(Number::doubleValue).reversed());
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地根据键移除哈希表的对象
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param key 键
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @return 被移除值
|
||||
*/
|
||||
public static <K, V> V removeByKey(Map<K, V> map, K key) {
|
||||
Iterator<K> iterator = map.keySet().iterator();
|
||||
K k;
|
||||
while (iterator.hasNext()) {
|
||||
k = iterator.next();
|
||||
if (key.equals(k)) {
|
||||
V v = map.get(k);
|
||||
iterator.remove();
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地根据一些键移除哈希表的对象
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param keys 键
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @return 被移除值列表
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <K, V> List<V> removeByKeys(Map<K, V> map, K... keys) {
|
||||
Set<K> keysList = Set.of(keys);
|
||||
List<V> removes = new ArrayList<>();
|
||||
Iterator<K> iterator = map.keySet().iterator();
|
||||
K k;
|
||||
while (iterator.hasNext()) {
|
||||
k = iterator.next();
|
||||
if (keysList.contains(k)) {
|
||||
removes.add(map.get(k));
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return removes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地根据值从哈希表移除数据
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param value 值
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @return 被移除的键
|
||||
*/
|
||||
public static <K, V> K removeByValue(Map<K, V> map, V value) {
|
||||
Iterator<K> iterator = map.keySet().iterator();
|
||||
K k;
|
||||
while (iterator.hasNext()) {
|
||||
k = iterator.next();
|
||||
if (map.get(k).equals(value)) {
|
||||
iterator.remove();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地根据一些值从哈希表移除数据
|
||||
*
|
||||
* @param map 哈希表
|
||||
* @param values 值
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @return 被移除的键列表
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <K, V> List<K> removeByValues(Map<K, V> map, V... values) {
|
||||
Set<V> valuesList = Set.of(values);
|
||||
List<K> removes = new ArrayList<>();
|
||||
Iterator<K> iterator = map.keySet().iterator();
|
||||
K k;
|
||||
while (iterator.hasNext()) {
|
||||
k = iterator.next();
|
||||
if (valuesList.contains(map.get(k))) {
|
||||
iterator.remove();
|
||||
removes.add(k);
|
||||
}
|
||||
}
|
||||
return removes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 深克隆列表(通过序列化)
|
||||
*
|
||||
* @param list 列表
|
||||
* @param <T> 数据类型
|
||||
* @return 克隆列表
|
||||
* @throws Exception 克隆异常
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> deepCopyList(List<T> list) throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(list);
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
List<T> result = (List<T>) in.readObject();
|
||||
|
||||
in.close();
|
||||
bais.close();
|
||||
oos.close();
|
||||
baos.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
105
src/main/java/com/imyeyu/utils/Decoder.java
Normal file
105
src/main/java/com/imyeyu/utils/Decoder.java
Normal file
@ -0,0 +1,105 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* 编码操作
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-02-13 10:59
|
||||
*/
|
||||
public class Decoder {
|
||||
|
||||
/**
|
||||
* 解码 Unicode 字符串
|
||||
*
|
||||
* @param data Unicode 字符串
|
||||
* @return 解码结果
|
||||
*/
|
||||
public static String unicode(String data) {
|
||||
StringWriter out = new StringWriter(data.length());
|
||||
|
||||
StringBuilder unicode = new StringBuilder(4);
|
||||
boolean hadSlash = false;
|
||||
boolean inUnicode = false;
|
||||
for (int i = 0, l = data.length(); i < l; i++) {
|
||||
char c = data.charAt(i);
|
||||
if (inUnicode) {
|
||||
unicode.append(c);
|
||||
if (unicode.length() == 4) {
|
||||
try {
|
||||
out.write((char) Integer.parseInt(unicode.toString(), 16));
|
||||
unicode.setLength(0);
|
||||
inUnicode = false;
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new TimiException(TimiCode.ERROR, "Unable to parse unicode value: " + unicode);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (hadSlash) {
|
||||
hadSlash = false;
|
||||
switch (c) {
|
||||
case '\\' -> out.write('\\');
|
||||
case '\'' -> out.write('\'');
|
||||
case '\"' -> out.write('"');
|
||||
case 'r' -> out.write('\r');
|
||||
case 'f' -> out.write('\f');
|
||||
case 't' -> out.write('\t');
|
||||
case 'n' -> out.write('\n');
|
||||
case 'b' -> out.write('\b');
|
||||
case 'u' -> inUnicode = true;
|
||||
default -> out.write(c);
|
||||
}
|
||||
continue;
|
||||
} else if (c == '\\') {
|
||||
hadSlash = true;
|
||||
continue;
|
||||
}
|
||||
out.write(c);
|
||||
}
|
||||
if (hadSlash) {
|
||||
out.write('\\');
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码 Base64 字符串
|
||||
*
|
||||
* @param data Base64 字符串
|
||||
* @return 解码结果
|
||||
*/
|
||||
public static byte[] base64(String data) {
|
||||
return Base64.getDecoder().decode(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码 Base64 字符串
|
||||
*
|
||||
* @param data Base64 字符串
|
||||
* @return 解码结果
|
||||
*/
|
||||
public static String base64String(String data) {
|
||||
return new String(base64(data), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码 URL 链接
|
||||
*
|
||||
* @param url 已编码的 URL 链接
|
||||
* @return 解码结果
|
||||
*/
|
||||
public static String url(String url) {
|
||||
if (url == null) {
|
||||
return "";
|
||||
}
|
||||
return URLDecoder.decode(url, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
83
src/main/java/com/imyeyu/utils/Digest.java
Normal file
83
src/main/java/com/imyeyu/utils/Digest.java
Normal file
@ -0,0 +1,83 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2023-08-07 14:27
|
||||
*/
|
||||
public class Digest {
|
||||
|
||||
public static String md5(String data) throws NoSuchAlgorithmException {
|
||||
return md5(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String md5(String data, Charset charset) throws NoSuchAlgorithmException {
|
||||
return md5(data.getBytes(charset));
|
||||
}
|
||||
|
||||
public static String md5(byte[] data) throws NoSuchAlgorithmException {
|
||||
if (data == null || data.length == 0) {
|
||||
return null;
|
||||
}
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
md5.update(data);
|
||||
byte[] bytes = md5.digest();
|
||||
char[] chars = new char[bytes.length * 2];
|
||||
for (int i = 0, j = 0; i < bytes.length; i++) {
|
||||
chars[j++] = Text.HEX_DIGITS_LOWER[bytes[i] >>> 4 & 0xF];
|
||||
chars[j++] = Text.HEX_DIGITS_LOWER[bytes[i] & 0xF];
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public static String sha1(String data) throws NoSuchAlgorithmException {
|
||||
return sha1(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String sha1(String data, Charset charset) throws NoSuchAlgorithmException {
|
||||
return sha1(data.getBytes(charset));
|
||||
}
|
||||
|
||||
public static String sha1(byte[] bytes) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA");
|
||||
sha.update(bytes);
|
||||
return Text.byteToHex(sha.digest());
|
||||
}
|
||||
|
||||
public static String sha256(String data) throws NoSuchAlgorithmException {
|
||||
return sha256(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String sha256(String data, Charset charset) throws NoSuchAlgorithmException {
|
||||
return sha256(data.getBytes(charset));
|
||||
}
|
||||
|
||||
public static String sha256(byte[] bytes) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA-256");
|
||||
sha.update(bytes);
|
||||
return Text.byteToHex(sha.digest());
|
||||
}
|
||||
|
||||
public static String sha512(String data) throws NoSuchAlgorithmException {
|
||||
return sha512(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String sha512(String data, Charset charset) throws NoSuchAlgorithmException {
|
||||
return sha512(data.getBytes(charset));
|
||||
}
|
||||
|
||||
public static String sha512(byte[] bytes) throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA-512");
|
||||
BigInteger number = new BigInteger(1, sha.digest(bytes));
|
||||
StringBuilder result = new StringBuilder(number.toString(16));
|
||||
while (result.length() < 64) {
|
||||
result.insert(0, "0");
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
162
src/main/java/com/imyeyu/utils/Encoder.java
Normal file
162
src/main/java/com/imyeyu/utils/Encoder.java
Normal file
@ -0,0 +1,162 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.timi.TimiCode;
|
||||
import com.imyeyu.java.bean.timi.TimiException;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.net.IDN;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 编码操作
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-02-13 10:59
|
||||
*/
|
||||
public class Encoder {
|
||||
|
||||
/**
|
||||
* Unicode 编码全角字符
|
||||
*
|
||||
* @param data 字符串
|
||||
* @return 编码结果
|
||||
*/
|
||||
public static String unicode(String data) {
|
||||
StringWriter out = new StringWriter(data.length() * 2);
|
||||
for (int i = 0, l = data.length(); i < l; i++) {
|
||||
char c = data.charAt(i);
|
||||
String cHex = Integer.toHexString(c).toUpperCase(Locale.ENGLISH);
|
||||
|
||||
if (0xFFF < c) {
|
||||
out.write("\\u" + cHex);
|
||||
} else if (0xFF < c) {
|
||||
out.write("\\u0" + cHex);
|
||||
} else if (0x7F < c) {
|
||||
out.write("\\u00" + cHex);
|
||||
} else if (c < 32) {
|
||||
switch (c) {
|
||||
case '\b' -> {
|
||||
out.write('\\');
|
||||
out.write('b');
|
||||
}
|
||||
case '\n' -> {
|
||||
out.write('\\');
|
||||
out.write('n');
|
||||
}
|
||||
case '\t' -> {
|
||||
out.write('\\');
|
||||
out.write('t');
|
||||
}
|
||||
case '\f' -> {
|
||||
out.write('\\');
|
||||
out.write('f');
|
||||
}
|
||||
case '\r' -> {
|
||||
out.write('\\');
|
||||
out.write('r');
|
||||
}
|
||||
default -> {
|
||||
if (0xF < c) {
|
||||
out.write("\\u00" + cHex);
|
||||
} else {
|
||||
out.write("\\u000" + cHex);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (c) {
|
||||
case '\'' -> {
|
||||
out.write('\\');
|
||||
out.write('\'');
|
||||
}
|
||||
case '"' -> {
|
||||
out.write('\\');
|
||||
out.write('"');
|
||||
}
|
||||
case '\\' -> {
|
||||
out.write('\\');
|
||||
out.write('\\');
|
||||
}
|
||||
case '/' -> {
|
||||
out.write('\\');
|
||||
out.write('/');
|
||||
}
|
||||
default -> out.write(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 编码字符串
|
||||
*
|
||||
* @param data 字符串
|
||||
* @return 编码结果
|
||||
*/
|
||||
public static String base64(String data) {
|
||||
return base64(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 编码字节数据
|
||||
*
|
||||
* @param bytes 字节数据
|
||||
* @return 编码结果
|
||||
*/
|
||||
public static String base64(byte[] bytes) {
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码 URL 参数值。不要传整个链接,此方法只用于<u>参数值</u>的编码
|
||||
*
|
||||
* @param data URL 参数
|
||||
* @return 编码结果
|
||||
*/
|
||||
public static String urlArg(String data) {
|
||||
return URLEncoder.encode(data, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码 URL 参数
|
||||
*
|
||||
* @param args URL 参数表
|
||||
* @return 编码结果
|
||||
*/
|
||||
public static String urlArgs(Map<String, Object> args) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, Object> param : args.entrySet()) {
|
||||
sb.append('&').append(param.getKey()).append('=').append(urlArg(param.getValue().toString()));
|
||||
}
|
||||
return sb.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码 URL 链接,半角 "+" 号会使用 "%2B"
|
||||
*
|
||||
* @param urlStr URL 链接
|
||||
* @return 编码结果
|
||||
*/
|
||||
public static String url(String urlStr) {
|
||||
if (TimiJava.isEmpty(urlStr)) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
URL url = new URL(urlStr);
|
||||
URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
|
||||
return uri.toASCIIString().replace("+", "%2B");
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw new TimiException(TimiCode.ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
261
src/main/java/com/imyeyu/utils/OS.java
Normal file
261
src/main/java/com/imyeyu/utils/OS.java
Normal file
@ -0,0 +1,261 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2023-08-07 11:46
|
||||
*/
|
||||
public class OS {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2021-02-13 10:15
|
||||
*/
|
||||
public interface FileSystem {
|
||||
|
||||
/** 文件系统路径分隔符 FileSystem.separator */
|
||||
String SEP = File.separator;
|
||||
|
||||
/** 文件名排序,优先文件和文件夹,次级名称 */
|
||||
Comparator<File> COMPARATOR_FILE_NAME = (f1, f2) -> {
|
||||
if (f1.isDirectory() && f2.isFile()) {
|
||||
return -1;
|
||||
} else {
|
||||
if (f1.isFile() && f2.isDirectory()) {
|
||||
return 1;
|
||||
} else {
|
||||
return f1.getName().compareToIgnoreCase(f2.getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统平台
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2024-06-22 14:41
|
||||
*/
|
||||
public enum Platform {
|
||||
|
||||
WINDOWS,
|
||||
|
||||
LINUX,
|
||||
|
||||
MAC
|
||||
}
|
||||
|
||||
/** 运行时系统 */
|
||||
public static final String NAME = System.getProperty("os.name");
|
||||
|
||||
/** true 为 Windows 系统 */
|
||||
public static final boolean IS_WINDOWS = NAME.toLowerCase().contains("win");
|
||||
|
||||
/** true 为 Mac OSX 系统 */
|
||||
public static final boolean IS_OSX = NAME.toLowerCase().contains("mac os x");
|
||||
|
||||
/** true 为 UNIX 系统 */
|
||||
public static final boolean IS_UNIX = NAME.contains("nix") || NAME.contains("nux") || NAME.contains("mac");
|
||||
|
||||
/** 当前系统平台 */
|
||||
public static final Platform PLATFORM = IS_WINDOWS ? Platform.WINDOWS : (IS_OSX ? Platform.MAC : Platform.LINUX);
|
||||
|
||||
/**
|
||||
* 不处理异常执行命令
|
||||
*
|
||||
* @param command 命令
|
||||
*/
|
||||
public static void run(String command) {
|
||||
try {
|
||||
Runtime.getRuntime().exec(new String[] {command});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止程序时执行命令(主线程结束后)
|
||||
*
|
||||
* @param command 命令
|
||||
*/
|
||||
public static void runAfterShutdown(String command) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
new ProcessBuilder(command).start();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/** @return 系统内存大小(单位:字节) */
|
||||
public static Long getSystemMemorySize() {
|
||||
return ((OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean()).getTotalMemorySize();
|
||||
}
|
||||
|
||||
/** Windows 系统禁用的字符 */
|
||||
public static final Character[] INVALID_WINDOWS_SPECIFIC_CHARS = {'"', '*', ':', '<', '>', '?', '\\', '|', '/', 0x7F};
|
||||
|
||||
/** Unix 系统禁用的字符 */
|
||||
public static final Character[] INVALID_UNIX_SPECIFIC_CHARS = {'\000'};
|
||||
|
||||
/**
|
||||
* 文件名规则验证
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return true 为有效的
|
||||
*/
|
||||
public static boolean isValidFileName(String fileName) {
|
||||
if (fileName == null || fileName.isEmpty() || 255 < fileName.length()) {
|
||||
return false;
|
||||
}
|
||||
Character[] chars;
|
||||
if (IS_WINDOWS) {
|
||||
chars = INVALID_WINDOWS_SPECIFIC_CHARS;
|
||||
} else if (IS_UNIX) {
|
||||
chars = INVALID_UNIX_SPECIFIC_CHARS;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (fileName.contains(chars[i].toString())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用系统资源管理器打开位置
|
||||
*
|
||||
* @param dir 文件
|
||||
*/
|
||||
public static void showInExplorer(File dir) {
|
||||
if (dir == null || !dir.exists()) {
|
||||
throw new IllegalArgumentException("dir is not found");
|
||||
}
|
||||
if (IS_WINDOWS) {
|
||||
if (dir.isFile()) {
|
||||
dir = dir.getParentFile();
|
||||
}
|
||||
run("explorer " + dir.getAbsolutePath() + FileSystem.SEP);
|
||||
} else {
|
||||
Desktop.getDesktop().browseFileDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用系统资源管理器打开文件位置并选中
|
||||
*
|
||||
* @param files 文件列表
|
||||
*/
|
||||
public static void showAndSelectInExplorer(File... files) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
sb.append('"').append(files[i].getAbsolutePath()).append('"').append(',');
|
||||
}
|
||||
if (IS_WINDOWS) {
|
||||
run("explorer /select," + sb.substring(0, sb.length() - 1));
|
||||
} else {
|
||||
Desktop.getDesktop().browseFileDirectory(files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查某程序的某进程是否在运行(Windows 方法)
|
||||
*
|
||||
* @param appName 程序名
|
||||
* @param processName 进程名
|
||||
* @param excludeProcessName 排除名称
|
||||
* @return true 为正在运行
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
public static boolean findProcess4Similarity(String appName, String processName, String... excludeProcessName) throws Exception {
|
||||
return findProcess(appName, processName, true, .8F, excludeProcessName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查某程序的某进程是否在运行(Windows 方法)
|
||||
*
|
||||
* @param appName 程序名
|
||||
* @param processName 进程名
|
||||
* @param similarity true 为启用相似度搜索
|
||||
* @param similarityRate 相似度达到多少判定为 true
|
||||
* @param excludeProcessName 排除名称
|
||||
* @return true 为正在运行
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
public static boolean findProcess(String appName, String processName, boolean similarity, float similarityRate, String... excludeProcessName) throws Exception {
|
||||
Process proc = Runtime.getRuntime().exec("tasklist -v -fi " + '"' + "imagename eq " + appName + '"');
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
|
||||
|
||||
int titleStart = -1; // 线程名起始字符
|
||||
String line;
|
||||
whi1e:
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (titleStart == -1) {
|
||||
if (line.startsWith("===")) {
|
||||
titleStart = line.lastIndexOf(" ");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(appName)) {
|
||||
// 排除名
|
||||
if (excludeProcessName != null) {
|
||||
for (int i = 0; i < excludeProcessName.length; i++) {
|
||||
if (line.contains(excludeProcessName[i])) {
|
||||
continue whi1e;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 相似度匹配
|
||||
if (similarity && titleStart < line.length()) {
|
||||
String title = line.substring(titleStart).trim();
|
||||
if (!title.equals("")) {
|
||||
if (similarityRate < Text.similarityRatio(processName, title)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return line.contains(processName);
|
||||
}
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字符串到剪切板(复制)
|
||||
*
|
||||
* @param s 字符串
|
||||
*/
|
||||
public static void setIntoClipboard(String s) {
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剪切版的字符串(粘贴)
|
||||
*
|
||||
* @return 剪切板字符串,如果剪切板没有字符串将返回空的字符串
|
||||
*/
|
||||
public static String getIntoClipboard() {
|
||||
try {
|
||||
return Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).getTransferData(DataFlavor.stringFlavor).toString();
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/main/java/com/imyeyu/utils/StringInterpolator.java
Normal file
128
src/main/java/com/imyeyu/utils/StringInterpolator.java
Normal file
@ -0,0 +1,128 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
import com.imyeyu.java.bean.CallbackArgReturn;
|
||||
import com.imyeyu.java.ref.Ref;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 字符串插值器
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2022-12-01 00:41
|
||||
*/
|
||||
public class StringInterpolator {
|
||||
|
||||
/** ${} 插值正则 */
|
||||
public static final String DOLLAR_OBJ = "\\$\\{(.+?)\\}";
|
||||
|
||||
/** {} 插值正则 */
|
||||
public static final String SIMPLE_OBJ = "\\{(.+?)\\}";
|
||||
|
||||
/** 正则匹配器 */
|
||||
private final Pattern pattern;
|
||||
|
||||
/** 为 true 时允许安全插值空,变量可以插值 null 而不抛出异常 */
|
||||
private boolean nullable = false;
|
||||
|
||||
/** 过滤器 */
|
||||
private Map<String, CallbackArgReturn<String, String>> filterMap;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
*
|
||||
* @param regex 插槽正则
|
||||
*/
|
||||
public StringInterpolator(String regex) {
|
||||
this.pattern = Pattern.compile(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入变量
|
||||
*
|
||||
* @param string 字符串
|
||||
* @param argsMap 变量表
|
||||
* @return 插值结果
|
||||
*/
|
||||
public String inject(String string, Map<String, Object> argsMap) {
|
||||
return pattern.matcher(string).replaceAll(result -> {
|
||||
String group = result.group(1);
|
||||
String key = group.trim();
|
||||
String[] filters = null;
|
||||
if (group.contains("|")) {
|
||||
// 过滤器
|
||||
String[] groups = group.split("\\|");
|
||||
key = groups[0].trim();
|
||||
filters = new String[groups.length - 1];
|
||||
System.arraycopy(groups, 1, filters, 0, filters.length);
|
||||
}
|
||||
Object value = argsMap.get(key);
|
||||
if (key.contains(".")) {
|
||||
// 反射获取
|
||||
try {
|
||||
String[] deepKey = key.split("\\.");
|
||||
value = argsMap.get(deepKey[0]);
|
||||
for (int i = 1; i < deepKey.length; i++) {
|
||||
value = Ref.getFieldValue(value, deepKey[i], Object.class);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("ref field file: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (TimiJava.isNotEmpty(filterMap) && TimiJava.isNotEmpty(filters)) {
|
||||
// 过滤
|
||||
for (int i = 0; i < filters.length; i++) {
|
||||
CallbackArgReturn<String, String> filter = filterMap.get(filters[i].trim());
|
||||
if (filter == null) {
|
||||
throw new NullPointerException("not found %s filter for %s".formatted(filters[i], key));
|
||||
}
|
||||
value = filter.handler((String) value);
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
if (!nullable) {
|
||||
throw new NullPointerException("null pointer exception for arg: " + key);
|
||||
}
|
||||
value = "";
|
||||
}
|
||||
return String.valueOf(value);
|
||||
});
|
||||
}
|
||||
|
||||
public void putFilter(String name, CallbackArgReturn<String, String> callback) {
|
||||
if (filterMap == null) {
|
||||
filterMap = new HashMap<>();
|
||||
}
|
||||
filterMap.put(name, callback);
|
||||
}
|
||||
|
||||
public void putAllFilter(Map<String, CallbackArgReturn<String, String>> filterMap) {
|
||||
if (this.filterMap == null) {
|
||||
this.filterMap = new HashMap<>();
|
||||
}
|
||||
this.filterMap.putAll(filterMap);
|
||||
}
|
||||
|
||||
public void removeFilter(String name) {
|
||||
if (TimiJava.isNotEmpty(filterMap)) {
|
||||
filterMap.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearFilter() {
|
||||
if (TimiJava.isNotEmpty(filterMap)) {
|
||||
filterMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setNullable(boolean nullable) {
|
||||
this.nullable = nullable;
|
||||
}
|
||||
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
}
|
||||
463
src/main/java/com/imyeyu/utils/Text.java
Normal file
463
src/main/java/com/imyeyu/utils/Text.java
Normal file
@ -0,0 +1,463 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author 夜雨
|
||||
* @version 2023-08-07 11:58
|
||||
*/
|
||||
public class Text {
|
||||
|
||||
/** 十六进制小写 */
|
||||
public static char[] HEX_DIGITS_LOWER = "0123456789abcdef".toCharArray();
|
||||
|
||||
/** 十六进制大写 */
|
||||
public static char[] HEX_DIGITS_UPPER = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
/**
|
||||
* 字节数据转 16 进制字符串
|
||||
*
|
||||
* @param bytes 字节数据
|
||||
* @return 16 进制字符串
|
||||
*/
|
||||
public static String byteToHex(byte[] bytes) {
|
||||
final int l = bytes.length;
|
||||
final char[] c = new char[l << 1];
|
||||
for (int i = 0, j = 0; i < l; i++) {
|
||||
c[j++] = Text.HEX_DIGITS_LOWER[(0xF0 & bytes[i]) >>> 4];
|
||||
c[j++] = Text.HEX_DIGITS_LOWER[0x0F & bytes[i]];
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 16 进制字符串转字节数据
|
||||
*
|
||||
* @param hex 16 进制字符串
|
||||
* @return 字节数据
|
||||
* @throws UnsupportedEncodingException 不支持的编码
|
||||
*/
|
||||
public static byte[] hexToByte(String hex) throws UnsupportedEncodingException {
|
||||
final char[] c = hex.toCharArray();
|
||||
final byte[] b = new byte[c.length >> 1];
|
||||
|
||||
final int len = c.length;
|
||||
if ((len & 0x01) != 0) {
|
||||
throw new UnsupportedEncodingException("Odd number of characters.");
|
||||
}
|
||||
|
||||
final int outLen = len >> 1;
|
||||
if (c.length < outLen) {
|
||||
throw new UnsupportedEncodingException("Output array is not large enough to accommodate decoded data.");
|
||||
}
|
||||
for (int i = 0, j = 0; j < len; i++) {
|
||||
int f = toDigit(c[j], j) << 4;
|
||||
j++;
|
||||
f = f | toDigit(c[j], j);
|
||||
j++;
|
||||
b[i] = (byte) (f & 0xFF);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
private static int toDigit(final char ch, final int index) throws UnsupportedEncodingException {
|
||||
final int digit = Character.digit(ch, 16);
|
||||
if (digit == -1) {
|
||||
throw new UnsupportedEncodingException("Illegal hexadecimal character " + ch + " at index " + index);
|
||||
}
|
||||
return digit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为半角字符
|
||||
*
|
||||
* @param c 字符
|
||||
* @return 为 true 是表示是半角字符
|
||||
*/
|
||||
public static boolean isHalfChar(char c) {
|
||||
return (int) c < 129;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串加双引号
|
||||
*
|
||||
* @param text 字符串内容
|
||||
* @return 结果
|
||||
*/
|
||||
public static String quote(String text) {
|
||||
return '"' + text + '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* 前补零(最终长度 2 字符)
|
||||
*
|
||||
* @param number 数值
|
||||
* @return 补零字符串
|
||||
*/
|
||||
public static String zero(Number number) {
|
||||
return zero(2, number);
|
||||
}
|
||||
|
||||
public static String paddedSpaceStart(String str, int totalWidth) {
|
||||
return String.format("%" + totalWidth + "s", str);
|
||||
}
|
||||
|
||||
public static String paddedSpaceEnd(String str, int totalWidth) {
|
||||
return String.format("%-" + totalWidth + "s", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 前补零
|
||||
*
|
||||
* @param l 最终长度
|
||||
* @param number 数值
|
||||
* @return 补零字符串
|
||||
*/
|
||||
public static String zero(int l, Number number) {
|
||||
return String.format("%0" + l + "d", number);
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则表达式测试
|
||||
*
|
||||
* @param reg 正则
|
||||
* @param value 文本
|
||||
* @return true 为匹配
|
||||
*/
|
||||
public static boolean testReg(String reg, String value) {
|
||||
return Pattern.compile(reg).matcher(value).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线
|
||||
*
|
||||
* @param camelCaseStr 驼峰字符串
|
||||
* @return 下划线字符串
|
||||
*/
|
||||
public static String camelCase2underscore(String camelCaseStr) {
|
||||
return camelCaseStr.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线转驼峰
|
||||
*
|
||||
* @param underscoreName 下划线字符串
|
||||
* @return 驼峰字符串
|
||||
*/
|
||||
public static String underscore2camelCase(String underscoreName) {
|
||||
if (TimiJava.isEmpty(underscoreName)) {
|
||||
return underscoreName;
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean flag = false;
|
||||
for (int i = 0; i < underscoreName.length(); i++) {
|
||||
char c = underscoreName.charAt(i);
|
||||
if ('_' == c) {
|
||||
flag = true;
|
||||
} else {
|
||||
if (flag) {
|
||||
result.append(Character.toUpperCase(c));
|
||||
flag = false;
|
||||
} else {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 与多个字符串进行与比较
|
||||
*
|
||||
* @param string 比较字符串
|
||||
* @param other 其他字符串
|
||||
* @return true 时全部其他字符串和比较字符串一致
|
||||
*/
|
||||
public static boolean eqAnd(String string, String... other) {
|
||||
for (int i = 0; i < other.length; i++) {
|
||||
if (!string.equals(other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与多个字符串进行或比较
|
||||
*
|
||||
* @param string 比较字符串
|
||||
* @param other 其他字符串
|
||||
* @return true 时其他字符串存在和比较字符串一致
|
||||
*/
|
||||
public static boolean eqOr(String string, String... other) {
|
||||
for (int i = 0; i < other.length; i++) {
|
||||
if (!string.equals(other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与多个字符串进行忽略大小写的与比较
|
||||
*
|
||||
* @param string 比较字符串
|
||||
* @param other 其他字符串
|
||||
* @return true 时全部其他字符串和比较字符串一致
|
||||
*/
|
||||
public static boolean eqIgnoreCaseAnd(String string, String... other) {
|
||||
for (int i = 0; i < other.length; i++) {
|
||||
if (!string.equalsIgnoreCase(other[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与多个字符串进行忽略大小写的或比较
|
||||
*
|
||||
* @param string 比较字符串
|
||||
* @param other 其他字符串
|
||||
* @return true 时其他字符串存在和比较字符串一致
|
||||
*/
|
||||
public static boolean eqIgnoreCaseOr(String string, String... other) {
|
||||
for (int i = 0; i < other.length; i++) {
|
||||
if (string.equalsIgnoreCase(other[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与多个字符串进行忽略大小写包含关系
|
||||
*
|
||||
* @param string 原字符串
|
||||
* @param other 其他字符串
|
||||
* @return true 为 string 中至少含有一个 other 的忽略大小写的字符段
|
||||
*/
|
||||
public static boolean containsIgnoreCase(String string, String... other) {
|
||||
String stringUpper = string.toUpperCase();
|
||||
String stringLower = string.toLowerCase();
|
||||
for (int i = 0; i < other.length; i++) {
|
||||
if (stringLower.contains(other[i].toLowerCase()) || stringUpper.contains(other[i].toUpperCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String randomString(int length) {
|
||||
return randomString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", length);
|
||||
}
|
||||
|
||||
public static String randomString(String pool, int length) {
|
||||
SecureRandom r = new SecureRandom();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(pool.charAt(r.nextInt(pool.length() - 1)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 较短的临时 UUID,如果使用在庞大的数据里,很可能会发生重复
|
||||
*
|
||||
* @return 完整 UUID 前 8 位
|
||||
*/
|
||||
public static String tempUUID() {
|
||||
return UUID.randomUUID().toString().substring(0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算字符串相似度(编辑距离算法)
|
||||
*
|
||||
* @param source 需比较的字符串
|
||||
* @param target 被比较的字符串
|
||||
* @param isIgnore 为 true 时忽略大小写
|
||||
* @return 相似度 [0, 1]
|
||||
*/
|
||||
private static float levenshteinDistance(String source, String target, boolean isIgnore) {
|
||||
int[][] d;
|
||||
int n = source.length(), m = target.length(), i, j, temp;
|
||||
char charS, charT;
|
||||
|
||||
if (n == 0) {
|
||||
return m;
|
||||
}
|
||||
if (m == 0) {
|
||||
return n;
|
||||
}
|
||||
|
||||
d = new int[n + 1][m + 1];
|
||||
for (i = 0; i <= n; i++) {
|
||||
d[i][0] = i;
|
||||
}
|
||||
for (j = 0; j <= m; j++) {
|
||||
d[0][j] = j;
|
||||
}
|
||||
for (i = 1; i <= n; i++) {
|
||||
charS = source.charAt(i - 1);
|
||||
for (j = 1; j <= m; j++) {
|
||||
charT = target.charAt(j - 1);
|
||||
if (isIgnore) {
|
||||
if (charS == charT || charS == charT + 32 || charS + 32 == charT) {
|
||||
temp = 0;
|
||||
} else {
|
||||
temp = 1;
|
||||
}
|
||||
} else {
|
||||
if (charS == charT) {
|
||||
temp = 0;
|
||||
} else {
|
||||
temp = 1;
|
||||
}
|
||||
}
|
||||
d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + temp);
|
||||
}
|
||||
}
|
||||
return d[n][m];
|
||||
}
|
||||
|
||||
/**
|
||||
* 三数求最小
|
||||
*
|
||||
* @param one 值一
|
||||
* @param two 值二
|
||||
* @param three 值三
|
||||
* @return 最小值
|
||||
*/
|
||||
public static int min(int one, int two, int three) {
|
||||
return (one = Math.min(one, two)) < three ? one : three;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量相似度比较字符串(忽略大小写),返回相似度比较列表倒叙结果,较为相似的排最前
|
||||
*
|
||||
* @param sources 需比较的字符串列表
|
||||
* @param target 被比较的字符串
|
||||
* @return 比较结果列表
|
||||
*/
|
||||
public static LinkedHashMap<String, Number> similarityRatioList(Collection<String> sources, String target) {
|
||||
return similarityRatioList(sources, target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量相似度比较字符串,返回相似度比较列表倒叙结果,较为相似的排最前
|
||||
*
|
||||
* @param sources 需比较的字符串列表
|
||||
* @param target 被比较的字符串
|
||||
* @param isIgnoreCase true 为忽略大小写
|
||||
* @return 比较结果列表
|
||||
*/
|
||||
public static LinkedHashMap<String, Number> similarityRatioList(Collection<String> sources, String target, boolean isIgnoreCase) {
|
||||
Map<String, Number> items = new HashMap<>();
|
||||
for (String source : sources) {
|
||||
items.put(source, 0);
|
||||
}
|
||||
items.replaceAll((k, v) -> similarityRatio(k, target, isIgnoreCase));
|
||||
return Collect.sortMapByNumberValueDESC(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 求字符串相似度,忽略大小写
|
||||
*
|
||||
* @param source 需比较的字符串
|
||||
* @param target 被比较的字符串
|
||||
* @return 相似度 [0, 1]
|
||||
*/
|
||||
public static float similarityRatio(String source, String target) {
|
||||
return similarityRatio(source, target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 求字符串相似度
|
||||
*
|
||||
* @param source 需比较的字符串
|
||||
* @param target 被比较的字符串
|
||||
* @param isIgnoreCase true 为忽略大小写
|
||||
* @return 相似度 [0, 1]
|
||||
*/
|
||||
public static float similarityRatio(String source, String target, boolean isIgnoreCase) {
|
||||
float ret;
|
||||
final int max = Math.max(source.length(), target.length());
|
||||
if (max == 0) {
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = 1 - levenshteinDistance(source, target, isIgnoreCase) / max;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验字符串是否为 json 数据,不校验是否有错误
|
||||
*
|
||||
* @param s 字符串
|
||||
* @return true 为是 JSON 数据
|
||||
*/
|
||||
public static boolean isJson(String s) {
|
||||
return isJsonObject(s) || isJsonArray(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验字符串是否为 json 对象,不校验是否有错误
|
||||
*
|
||||
* @param s 字符串
|
||||
* @return true 为是 JSON 对象
|
||||
*/
|
||||
public static boolean isJsonObject(String s) {
|
||||
return s.startsWith("{") && s.endsWith("}");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检验字符串是否为 json 数组,不校验是否有错误
|
||||
*
|
||||
* @param s 字符串
|
||||
* @return true 为是 JSON 数组
|
||||
*/
|
||||
public static boolean isJsonArray(String s) {
|
||||
return s.startsWith("[") && s.endsWith("]");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 字符串替换,不需要正则的情况下
|
||||
*
|
||||
* @param string 字符串
|
||||
* @param from 被替换字符
|
||||
* @param to 替换字符串
|
||||
* @return 替换结果
|
||||
*/
|
||||
public static String replaceAll(String string, char from, String to) {
|
||||
return replaceAll(new StringBuilder(string), from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串替换,不需要正则的情况下
|
||||
*
|
||||
* @param sb 字符构造器
|
||||
* @param from 被替换字符
|
||||
* @param to 替换字符串
|
||||
* @return 替换结果
|
||||
*/
|
||||
public static String replaceAll(StringBuilder sb, char from, String to) {
|
||||
for (int i = 0; i < sb.length(); i++) {
|
||||
if (sb.charAt(i) == from) {
|
||||
sb.replace(i, ++i, to);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
417
src/main/java/com/imyeyu/utils/Time.java
Normal file
417
src/main/java/com/imyeyu/utils/Time.java
Normal file
@ -0,0 +1,417 @@
|
||||
package com.imyeyu.utils;
|
||||
|
||||
import com.imyeyu.java.TimiJava;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 时间转换相关
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2021-06-10 20:07
|
||||
*/
|
||||
public class Time {
|
||||
|
||||
// ---------- 格式化 ----------
|
||||
|
||||
/** 格式化 yy */
|
||||
public static final SimpleDateFormat year = new SimpleDateFormat("yy");
|
||||
|
||||
/** 格式化 yyyy */
|
||||
public static final SimpleDateFormat yearFull = new SimpleDateFormat("yyyy");
|
||||
|
||||
/** 格式化 M */
|
||||
public static final SimpleDateFormat month = new SimpleDateFormat("M");
|
||||
|
||||
/** 格式化 MM */
|
||||
public static final SimpleDateFormat monthFull = new SimpleDateFormat("MM");
|
||||
|
||||
/** 格式化 d */
|
||||
public static final SimpleDateFormat day = new SimpleDateFormat("d");
|
||||
|
||||
/** 格式化 dd */
|
||||
public static final SimpleDateFormat dayFull = new SimpleDateFormat("dd");
|
||||
|
||||
/** 格式化 HH */
|
||||
public static final SimpleDateFormat hour = new SimpleDateFormat("HH");
|
||||
|
||||
/** 格式化 mm */
|
||||
public static final SimpleDateFormat minute = new SimpleDateFormat("mm");
|
||||
|
||||
/** 格式化 ss */
|
||||
public static final SimpleDateFormat second = new SimpleDateFormat("ss");
|
||||
|
||||
/** 格式化 hh:mm */
|
||||
public static final SimpleDateFormat hhmm = new SimpleDateFormat("HH:mm");
|
||||
|
||||
/** 格式化 mm:ss */
|
||||
public static final SimpleDateFormat mmss = new SimpleDateFormat("mm:ss");
|
||||
|
||||
/** 格式化 yyyyMMdd */
|
||||
public static final SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
|
||||
|
||||
/** 格式化 HH:mm:ss.SSS */
|
||||
public static final SimpleDateFormat log = new SimpleDateFormat("[HH:mm:ss.SSS]");
|
||||
|
||||
/** 格式化 HH:mm:ss.SSS */
|
||||
public static final SimpleDateFormat longLog = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss.SSS]");
|
||||
|
||||
/** 格式化 yyyy-MM-dd */
|
||||
public static final SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
/** 格式化 HH:mm:ss */
|
||||
public static final SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
|
||||
|
||||
/** 格式化 yyyy-MM-dd HH:mm:ss */
|
||||
public static final SimpleDateFormat dateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/** 格式化 yyyy-MM-dd'T'HH:mm:ss */
|
||||
public static final SimpleDateFormat dateTimeT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
|
||||
// ---------- 长整型时间戳 ----------
|
||||
|
||||
/** 1 秒时间戳 */
|
||||
public static final long S = 1000;
|
||||
|
||||
/** 1 分钟时间戳 */
|
||||
public static final long M = S * 60;
|
||||
|
||||
/** 1 小时时间戳 */
|
||||
public static final long H = M * 60;
|
||||
|
||||
/** 1 天时间戳 */
|
||||
public static final long D = H * 24;
|
||||
|
||||
// ---------- 整型时间戳 ----------
|
||||
|
||||
/** 1 秒时间戳(整型) */
|
||||
public static final int SI = 1000;
|
||||
|
||||
/** 1 分钟时间戳(整型) */
|
||||
public static final int MI = SI * 60;
|
||||
|
||||
/** 1 小时时间戳(整型) */
|
||||
public static final int HI = MI * 60;
|
||||
|
||||
/** 1 天时间戳(整型) */
|
||||
public static final int DI = HI * 24;
|
||||
|
||||
/**
|
||||
* 计算两个时间戳精确的日期时间差
|
||||
*
|
||||
* @param begin 开始时间戳
|
||||
* @param end 结束时间戳
|
||||
* @return 时差
|
||||
*/
|
||||
public static Between between(long begin, long end) {
|
||||
if (end < begin) {
|
||||
throw new IllegalArgumentException("end time must greater than begin time:" + end + " < " + begin);
|
||||
}
|
||||
LocalDateTime ldtBegin = toLocalDateTime(begin);
|
||||
LocalDateTime ldtEnd = toLocalDateTime(end);
|
||||
|
||||
Between between = new Between();
|
||||
between.year = (int) ChronoUnit.YEARS.between(ldtBegin, ldtEnd);
|
||||
between.month = (int) ChronoUnit.MONTHS.between(ldtBegin, ldtEnd) % 12;
|
||||
between.day = (int) ChronoUnit.DAYS.between(ldtBegin.plusMonths(between.year * 12L + between.month), ldtEnd);
|
||||
between.hour = (int) (ChronoUnit.HOURS.between(ldtBegin, ldtEnd) - ChronoUnit.DAYS.between(ldtBegin, ldtEnd) * 24);
|
||||
between.minute = (int) ChronoUnit.MINUTES.between(ldtBegin, ldtEnd) % 60;
|
||||
between.second = (int) ChronoUnit.SECONDS.between(ldtBegin, ldtEnd) % 60;
|
||||
between.millis = (int) ((end - begin) % 1000);
|
||||
return between;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此刻毫秒
|
||||
*
|
||||
* @return 毫秒
|
||||
*/
|
||||
public static long now() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @return 当前时间
|
||||
*/
|
||||
public static String nowString() {
|
||||
return toDateTime(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取昨天零时时间戳
|
||||
*
|
||||
* @return 昨天零时时间戳
|
||||
*/
|
||||
public static long yesterday() {
|
||||
return today() - D;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今天零时时间戳
|
||||
*
|
||||
* @return 今天零时时间戳
|
||||
*/
|
||||
public static long today() {
|
||||
long now = now();
|
||||
final long H8 = H * 8;
|
||||
return ((now + H8 - (now + H8) % (H * 24)) - H8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取明天零时时间戳
|
||||
*
|
||||
* @return 明天零时时间戳
|
||||
*/
|
||||
public static long tomorrow() {
|
||||
return today() + D;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义为日期 yyyy-MM-dd
|
||||
*
|
||||
* @param unixTime 时间戳
|
||||
* @return 日期字符串
|
||||
*/
|
||||
public static String toDate(long unixTime) {
|
||||
return date.format(unixTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义为时间 HH:mm:ss
|
||||
*
|
||||
* @param unixTime 时间戳
|
||||
* @return 时间字符串
|
||||
*/
|
||||
public static String toTime(long unixTime) {
|
||||
return time.format(unixTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义为日期时间 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param unixTime 时间戳
|
||||
* @return 日期时间字符串
|
||||
*/
|
||||
public static String toDateTime(long unixTime) {
|
||||
return dateTime.format(unixTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义为日期时间 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param date 时间对象
|
||||
* @return 日期时间字符串
|
||||
*/
|
||||
public static String toDateTime(Date date) {
|
||||
return dateTime.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地时间对象转时间戳(本地时区)
|
||||
*
|
||||
* @param date 本地日期对象
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static Long fromLocalDate(LocalDate date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
return date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地时间对象转时间戳(本地时区)
|
||||
*
|
||||
* @param dateTime 本地日期对象
|
||||
* @return 时间戳
|
||||
*/
|
||||
public static Long fromLocalDateTime(LocalDateTime dateTime) {
|
||||
if (dateTime == null) {
|
||||
return null;
|
||||
}
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间戳转本地日期(本地时区)
|
||||
*
|
||||
* @param unixTime 时间戳
|
||||
* @return 本地日期对象
|
||||
*/
|
||||
public static LocalDateTime toLocalDateTime(Long unixTime) {
|
||||
if (unixTime == null) {
|
||||
return null;
|
||||
}
|
||||
return Instant.ofEpochMilli(unixTime).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 媒体时间操作
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2023-01-24 01:10
|
||||
*/
|
||||
public static class Media {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("(\\d*):?([0-5]\\d):([0-5]\\d)\\.?(\\d{1,3})?");
|
||||
|
||||
/**
|
||||
* 解析字符串为毫秒,匹配 999:59:59.999 格式,兼容 999:59:59
|
||||
*
|
||||
* @param time 时间字符串
|
||||
* @return 毫秒
|
||||
*/
|
||||
|
||||
public static long fromString(String time) {
|
||||
if (TimiJava.isEmpty(time)) {
|
||||
return 0;
|
||||
}
|
||||
Matcher matcher = PATTERN.matcher(time);
|
||||
if (matcher.find()) {
|
||||
long h = 0;
|
||||
if (TimiJava.isNotEmpty(matcher.group(1))) {
|
||||
h = Long.parseLong(matcher.group(1));
|
||||
}
|
||||
long m = Long.parseLong(matcher.group(2));
|
||||
long s = Long.parseLong(matcher.group(3));
|
||||
long ms = 0;
|
||||
if (TimiJava.isNotEmpty(matcher.group(4))) {
|
||||
String gms = matcher.group(4);
|
||||
ms = Long.parseLong(gms);
|
||||
if (gms.length() == 2) {
|
||||
ms *= 10;
|
||||
}
|
||||
}
|
||||
return h * Time.H + m * Time.M + s * Time.S + ms;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒转媒体时间,00:00:00
|
||||
*
|
||||
* @param second 秒
|
||||
* @return 时间字符串
|
||||
*/
|
||||
public static String toString(double second) {
|
||||
return toString((int) second);
|
||||
}
|
||||
|
||||
/**
|
||||
* 秒转媒体时间,00:00:00
|
||||
*
|
||||
* @param second 秒
|
||||
* @return 时间字符串
|
||||
*/
|
||||
public static String toString(int second) {
|
||||
int h = second / 60 / 60;
|
||||
if (0 < h) {
|
||||
return String.format("%d:%02d:%02d", h, second / 60 - h * 60, second % 60);
|
||||
} else {
|
||||
return String.format("%02d:%02d", second / 60, second % 60);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 毫秒转媒体时间,音视频播放时间,00:00:00.000
|
||||
*
|
||||
* @param millis 毫秒
|
||||
* @return 时间字符串
|
||||
*/
|
||||
public static String toString(long millis) {
|
||||
long s = millis / 1000;
|
||||
long h = s / 60 / 60;
|
||||
return String.format("%d:%02d:%02d.%03d", h, s / 60 - h * 60, s % 60, millis % 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 时差
|
||||
*
|
||||
* @author 夜雨
|
||||
* @version 2022-10-12 14:46
|
||||
*/
|
||||
public static class Between {
|
||||
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int hour;
|
||||
int minute;
|
||||
int second;
|
||||
int millis;
|
||||
|
||||
/**
|
||||
* 获取年数
|
||||
*
|
||||
* @return 年数
|
||||
*/
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月数
|
||||
*
|
||||
* @return 月数
|
||||
*/
|
||||
public int getMonth() {
|
||||
return month;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取天数
|
||||
*
|
||||
* @return 天数
|
||||
*/
|
||||
public int getDay() {
|
||||
return day;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小时
|
||||
*
|
||||
* @return 小时
|
||||
*/
|
||||
public int getHour() {
|
||||
return hour;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分钟
|
||||
*
|
||||
* @return 分钟
|
||||
*/
|
||||
public int getMinute() {
|
||||
return minute;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取秒
|
||||
*
|
||||
* @return 秒
|
||||
*/
|
||||
public int getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取毫秒
|
||||
*
|
||||
* @return 毫秒
|
||||
*/
|
||||
public int getMillis() {
|
||||
return millis;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user