Translate

2024년 1월 30일 화요일

[Java] jxl 라이브러리 팁


Laptop
운영체제 Windows 10 Pro 64bit
개발환경
JDK1.7.0_80

현 시점에서는 상당히 오래전에 쓰던 엑셀 라이브러리지만,
유지보수 프로젝트 참여 중 기능을 추가해보면서 알게 된 내용을 정리해본다.

일단 기본적인 개념으로, jxl은 읽기 객체와 쓰기 객체가 따로 존재한다.
예를 들어 아래와 같은 소스가 있을 때,

Workbook originExcel = Workbook.getWorkbook(path);
WritableWorkbook newExcel = Workbook.createWorkbook(file, originExcel);

Workbook 클래스로 생성된 originExcel는 읽기 전용 객체가 되고,
.getSheet, .getCell 메소드로 얻는 Sheet, Cell 객체들 또한 읽기 전용 객체가 되며, 즉 원본 파일(path)에 대해 엑셀 객체를 생성하여 내용을 읽을 수는 있지만 직접적으로 파일에 영향을 줄 수는 없다는 뜻이다.

그러나 WritableWorkbook 클래스로 생성된 newExcel은 쓰기 가능 객체로,
.getSheet, .getWritableCell 메소드를 통해 WritableSheet, WritableCell 라는 셀의 추가나 수정 등을 할 수 있는, 즉 파일에 영향을 줄 수 있는 객체를 얻을 수 있게 된다.

정리하면 위 상태는 원본 파일(String path)로부터 엑셀 객체를 생성한 뒤,
그것의 복사 파일(File file)을 만들어 수정 가능 상태로 만들어놓은 설정이라고 할 수 있다.

그럼 이제 원본 파일의 경우 아무 내용도 없는 템플릿으로 만들어두고,
복사 파일에서는 프로그래밍적으로 값을 입력하면 될텐데, 여기서 치명적인 문제가 하나 있다.
셀의 스타일 등을 유지한 채 값만 바꾸는, 즉 비교적 최신 엑셀 라이브러리인 poi 라이브러리의 setCellValue에 해당하는 기능이 jxl에는 없다.
WritableCell 객체에도 setCellFormat과 같이 속성을 바꾸는 메소드는 있지만 값을 바꾸는 메소드가 없다.

WritableCellFormat를 생성해서 일일히 설정하여 셀을 생성하는 것도 방법이지만,
그렇게 되면 굳이 앞에서 복사본을 생성한 이유가 없어진다.
그래서 셀을 생성하되, 원래 셀의 속성을 가져와 사용하는 방식을 사용하면 된다.

WritableSheet sheet = newExcel.getSheet(0);
for (int r = 0; r < sheet.getRows(); r++) {
	for (int c = 0; c < sheet.getColumns(); c++) {
		CellFormat format = sheet.getCell(c, r).getCellFormat();
		Label cell = new Label(c, r, "값", format);
		sheet.addCell(cell);
	}
}

이렇게 만들어진 셀에서는 cell.setString("값2"); 를 통해 값 수정도 가능하다.


예시로 Label을 썼는데, 이것은 문자열 값을 받는 셀 객체를 의미하기 때문에
숫자에 쉼표를 넣는 등의 설정이 필요할 경우 java에서 구현해서 넣어야 하는 비효율적인 문제가 발생한다.

좀 더 괜찮은 방법으로는 원본 엑셀에 해당하는 부분은 셀 서식에서 숫자를 설정해준 뒤,  
위 소스에서 Label 부분을
Number cell = new Number(c, r, 100, format);
이렇게 전환하면 숫자 설정을 유지할 수 있게 된다.


또한 수식을 사용하고 싶으면 Formula 객체를 이용하면 되는데,
주의사항은 jxl에서 사용하는 index의 개념은 0부터 시작하지만,
실제 엑셀 파일에서 사용되는 값은 행은 1부터 시작되는 순번, 열은 알파벳으로 인식되기 때문에
이 부분에 주의해서 수식을 입력해야 한다.

아래는 getColNm 메소드를 통해 index를 알파벳으로 치환하여,
c가 0이라는 전제에서 =SUM(A1:A10) 수식 셀을 생성하는 예시이다.

int start = 0;
int end = 9;
String nm = getColNm(c);
Formula cell = new Formula(c, r, "SUM(" + nm + (start + 1) + ":" + nm + (end + 1) + ")", format);

...

private String getColNm(int columnNumber) {
	StringBuilder columnName = new StringBuilder();
	columnNumber++;
	while (columnNumber > 0) {
		int rem = columnNumber % 26;
		if (rem == 0) {
			columnName.insert(0, 'Z');
			columnNumber = (columnNumber / 26) - 1;
		} else {
			columnName.insert(0, (char) ((rem - 1) + 'A'));
			columnNumber = columnNumber / 26;
		}
	}
	return columnName.toString();
}

추가로 jxl-2.6.12 버전에서 SUM와 같은 엑셀 함수 사용 시
Can't find bundle for base name functions ...
와 같은 오류를 발생시키는 경우가 있는데,
이는 특정 시기에 배포 오류가 있었던건지 jxl 파일 내에 관련 파일이 없는 경우가 있다.
기존에 구축된 프로젝트라 버전이나 라이브러리를 바꾸는데 부담이 있다면, 
새로운 버전을 jar로 임시로 받아 압축 프로그램을 사용하여 functions.properties 파일만 추출해서 기존 파일에 넣으면 정상적으로 동작한다.
(이름이 비슷한 다른 파일들은 언어별 파일인것 같은데, 맞는 언어가 없어도 자동적으로 기본 파일만 참조하는 것 같다.)


Formula로 생성한 셀의 수식은 cell.getContents()로 조회 가능하나
수식 결과로 나온 값은 java에서는 조회가 불가능하며,
읽기 전용인 원본 파일에서 수식이 입력되어있던 셀은 수식, 결과 모두 조회 불가능하다.

마지막으로 아무 값도 없이 속성만 복사한 셀을 생성할 때는
Blank cell = new Blank(c, r, format);
이렇게 하면 된다.
(원본 파일에 있던 셀인 경우 겉보기에는 굳이 java에서 건드리는 의미는 없으나, 엑셀 함수 사용 시 범위에 포함된 셀을 Blank로 생성했을 경우 0으로 인식되지만 그대로 둔 셀은 값 오류가 발생하는 경우가 있었다.)

참고로 Label, Number, Formula, Blank 모두 WritableCell 인터페이스의 구현 클래스들이므로
위의 예시들에서 WritableCell cell = ... 로 선언해도 상관없으나,
setString(Number의 경우 setValue)과 같이 나중에 값을 수정할 수 없다는 단점은 있으나
셀 타입마다 일일히 다른 cell 객체를 생성하지 않아도 된다는 장점이 있다.

2024년 1월 26일 금요일

[Python] Window에서 Python 설치한 상태로 pip 없을 때 설치법


Laptop
운영체제 Windows 10 Pro 64bit


Python은 설치했으나 어떠한 이유로 pip이 설치되지 않았을 때,
즉 Python설치경로/Scripts 안에 pip.exe가 존재하지 않을 때 cmd에서 아래의 커맨드를 입력하면 설치가 된다.

> py -m ensurepip --default-pip

2024년 1월 24일 수요일

[Eclipse] SVN, GIT에서 공백 자동 생성되는 현상 해결 정리



  


주로 다른 개발자가 만든 소스를 전달받았을 때, 
위와 같이 육안으로 보이지 않는 공백이 버전관리 툴에서 잡히는 경우들과 해결책을 정리해보았다.


1) UTF-8 BOM

파일 앞부분에 특정 문자를 넣는 방식이라 첫번째 줄에서 차이점이 발견되며, 이클립스 상에서 파일 우측메뉴 Properties - Resource 에서 확인할 수 있다.


.java 파일일 경우 빌드 시 illegal character: '\ufeff' 라는 오류를 발생시키기도 한다.

해결 방법은 VSCode, Notepad++ 등 인코딩 전환을 지원하는 에디터를 통해 다시 저장해주면 된다.

- Notepad++
상단 인코딩 메뉴에서 변경 후 저장(Ctrl + S)




- VSCode
1. 하단 인코딩 표시 영역 클릭


2. 커맨드 창에서 Save with Encoding 클릭



3. UTF-8 클릭



2) GIT autocrlf

윈도우와 리눅스 간 개행문자의 차이로 발생하는 문제로, 
이를 자동으로 전환해주는 옵션으로 이클립스 Window - Preferences 에서 설정 가능하다.

개발자들이 모두 윈도우에서만 작업하는 경우 true, false 모두 문제가 있지는 않지만
설정이 다 제각각인 상태에서는 전환이 발생하여 차이점으로 인식될 수 있으므로
심플하게 false로 통일하거나 각각의 상황에 맞추어 변경하면 될 것 같다. 



3) AnyEdit 설정

이클립스 확장 프로그램 중 다양한 기능을 제공하는 AnyEdit이라는 플러그인을 설치하면,
Remove trailing whitespace라는 저장할 때 의미없는 공백을 제거하는 옵션이 기본적으로 적용되도록 설정되어 있어 의도치 않은 수정이 생기는 현상이 발생할 수 있다.
Window - Preferences 에서 해제하면 된다.





- 참고 사이트: