자유롭게 질의 및 응답을 할 수 있는 게시판입니다. 개발자 여러분의 답변이 큰 도움이 됩니다.
- 제품설치/등록 오류 문의: 설치/등록 Q&A 이용 (제품 구매 고객 한정)
Delphi TStreamReader 한글깨짐 문제
2020.02.29 03:48
본 게시판은 개발자들이 자유롭게 질문과 답변을 공유하는 게시판입니다.
* 따라서 최대한 정중하게 질문을 올려 주세요.
* 질문을 상세히 작성해 주실 수록 좋은 답변이 올라 옵니다.
* 다른 분들도 참고할 수 있도록 결과 댓글 필수(또는 감사 댓글)
(결과 댓글을 달지 않는 경우 다음 질문에 대한 답변이 달리지 않는 불이익이 있을 수 있습니다.)
-----------------------------------------------------------------------------------------------
delphi 10.3 을 사용중입니다.
의도하는 작업은
euc-kr(정확히는 cp949)로 되어있는 도로명주소파일을 utf-8로 변환하는 기능을 만들기입니다.
오류내용은
cp949에 #13#10 라인문자로 되어있는 파일을
TStreamReader로 읽어들이면 한글이 깨지는 현상이 발생합니다.
TFileStream을 이용하여 파일 바이트를 모두읽어 TBytes를 통째로 파일로 Write하면 한글이 깨지지 않습니다
TStreamReader에서 멀티바이트 엔코딩처리에 문제가 있는것으로 추측됩니다.
테스트한여 현상을 재현하였으며
문제가 발생하는 경우와 정상인 경우 케이스를 모두 재현하였습니다.
첨부파일을 확인부탁드립니다.
첨부파일의 도로명자료는
http://www.juso.go.kr/addrlink/addressBuildDevNew.do?menu=rdnm
전체자료중 1월 자료이며 지역은 부산입니다.
감사합니다.
댓글 6
-
김원경
2020.03.02 09:56
-
IslamsWine
2020.03.02 20:34
답변감사합니다
제가 첨부로 추가한 소스에서 TEncoding.Default의 사용을 지적하는 내용으로 보입니다.
제가 TEncoding.Default로 사용한 이유는 다음과 같습니다.
- Window사용사용하는데 한국어 사용
- 샘플로 첨부한 주소파일이 euc-kr(cp949)
그러므로 TEncoding.Default를 사용한 것이고, TEncoding.GetEncoding("cp949")로 코드를 변경해도 결과는 동일합니다
이 점을 찾고해 주시기 바랍니다.
감사합니다.
-
험프리
2020.03.03 14:42
저도 확인해 봤습니다. 제가 생각하는 원인은 LReader.ReadToEnd 메소드입니다
샘플의 문제가되는 코드의 흐름은 다음과 같습니다.
1) 스트림(TFileStream)으로 파일 읽기
2) 리더(TStreamReader) 생성(위 스트림 전달)
3) 리더에서 모든 내용 문자화 - LReader.ReadToEnd
4) 위 문자열 저장 - TFile.WriteAllText
테스트 해보니 위 3)번 과정인 ReadToEnd에서 문자열이 깨지는 현상이 발생합니다.
제가 생각하는 이유는 다음과 같습니다.
ReadToEnd 메소드에서는 데이터가 있는 경우 반복해 버퍼를 채웁니다.(FillBuffer 메소드)
function TStreamReader.ReadToEnd: string;
begin
Result := '';
if (FBufferedData <> nil) and (not EndOfStream) then
begin
repeat
FillBuffer(FEncoding);
until FNoDataInStream;
Result := FBufferedData.ToString;
FBufferedData.Remove(0, FBufferedData.Length);
end;
end;
FillBuffer 메소드에서는 스트림에서 지정된 버퍼크기(FBufferSize)만큼씩 문자열로 변환합니다.
만약, 버퍼크기로 읽은 데이터의 마지막 바이트중 한글2바이트 중 1바이트만 읽어 온다면 한글이 깨져서 반환될것으로 보입니다.
FBufferSize는 스트림 생성자에서 4096으로 지정됩니다.
constructor Create(Stream: TStream; Encoding: TEncoding;
DetectBOM: Boolean = False; BufferSize: Integer = 4096); overload;
개선방안으로 한번에 모든 데이터를 읽는 LReader.ReadToEnd 대신 한줄씩 읽는 LReader.ReadLine 등을 사용하도록 코드를 변경하는 것을 권장드립니다.
이 내용이 의도한 것인지 아니면 버그인지는 저도 좀더 확인할 필요가 있을것 같습니다.
——————
답글이 도움이 되셨는지 다른 분들도 참고할 수 있도록 결과 댓글 부탁드립니다.
(결과 댓글이 없는 경우 다른 질문에 대한 답변이 달리지 않는 불이익이 있을 수 있습니다.)
-
IslamsWine
2020.03.03 20:01
답변 감사드립니다. 저도 2바이트 이상의 멀티바이트로 구성된 문자가 총 수량의 바이트가 읽히지 않고 1바이트만 읽혀서 발생한 문제로 보고 있습니다.
LReader.ReadLine을 이용해도 여전히 문자깨짐 현상이 발생하며
이 부분은 제가 샘플프로젝트로 첨부한 [한글깨짐 : TStreamReader를 이용하여 라인별로 복사] 버튼을 이용하여 재현이 가능합니다.
ReadLine을 이용한 라인별 읽기는 중요한데, 이는 큰사이즈(예를 들어 로그파일같은 경우에는 500MB이상 또는 그 이상) 를 읽어들일땐 속도도 중요하지만 그와 못지 않게 메모리 사용률도 중요하기 때문입니다.
델파이에서 의도한 작업은 아닐거라 보이며 패치가 필요한 사안으로 볼수 있다고 생각합니다.
아무튼 시간내어 주신 답변 감사합니다.
-
DarkRyu
2021.02.08 17:53
TForm5.Button3Click 에서
LLineNo := 0;
while not LReader.EndOfStream do
begin
// LWriter.WriteLine( LReader.ReadLine );
LLineNo := LLineNo + 1;
LLineStr := LReader.ReadLine;
LWriter.WriteLine( LLineStr );
end;
로 수정하고 LLineStr := LReader.ReadLine; 에 브레이크 포인트 조건을 LLineNo = 327 로 하고 테스트 해보았습니다.
TStreamReader.ReadLine 안의 FBufferedData.Remove(0, PostNewLineIndex); 에서 Trace into 하고
TStringBuilder.Remove(StartIndex, RemLength: Integer): TStringBuilder; 에서
Move(FData[StartIndex + RemLength], FData[StartIndex], (Length - (StartIndex + RemLength)) * SizeOf(Char));
에서 브레이크 포인트를 걸고 FData 를 와치 하면
Name Value
FData '2611010100|부산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|부산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?2611010100|부산광역시|중구|영주동||0|546|3|261'
입니다.
Step over 하고 FData 를 와치하면
Name Value
FData '2611010100|?산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|부산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?2611010100|부산광역시|중구|영주동||0|546|3|261'
입니다.
문제의 328 라인을 ReadLine 하기 전에 327 라인을 ReadLine 할 때 TStringBuilder.Remove 에서 FData 값을 보았을 때 이미 한글이 깨진 상태인 것을 확인할 수 있습니다.
제가 보기에도 버그 같아 보입니다.
-
happy
2021.02.08 20:58
델파이 버그네요.
10.3 뿐 아니라 10.4.1도 동일한 버그를 갖고있네요.
C#으로 하면 정상적으로 되고요.
엠바카데로 툴은 왜 이렇게 버그가 많은건지 모르겠네요
Delphi TStreamReader 한글깨짐 문제
2020.02.29 03:48
본 게시판은 개발자들이 자유롭게 질문과 답변을 공유하는 게시판입니다.
* 따라서 최대한 정중하게 질문을 올려 주세요.
* 질문을 상세히 작성해 주실 수록 좋은 답변이 올라 옵니다.
* 다른 분들도 참고할 수 있도록 결과 댓글 필수(또는 감사 댓글)
(결과 댓글을 달지 않는 경우 다음 질문에 대한 답변이 달리지 않는 불이익이 있을 수 있습니다.)
-----------------------------------------------------------------------------------------------
delphi 10.3 을 사용중입니다.
의도하는 작업은
euc-kr(정확히는 cp949)로 되어있는 도로명주소파일을 utf-8로 변환하는 기능을 만들기입니다.
오류내용은
cp949에 #13#10 라인문자로 되어있는 파일을
TStreamReader로 읽어들이면 한글이 깨지는 현상이 발생합니다.
TFileStream을 이용하여 파일 바이트를 모두읽어 TBytes를 통째로 파일로 Write하면 한글이 깨지지 않습니다
TStreamReader에서 멀티바이트 엔코딩처리에 문제가 있는것으로 추측됩니다.
테스트한여 현상을 재현하였으며
문제가 발생하는 경우와 정상인 경우 케이스를 모두 재현하였습니다.
첨부파일을 확인부탁드립니다.
첨부파일의 도로명자료는
http://www.juso.go.kr/addrlink/addressBuildDevNew.do?menu=rdnm
전체자료중 1월 자료이며 지역은 부산입니다.
감사합니다.
댓글 6
-
김원경
2020.03.02 09:56
-
IslamsWine
2020.03.02 20:34
답변감사합니다
제가 첨부로 추가한 소스에서 TEncoding.Default의 사용을 지적하는 내용으로 보입니다.
제가 TEncoding.Default로 사용한 이유는 다음과 같습니다.
- Window사용사용하는데 한국어 사용
- 샘플로 첨부한 주소파일이 euc-kr(cp949)
그러므로 TEncoding.Default를 사용한 것이고, TEncoding.GetEncoding("cp949")로 코드를 변경해도 결과는 동일합니다
이 점을 찾고해 주시기 바랍니다.
감사합니다.
-
험프리
2020.03.03 14:42
저도 확인해 봤습니다. 제가 생각하는 원인은 LReader.ReadToEnd 메소드입니다
샘플의 문제가되는 코드의 흐름은 다음과 같습니다.
1) 스트림(TFileStream)으로 파일 읽기
2) 리더(TStreamReader) 생성(위 스트림 전달)
3) 리더에서 모든 내용 문자화 - LReader.ReadToEnd
4) 위 문자열 저장 - TFile.WriteAllText
테스트 해보니 위 3)번 과정인 ReadToEnd에서 문자열이 깨지는 현상이 발생합니다.
제가 생각하는 이유는 다음과 같습니다.
ReadToEnd 메소드에서는 데이터가 있는 경우 반복해 버퍼를 채웁니다.(FillBuffer 메소드)
function TStreamReader.ReadToEnd: string;
begin
Result := '';
if (FBufferedData <> nil) and (not EndOfStream) then
begin
repeat
FillBuffer(FEncoding);
until FNoDataInStream;
Result := FBufferedData.ToString;
FBufferedData.Remove(0, FBufferedData.Length);
end;
end;
FillBuffer 메소드에서는 스트림에서 지정된 버퍼크기(FBufferSize)만큼씩 문자열로 변환합니다.
만약, 버퍼크기로 읽은 데이터의 마지막 바이트중 한글2바이트 중 1바이트만 읽어 온다면 한글이 깨져서 반환될것으로 보입니다.
FBufferSize는 스트림 생성자에서 4096으로 지정됩니다.
constructor Create(Stream: TStream; Encoding: TEncoding;DetectBOM: Boolean = False; BufferSize: Integer = 4096); overload;개선방안으로 한번에 모든 데이터를 읽는 LReader.ReadToEnd 대신 한줄씩 읽는 LReader.ReadLine 등을 사용하도록 코드를 변경하는 것을 권장드립니다.이 내용이 의도한 것인지 아니면 버그인지는 저도 좀더 확인할 필요가 있을것 같습니다.——————답글이 도움이 되셨는지 다른 분들도 참고할 수 있도록 결과 댓글 부탁드립니다.(결과 댓글이 없는 경우 다른 질문에 대한 답변이 달리지 않는 불이익이 있을 수 있습니다.) -
IslamsWine
2020.03.03 20:01
답변 감사드립니다. 저도 2바이트 이상의 멀티바이트로 구성된 문자가 총 수량의 바이트가 읽히지 않고 1바이트만 읽혀서 발생한 문제로 보고 있습니다.
LReader.ReadLine을 이용해도 여전히 문자깨짐 현상이 발생하며
이 부분은 제가 샘플프로젝트로 첨부한 [한글깨짐 : TStreamReader를 이용하여 라인별로 복사] 버튼을 이용하여 재현이 가능합니다.
ReadLine을 이용한 라인별 읽기는 중요한데, 이는 큰사이즈(예를 들어 로그파일같은 경우에는 500MB이상 또는 그 이상) 를 읽어들일땐 속도도 중요하지만 그와 못지 않게 메모리 사용률도 중요하기 때문입니다.
델파이에서 의도한 작업은 아닐거라 보이며 패치가 필요한 사안으로 볼수 있다고 생각합니다.
아무튼 시간내어 주신 답변 감사합니다.
-
DarkRyu
2021.02.08 17:53
TForm5.Button3Click 에서
LLineNo := 0;
while not LReader.EndOfStream do
begin
// LWriter.WriteLine( LReader.ReadLine );
LLineNo := LLineNo + 1;
LLineStr := LReader.ReadLine;
LWriter.WriteLine( LLineStr );
end;
로 수정하고 LLineStr := LReader.ReadLine; 에 브레이크 포인트 조건을 LLineNo = 327 로 하고 테스트 해보았습니다.
TStreamReader.ReadLine 안의 FBufferedData.Remove(0, PostNewLineIndex); 에서 Trace into 하고
TStringBuilder.Remove(StartIndex, RemLength: Integer): TStringBuilder; 에서
Move(FData[StartIndex + RemLength], FData[StartIndex], (Length - (StartIndex + RemLength)) * SizeOf(Char));
에서 브레이크 포인트를 걸고 FData 를 와치 하면
Name Value
FData '2611010100|부산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|부산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?2611010100|부산광역시|중구|영주동||0|546|3|261'
입니다.
Step over 하고 FData 를 와치하면
Name Value
FData '2611010100|?산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?|부산광역시|중구|영주동||0|546|3|261103125006|중구로|0|182|1|||2611010100105460003004891|01|2611059000|영주제1동|48923||||||미소지움|1|48923|1||'#$D#$A'2611010100|?2611010100|부산광역시|중구|영주동||0|546|3|261'
입니다.
문제의 328 라인을 ReadLine 하기 전에 327 라인을 ReadLine 할 때 TStringBuilder.Remove 에서 FData 값을 보았을 때 이미 한글이 깨진 상태인 것을 확인할 수 있습니다.
제가 보기에도 버그 같아 보입니다.
-
happy
2021.02.08 20:58
델파이 버그네요.
10.3 뿐 아니라 10.4.1도 동일한 버그를 갖고있네요.
C#으로 하면 정상적으로 되고요.
엠바카데로 툴은 왜 이렇게 버그가 많은건지 모르겠네요
TStreamReader 사용하실 경우 인코딩 방법을 지정해 주셔서 테스트해보십시오. (참고롤 c#에서도 디퐅트 인토딩이라고 지정하지 않으면 한글이 깨빈다고합니다.)
델파이 헬프에 있는 예제입니다. 참조하십시오.