Java 기반 유틸리티 클래스 모음: 맵, 압축, 인코딩, 신분증, IP 처리

맵 키 필터링 유틸리티

특정 문자열을 포함하는 키를 기준으로 맵 항목을 추출하는 기능을 제공합니다. 스트림과 람다를 활용하여 조건에 맞는 엔트리만 필터링합니다.

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;
    }
}

태그: java Utility Classes Map Filtering GZip Compression Encoding

6월 10일 00:06에 게시됨