Skip to content

Instantly share code, notes, and snippets.

@reikop
Created March 15, 2018 09:07
Show Gist options
  • Save reikop/ca10f842cacac9d700f7ce5871383682 to your computer and use it in GitHub Desktop.
Save reikop/ca10f842cacac9d700f7ce5871383682 to your computer and use it in GitHub Desktop.
파일유틸
package com.dataworld.common.utils;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.compress.archivers.zip.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* @author reikop 파일유틸
*/
public class FileUtils {
private static Logger logger = LoggerFactory.getLogger(FileUtils.class);
/**
* 파일 목록을 ZIP으로 압축하여 outputstream에 쓴다.
*
* @param files
* 압축할 파일 목록
* @param outputStream
* 아웃풋 스트림
* @throws Exception
*/
public static void writeZipStream(File[] files, OutputStream outputStream) throws Exception {
List<File> list = Arrays.asList(files);
writeZipStream(list, null, outputStream, false);
}
/**
* 파일 목록을 ZIP으로 압축하여 outputstream에 쓴다.
*
* @param files
* 압축할 파일 목록
* @param outputStream
* 아웃풋 스트림
* @throws Exception
*/
public static void writeZipStream(List<File> files, OutputStream outputStream) throws Exception {
writeZipStream(files, null, outputStream, false);
}
/**
* 디렉토리를 선택하여 하위의 모든 폴더 및 파일을 압축한다.
* @param file 기준 디렉토리
* @param outputStream 아웃풋스트림
*/
public static void writeZipStream(Path path, OutputStream outputStream) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(outputStream);
ZipArchiveOutputStream output = new ZipArchiveOutputStream(bos);
Files.walkFileTree(path, new Bundler(output, path));
output.close();
}
/**
* 한글 NFC처리
*/
public static String toNFC(String value) {
return Normalizer.normalize(value, Normalizer.Form.NFC);
}
/**
*
* pom.xml 에 추가 <dependency> <groupId>org.apache.commons</groupId>
* <artifactId>commons-compress</artifactId> <version>1.11</version>
* </dependency>
*
*
* 파일 목록을 ZIP으로 압축하여 outputstream에 쓴다.
*
* @param files
* 압축할 파일 목록
* @param outputStream
* 아웃풋 스트림
* @param force
* 파일이 없거나 오류날경우 무시하고 다음파일로 넘어가는지 여부
* @throws Exception
*/
public static void writeZipStream(List<File> files, List<String> filesNm, OutputStream outputStream,
boolean force) throws Exception {
ZipArchiveOutputStream zos = new ZipArchiveOutputStream(outputStream);
zos.setEncoding("UTF-8");
int size = files.size();
for (int i=0; i<size; i++) {
File file = files.get(i);
if (!file.exists()) {
if (force) {
logger.info("'" + file.getName()
+ "' is not Founded. will be ignored");
continue;
} else {
throw new FileNotFoundException();
}
}
ZipArchiveEntry entry = new ZipArchiveEntry(filesNm.get(i));
zos.putArchiveEntry(entry);
FileCopyUtils.copy(new FileInputStream(file), zos);
zos.closeArchiveEntry();
}
try {
zos.close();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
/**
* 파일 목록을 ZIP으로 압축하여 outputstream에 쓴다. <br />
* Response header를 자동으로 생성한다.
*
* @param files
* 압축할 파일목록
* @param fileName
* 압축된 파일이름
* @param request
* {@link HttpServletRequest}
* @param response
* {@link HttpServletResponse}
* @throws @throws
* Exception
*/
public static void downloadZipFile(List<File> files, List<String> filesNm, String fileName,
HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("application/octet-stream");
response.setHeader("Content-Transfer-Encoding", "binary;");
response.setHeader("pragma", "no-cache;");
response.setHeader("Expires", "-1;");
response.setHeader("Content-Disposition", getDisposition(fileName, request));
writeZipStream(files, filesNm, response.getOutputStream(), false);
}
/**
* 파일 목록을 ZIP으로 압축하여 outputstream에 쓴다. <br />
* Response header를 자동으로 생성한다.
*
* @param files
* 압축할 파일목록
* @param fileName
* 압축된 파일이름
* @param request
* {@link HttpServletRequest}
* @param response
* {@link HttpServletResponse}
* @throws Exception
*/
public static void downloadZipFile(File[] files, String fileName, HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.setContentType("application/octet-stream");
response.setHeader("Content-Transfer-Encoding", "binary;");
response.setHeader("pragma", "no-cache;");
response.setHeader("Expires", "-1;");
response.setHeader("Content-Disposition", getDisposition(fileName, request));
writeZipStream(files, response.getOutputStream());
}
/**
* <pre>
* 다국어 파일명 처리
* 브라우저별 한글 파일명을 인코딩 시켜 Content-Disposition 에 해당하는 String을 리턴한다.
* 이 메소드를 거치지 않는다면 한글이 깨질것이다..........
* 사용방법 : response.setHeader("Content-Disposition", getDisposition(fileName, request));
* </pre>
*
* @param filename
* @param browser
* @return
* @throws UnsupportedEncodingException
*/
public static String getDisposition(String filename, HttpServletRequest request) {
String header = request.getHeader("User-Agent");
String browser = "Firefox";
String dispositionPrefix = "attachment;filename=";
String encodedFilename = null;
try {
/* NFC로 한글처리 참조 : http://helloworld.naver.com/helloworld/76650 */
filename = toNFC(filename);
if (header.indexOf("MSIE") > -1) {
browser = "MSIE";
} else if (header.indexOf("Trident") > -1) { // IE11
browser = "MSIE";
} else if (header.indexOf("Chrome") > -1) {
browser = "Chrome";
} else if (header.indexOf("Opera") > -1) {
browser = "Opera";
}
if (browser.equals("MSIE")) {
encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
} else {
encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
}
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
return dispositionPrefix + encodedFilename;
}
return dispositionPrefix + encodedFilename;
}
/**
* 스토리지에 중복된 파일이름이 있을경우 파일명 뒤에[n]을 붙인다. test.xml test[0].xml .... test[999].xml
* ...
*
* @param filename
* 파일명
* @param filePath
* 파일경로
* @return 중복되지 않은 파일 경로와 정보를 담은 File 객체를 리턴한다.
*/
public static File getDeduplicationFile(String filename, String filePath) {
filePath = filePath.replaceAll("[\\/]", File.separator);
filePath = (filePath.lastIndexOf(File.separator) == (filePath.length() - 1)) ? filePath
: filePath + File.separator;
File f = new File(filePath + filename);
if (f.exists() && f.isFile()) {
String value = filename;
String name = "";
String ext = "";
String period = "";
if (value.indexOf(".") > -1) {
name = value.substring(0, value.lastIndexOf("."));
ext = value.substring(value.lastIndexOf(".") + 1, value.length());
period = ".";
} else {
name = value;
ext = "";
period = "";
}
File fileDirec = new File(filePath);
ArrayList<String> ar = new ArrayList<String>();
final String regex = name.replaceAll("[\\[\\]]", "") + ".*\\" + period + ext;
for (File fli : fileDirec.listFiles()) {
if (fli.getName().matches(regex)) {
ar.add(fli.getName());
}
}
Collections.sort(ar, new WindowsExploererStringComparator());
if (ar.size() > 0) {
value = ar.get(ar.size() - 1);
}
if (value.indexOf(".") > -1) {
name = value.substring(0, value.lastIndexOf("."));
ext = value.substring(value.lastIndexOf(".") + 1, value.length());
period = ".";
} else {
name = value;
ext = "";
period = "";
}
if (value.lastIndexOf("[") > -1) {
try {
int idx = Integer.parseInt(value.substring(value.lastIndexOf("[") + 1, value.lastIndexOf("]")));
String realname = value.substring(0, value.lastIndexOf("["));
name = realname + "[" + (idx + 1) + "]";
} catch (Exception e) {
name += "[0]";
}
} else {
name += "[0]";
}
return new File(filePath + File.separator + name + period + ext);
} else {
return f;
}
}
/**
* 파일을 저장한다. 중복파일은 무시 한다.
*
* @param file
* @param upperCase
* @param filePath
* @return
*/
public static File overwrite(MultipartFile file, String filename, String filePath) throws IOException {
filename = refineFileName(filename);
filePath = filePath.replace("/", File.separator).replace("\\", File.separator);
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
if (!dir.isDirectory()) {
throw new IOException(String.format("경로 '%s' 를 생성할 수 없습니다. 퍼미션이등을 확인해주세요", dir.getAbsolutePath()));
}
File destination = new File(filePath + File.separator + filename);
if (destination.exists() && destination.isDirectory()) {
throw new IOException(String.format("경로 '%s'에 디렉토리가 생성되어 있습니다. 파일을 생성할 수 없습니다.", dir.getAbsolutePath()));
} else if (destination.exists()) {
return destination;
} else {
FileOutputStream fos = new FileOutputStream(destination);
BufferedOutputStream bos = new BufferedOutputStream(fos);
FileCopyUtils.copy(file.getInputStream(), bos);
fos.close();
bos.close();
}
return destination;
}
public static File overwrite(InputStream file, String filename, String filePath) throws IOException {
filename = refineFileName(filename);
filePath = filePath.replace("/", File.separator).replace("\\", File.separator);
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
if (!dir.isDirectory()) {
throw new IOException(String.format("경로 '%s' 를 생성할 수 없습니다. 퍼미션이등을 확인해주세요", dir.getAbsolutePath()));
}
File destination = new File(filePath + File.separator + filename);
if (destination.exists() && destination.isDirectory()) {
throw new IOException(String.format("경로 '%s'에 디렉토리가 생성되어 있습니다. 파일을 생성할 수 없습니다.", dir.getAbsolutePath()));
} else if (destination.exists()) {
return destination;
} else {
FileOutputStream fos = new FileOutputStream(destination);
BufferedOutputStream bos = new BufferedOutputStream(fos);
FileCopyUtils.copy(file, bos);
fos.close();
bos.close();
}
return destination;
}
/**
* 파일을 저장한다.
*
* @param file
* 원본파일
* @param filename
* 저장할 경로
* @param filePath
* 저장할 경로
* @return
* @throws IOException
*/
public static File write(MultipartFile file, String filename, String filePath) throws IOException {
filename = refineFileName(filename);
filePath = filePath.replace("/", File.separator).replace("\\", File.separator);
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
if (!dir.isDirectory()) {
throw new IOException(String.format("경로 '%s' 를 생성할 수 없습니다. 퍼미션이등을 확인해주세요", dir.getAbsolutePath()));
}
File destination = getDeduplicationFile(filename, filePath);
FileOutputStream fos = new FileOutputStream(destination);
BufferedOutputStream bos = new BufferedOutputStream(fos);
FileCopyUtils.copy(file.getInputStream(), bos);
fos.close();
bos.close();
return destination;
}
/**
* 파일을 저장한다.
*
* @param file
* 원본파일
* @param destinationDirectory
* 저장할 경로
* @return
* @throws IOException
*/
public static File write(MultipartFile file, String filePath) throws IOException {
return write(file, toNFC(file.getOriginalFilename()), filePath);
}
/**
* 파일을 저장한다.
*
* @param sourceFile
* 원본파일
* @param destinationDirectory
* 저장할 경로
* @return
* @throws IOException
*/
public static File write2(MultipartFile sourceFile, String fileName, String filePath) throws IOException {
return write(sourceFile, fileName, filePath);
}
/**
* 파일을 저장한다.
*
* @param sourceFile
* @param destinationDirctory
* @return
* @throws IOException
*/
public static File[] write(MultipartFile[] sourceFile, String filePath) throws IOException {
File[] f = new File[sourceFile.length];
for (int i = 0; i < sourceFile.length; i++) {
if (sourceFile[i].getSize() != 0) {
f[i] = write(sourceFile[i], filePath);
}
}
return f;
}
/**
* 파일사이즈를 KB, GB, TB 등으로 리턴하는 함수
*
* @param bytes
* 파일크기
* @return
*/
public static String getFormatFileSize(long bytes) {
int u = 0;
for (; bytes > 1024 * 1024; bytes >>= 10) {
u++;
}
if (bytes > 1024) {
u++;
}
return String.format("%.1f %cB", bytes / 1024f, " KMGTPE".charAt(u));
}
/**
* 이미지를 리사이즈 한다.
*
* @param imageFile
* 원본 이미
* @param width
* 가로길이
* @param height
* 세로길이
* @param outputStream
* 변경된 이미지를 쓸 outputstream
* @throws IOException
*/
public static void resizeImage(File imageFile, int width, int height, OutputStream outputStream)
throws IOException {
BufferedImage bi = ImageIO.read(imageFile);
Image scaledInstance = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);
int type = bi.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bi.getType();
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(scaledInstance, 0, 0, width, height, null);
g.dispose();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ImageIO.write(resizedImage, "gif", outputStream);
outputStream.close();
}
/**
* @param imageFile
* @param width
* @param height
* @param outputStream
* @throws IOException
*/
public static void resizeImage(File imageFile, int width, OutputStream outputStream) throws IOException {
int[] size = getImageStrechSize(imageFile);
double ratio = size[0] / size[1];
BufferedImage bi = ImageIO.read(imageFile);
int height = (int) (width / ratio);
Image scaledInstance = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);
int type = bi.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bi.getType();
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(scaledInstance, 0, 0, width, height, null);
g.dispose();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ImageIO.write(resizedImage, "PNG", outputStream);
outputStream.close();
}
public static void resizeImage(InputStream imageFile, int width, OutputStream outputStream, double ratio)
throws IOException {
imageFile.mark(0);
imageFile.reset();
BufferedImage bi = ImageIO.read(imageFile);
int height = (int) (width / ratio);
Image scaledInstance = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH);
int type = bi.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bi.getType();
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(scaledInstance, 0, 0, width, height, null);
g.dispose();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ImageIO.write(resizedImage, "PNG", outputStream);
outputStream.close();
}
/**
* 이미지 가로/세로사이즈를 구한다.
*
* @param f
* @return int[width,height]
*/
public static int[] getImageStrechSize(File f) {
BufferedImage img;
try {
img = ImageIO.read(f);
int[] strech = new int[2];
strech[0] = img.getWidth();
strech[1] = img.getHeight();
return strech;
} catch (IOException e) {
return null;
}
}
public static int[] getImageStrechSize(InputStream f) {
BufferedImage img;
try {
img = ImageIO.read(f);
int[] strech = new int[2];
strech[0] = img.getWidth();
strech[1] = img.getHeight();
return strech;
} catch (IOException e) {
return null;
}
}
//
// /**
// * 동영상의 가로/세로사이즈를 구한다.
// *
// * @PARAM 가져올 파일
// * @RETURN INT[WIDTH,HEIGHT]
// */
// public static int[] getVideoStrechSize(File f){
// Encoder encoder = new Encoder();
// int strech[] = new int[2];
// try {
// MultimediaInfo info = encoder.getInfo(f);
// strech[0] = info.getVideo().getSize().getWidth();
// strech[1] = info.getVideo().getSize().getHeight();
// } catch (Exception e) {
// strech[0] = 0;
// strech[1] = 0;
// return strech;
// }
// return strech;
// }
/**
* 오디오파일의 재생시간을 구한다.
*
* @param f
* @return 초
*/
public static int getAudioLength(File f) {
try {
AudioFormat fmt = AudioSystem.getAudioFileFormat(f).getFormat();
long frames = fmt.getFrameSize();
double durationInSeconds = (frames + 0.0) / fmt.getFrameRate();
return (int) durationInSeconds;
} catch (Exception e) {
return 0;
}
}
/**
* 특정 폴더안의 모든 폴더(재귀)에 있는 내용의 파일들을 정규식으로 배열로 가지고 온다.
*
* @param pattern
* 정규식
* @param filePath
* 파일경로
* @return
*/
public static ArrayList<File> findFolderInRegex(String pattern, String filePath) {
return findFolderInRegex(pattern, filePath, true);
}
/**
* 특정 폴더안에 있는 파일들을 정규식으로 배열로 가지고 온다.
*
* @param pattern
* 정규식
* @param filePath
* 파일 경로
* @return
*/
public static ArrayList<File> findFolderInRegex(String pattern, String filePath, boolean findInnerFolder) {
File dir = new File(filePath);
ArrayList<File> pathList = new ArrayList<File>();
File[] files = dir.listFiles();
Pattern p = Pattern.compile(pattern);
if (files == null && !dir.isDirectory()) {
return null;
}
for (File file : files) {
if (file.isFile()) {
Matcher m = p.matcher(file.getName());
if (m.find()) {
try {
pathList.add(new File(file.getCanonicalPath()));
} catch (IOException e) {
}
}
} else if (file.isDirectory() && findInnerFolder) {
try {
pathList.addAll(findFolderInRegex(pattern, file.getCanonicalPath().toString(), findInnerFolder));
} catch (Exception e) {
}
}
}
return pathList;
}
public static String refineFileName(String value) {
try {
value = URLDecoder.decode(value, "UTF-8");
} catch (UnsupportedEncodingException e) {
value = URLDecoder.decode(value);
}
// 특수문자 제거
// value = value.replaceAll("[^a-zA-Z0-9ㄱ-힣\\.\\-\\_]", "");
/* NFC로 한글처리 참조 : http://helloworld.naver.com/helloworld/76650 */
value = Normalizer.normalize(value, Normalizer.Form.NFC);
return value;
}
public static String md5Checksum(InputStream fis) {
try {
String hex = DigestUtils.md5Hex(fis);
fis.close();
return hex;
} catch (IOException e) {
return null;
}
}
public static String md5Checksum(File file) {
if (file != null && file.exists() && !file.isDirectory() && file.length() > 0) {
try {
FileInputStream fis = new FileInputStream(file);
return md5Checksum(fis);
} catch (FileNotFoundException e) {
return null;
}
}
return null;
}
/**
* 파일에 대해 섬네일을 생성한다.
*
* @param file
* @param string
* @return
* @throws IOException
* @throws FileNotFoundException
*/
public static File thumbnail(File file, String path) throws FileNotFoundException, IOException {
File output = new File(path);
output.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(output);
BufferedOutputStream bos = new BufferedOutputStream(fos);
resizeImage(file, 200, bos);
return output;
}
public static FileOutputStream getOutputStream(String path, String filename) {
try {
mkdir(path);
File file = new File(concat(path, filename));
return new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String concat(String filepath, String filename){
return filepath.replaceAll("/$", "") + File.separator +
filename.replaceAll("^/", "");
}
public static File mkdir(String path) throws IOException {
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
if(!dir.isDirectory()) {
throw new IOException(String.format("경로 '%s' 를 생성할 수 없습니다. 퍼미션이등을 확인해주세요", dir.getAbsolutePath()));
}
return dir;
}
// /**
// * 바이트를 Hex값으로 변환한다.
// *
// * @param hash
// * @return
// */
// private static String byteArray2Hex(byte[] hash) {
// Formatter formatter = new Formatter();
// for (byte b : hash) {
// formatter.format("%02x", b);
// }
// String s = formatter.toString();
// formatter.close();
// return s;
// }
}
class Bundler extends SimpleFileVisitor<Path> {
private ZipArchiveOutputStream out;
private Path root;
private Path prefix;
public Bundler(ZipArchiveOutputStream outputStream, Path root) {
this.out = outputStream;
this.root = root;
}
private String getEntryName(Path path) {
return root.relativize(path).toString();
}
private InputStream getInputStream(Path path) throws FileNotFoundException {
FileInputStream fin = new FileInputStream(path.toFile());
return new BufferedInputStream(fin);
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String entryName = getEntryName(dir);
if (!entryName.equals("")) {
ZipArchiveEntry entry = new ZipArchiveEntry(dir.toFile(), entryName);
out.putArchiveEntry(entry);
out.closeArchiveEntry();
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String entryName = getEntryName(file);
ZipArchiveEntry entry = new ZipArchiveEntry(file.toFile(), entryName);
out.putArchiveEntry(entry);
IOUtils.copy(getInputStream(file), out);
out.closeArchiveEntry();
return FileVisitResult.CONTINUE;
}
}
/**
* 윈도우방식으로 파일 정렬을 해준다. 자바 기본은 [1, 10, 2, 3] 윈도우방식은 [1, 2, 3, 10]
*
* @author reikop
*/
class WindowsExploererStringComparator implements Comparator<String> {
private String str1, str2;
private int pos1, pos2, len1, len2;
public int compare(String s1, String s2) {
str1 = s1;
str2 = s2;
len1 = str1.length();
len2 = str2.length();
pos1 = pos2 = 0;
int result = 0;
while (result == 0 && pos1 < len1 && pos2 < len2) {
char c1 = str1.charAt(pos1);
char c2 = str2.charAt(pos2);
if (Character.isDigit(c1)) {
result = Character.isDigit(c2) ? compareNumbers() : -1;
} else if (Character.isLetter(c1)) {
result = Character.isLetter(c2) ? compareOther(true) : 1;
} else {
result = Character.isDigit(c2) ? 1 : Character.isLetter(c2) ? -1 : compareOther(false);
}
pos1++;
pos2++;
}
return result == 0 ? len1 - len2 : result;
}
private int compareNumbers() {
int end1 = pos1 + 1;
while (end1 < len1 && Character.isDigit(str1.charAt(end1))) {
end1++;
}
int fullLen1 = end1 - pos1;
while (pos1 < end1 && str1.charAt(pos1) == '0') {
pos1++;
}
int end2 = pos2 + 1;
while (end2 < len2 && Character.isDigit(str2.charAt(end2))) {
end2++;
}
int fullLen2 = end2 - pos2;
while (pos2 < end2 && str2.charAt(pos2) == '0') {
pos2++;
}
int delta = (end1 - pos1) - (end2 - pos2);
if (delta != 0) {
return delta;
}
while (pos1 < end1 && pos2 < end2) {
delta = str1.charAt(pos1++) - str2.charAt(pos2++);
if (delta != 0) {
return delta;
}
}
pos1--;
pos2--;
return fullLen2 - fullLen1;
}
private int compareOther(boolean isLetters) {
char c1 = str1.charAt(pos1);
char c2 = str2.charAt(pos2);
if (c1 == c2) {
return 0;
}
if (isLetters) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
}
}
return c1 - c2;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment