맵 키 필터링 유틸리티
특정 문자열을 포함하는 키를 기준으로 맵 항목을 추출하는 기능을 제공합니다. 스트림과 람다를 활용하여 조건에 맞는 엔트리만 필터링합니다.
public class MapFilterUtil {
public static Map<String, Object> filterByKeyFragment(Map<String, Object> source, String fragment) {
if (source == null || fragment == null) return null;
return source.entrySet().stream()
.filter(entry -> entry.getKey().contains(fragment))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public static Optional<String> findFirstValueByKeyFragment(Map<String, String> source, String fragment) {
return source == null ? Optional.empty() : source.entrySet().stream()
.filter(e -> e.getKey().contains(fragment) && e.getValue() != null)
.map(Map.Entry::getValue)
.findFirst();
}
}
GZip 압축 및 해제 도구
문자열 데이터를 GZip 형식으로 압축하거나 압축을 해제하는 기능을 제공합니다. BASE64 인코딩과 결합해 바이너리 데이터를 안전하게 전송할 수 있습니다.
public class CompressionUtil {
private static final String CHARSET = "UTF-8";
public static byte[] compress(String input) throws IOException {
if (input == null || input.isEmpty()) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPOutputStream gos = new GZIPOutputStream(baos)) {
gos.write(input.getBytes(CHARSET));
}
return baos.toByteArray();
}
public static String decompressToString(byte[] compressedData) throws IOException {
if (compressedData == null || compressedData.length == 0) return null;
ByteArrayInputStream bais = new ByteArrayInputStream(compressedData);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPInputStream gis = new GZIPInputStream(bais)) {
byte[] buffer = new byte[1024];
int length;
while ((length = gis.read(buffer)) > 0) {
baos.write(buffer, 0, length);
}
}
return baos.toString(CHARSET);
}
}
인코딩 및 디코딩 유틸리티
여러 형태의 인코딩 작업을 지원합니다. URL, Base64, Hex 등 일반적인 변환 기능을 캡슐화하여 간편하게 사용할 수 있습니다.
public class EncodingUtils {
private static final String DEFAULT_ENCODING = "UTF-8";
private static final Base64 base64 = new Base64();
public static String urlEncode(String raw) {
try {
return URLEncoder.encode(raw, DEFAULT_ENCODING);
} catch (Exception e) {
return raw;
}
}
public static String urlDecode(String encoded) {
try {
return URLDecoder.decode(encoded, DEFAULT_ENCODING);
} catch (Exception e) {
return encoded;
}
}
public static String base64Encode(String plainText) {
try {
return base64.encodeToString(plainText.getBytes(DEFAULT_ENCODING));
} catch (Exception e) {
return "";
}
}
public static String base64Decode(String encoded) {
try {
return new String(base64.decode(encoded), DEFAULT_ENCODING);
} catch (Exception e) {
return "";
}
}
}
신분증 번호 검증 및 정보 추출
중국 주민등록번호(15/18자리)의 유효성을 검사하고 생년월일, 성별, 지역 등을 추출합니다. 대만, 홍콩 등 특수 지역도 부분적으로 지원합니다.
public class IdCardValidator {
private static final int LENGTH_18 = 18;
private static final int[] WEIGHTS = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
private static final char[] CHECK_CODES = {'1','0','X','9','8','7','6','5','4','3','2'};
public static boolean isValid(String id) {
if (id == null) return false;
String trimmed = id.trim();
return trimmed.length() == 18 ? validate18Digit(trimmed) :
trimmed.length() == 15 ? validate15Digit(trimmed) : false;
}
private static boolean validate18Digit(String id) {
String body = id.substring(0, 17);
char expected = calculateCheckCode(body);
return expected == Character.toUpperCase(id.charAt(17));
}
private static char calculateCheckCode(String body) {
int sum = 0;
for (int i = 0; i < body.length(); i++) {
sum += Character.getNumericValue(body.charAt(i)) * WEIGHTS[i];
}
return CHECK_CODES[sum % 11];
}
public static String getBirthYear(String id) {
if (id.length() == 15) {
id = upgradeTo18(id);
}
return id.substring(6, 10);
}
public static String getGender(String id) {
if (id.length() == 15) {
id = upgradeTo18(id);
}
int genderDigit = Character.getNumericValue(id.charAt(16));
return (genderDigit % 2 == 1) ? "M" : "F";
}
}
IP 주소 변환 및 범위 관리
점 표기법 IP와 정수 값 간 상호 변환, CIDR 또는 범위 기반 IP 그룹 처리, 내부 네트워크 여부 판별 등의 기능을 제공합니다.
public class IpAddressUtil {
public static long toLong(String ipStr) {
String[] parts = ipStr.split("\\.");
if (parts.length != 4) throw new IllegalArgumentException("Invalid IP format");
long result = 0;
for (String part : parts) {
result = (result << 8) + Integer.parseInt(part);
}
return result;
}
public static String toString(long ipLong) {
return String.format("%d.%d.%d.%d",
(ipLong >> 24) & 0xFF,
(ipLong >> 16) & 0xFF,
(ipLong >> 8) & 0xFF,
ipLong & 0xFF
);
}
public static boolean isPrivateNetwork(long ip) {
return (ip >= 0x0A000000L && ip <= 0x0AFFFFFFL) || // 10.x.x.x
(ip >= 0xAC100000L && ip <= 0xAC1FFFFFL) || // 172.16.x.x ~ 172.31.x.x
(ip >= 0xC0A80000L && ip <= 0xC0A8FFFFL); // 192.168.x.x
}
}
오프라인 IP 위치 조회
로컬 데이터베이스(17monipdb.dat)를 이용해 IP 주소로부터 지리적 위치 정보를 조회합니다. 네트워크 호출 없이 고속 조회가 가능합니다.
public class OfflineIpLocator {
private final byte[] database;
private final int dataLen;
public OfflineIpLocator(File dbFile) throws IOException {
this.database = Files.readAllBytes(dbFile.toPath());
this.dataLen = (int) ByteBuffer.wrap(database, 0, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
public String locate(String ip) {
try {
long ipNum = ipToLong(ip);
int indexPtr = ((int)(ipNum >> 24)) * 4;
long start = ByteBuffer.wrap(database, indexPtr + 4, 4).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;
int maxIdx = dataLen * 8 + 1024;
for (long offset = start * 8 + 1024; offset < maxIdx; offset += 8) {
long ipEnd = ByteBuffer.wrap(database, (int)offset, 4).order(ByteOrder.BIG_ENDIAN).getInt() & 0xFFFFFFFFL;
if (ipEnd >= ipNum) {
int dataPtr = ByteBuffer.wrap(database, (int)(offset + 4), 3).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFF;
int len = database[(int)(offset + 7)] & 0xFF;
byte[] regionBytes = Arrays.copyOfRange(database, dataPtr - 1024 + database.length - dataLen, dataPtr - 1024 + database.length - dataLen + len);
return new String(regionBytes, StandardCharsets.UTF_8);
}
}
} catch (Exception e) {
return "Unknown";
}
return "Not Found";
}
private long ipToLong(String ip) throws UnknownHostException {
return InetAddress.getByName(ip).getAddress()[3] & 0xFF |
(InetAddress.getByName(ip).getAddress()[2] & 0xFF) << 8 |
(InetAddress.getByName(ip).getAddress()[1] & 0xFF) << 16 |
(InetAddress.getByName(ip).getAddress()[0] & 0xFF) << 24;
}
}