바이트 배열을 문자열로 변환 (Java)


85

Google App Engine에서 웹 애플리케이션을 작성 중입니다. 기본적으로 .htmlblobstore에 파일로 저장되는 html 코드를 편집 할 수 있습니다 .

fetchData를 사용 byte[]하여 파일의 모든 문자 를 반환 합니다. 사용자가 html 코드를 편집 할 수 있도록 html로 인쇄하려고합니다. 모든 것이 잘 작동합니다!

지금 내 유일한 문제는 다음과 같습니다.

바이트 배열은 문자열로 다시 변환 할 때 몇 가지 문제가 있습니다. 똑똑한 따옴표와 몇 개의 문자가 펑키하게 나오고 있습니다. (? 's 또는 일본어 기호 등) 특히 문제를 일으키는 음수 값이있는 몇 바이트입니다.

스마트 따옴표로 돌아오고있다 -108-109바이트 배열을. 이것이 왜이며 올바른 문자 인코딩을 표시하기 위해 음의 바이트를 어떻게 디코딩 할 수 있습니까?



안녕, 나는 그것이 정말 오래된 게시물이라는 것을 알고 있지만 비슷한 문제에 직면하고 있습니다. SSL에 대한 man-in-the-middle 프록시를 만들고 있습니다. 내가 직면 한 문제는 당신과 동일합니다. 나는 소켓을 듣고 데이터 InputStreambyte[]. 이제 byte[]문자열 로 변환하려고 할 때 (공격을 위해 응답 본문을 사용해야 함) 스마트 따옴표와 물음표로 가득 찬 정말 재미있는 문자가 표시됩니다. 나는 당신의 문제는 우리 모두가 다루고있는대로 내와 동일합니다 생각 html에서 byte[]. 조언 해 주시겠습니까?
Parul S

그건 그렇고, 나는 Sytem.properties를 사용하여 내 시스템의 인코딩을 찾을 정도로 갔고 그것이 "Cp1252"인 것을 발견했습니다. 이제 사용 String str=new String(buffer, "Cp1252");했지만 도움이되지 않았습니다.
Parul S

답변:


141

바이트 배열에는 특수 인코딩 (알아야 할) 문자가 포함됩니다. 문자열로 변환하는 방법은 다음과 같습니다.

String decoded = new String(bytes, "UTF-8");  // example for one encoding type

그런데-원시 바이트는 Java 데이터 유형 byte이 서명 되었기 때문에 음수 십진수로 나타날 수 있으며 -128에서 127 사이의 범위를 포함합니다.


-109 = 0x93: Control Code "Set Transmit State"

값 (-109)은 유니 코드의 인쇄 할 수없는 제어 문자입니다. 따라서 UTF-8은 해당 문자 스트림에 대한 올바른 인코딩이 아닙니다.

0x93"Windows-1252"에서 찾고있는 "똑똑한 따옴표"는 해당 인코딩의 Java 이름이 "Cp1252"입니다. 다음 줄은 테스트 코드를 제공합니다.

System.out.println(new String(new byte[]{-109}, "Cp1252")); 

5
UTF-8을 사용해 보았지만 여전히?로 나왔습니다. 음수 값에 대한 매핑을 찾지 못하는 이유는 무엇입니까?
Josh

0x93은 UTF-8에서 유효한 연속 바이트입니다.하지만 해당 바이트의 존재는 처음 두 비트가 설정된 바이트 뒤에 오지 않는 경우에만 UTF-8임을 배제합니다.
Nick Johnson

1
@Josh Andreas는 Java의 byte데이터 유형이 서명 되었기 때문에 이유를 설명합니다 . '음수'값은 최상위 바이트 세트가있는 바이트입니다. 그는 또한 당신이 사용해야 할 가장 가능성이 높은 문자 집합이 무엇인지 설명합니다-Windows-1252. 하지만 추측 할 필요없이 컨텍스트 또는 규칙에서 사용할 문자 세트를 알아야합니다.
Nick Johnson

25

Java 7 이상

원하는 인코딩을 StandardCharsetsStringCharset상수 로 생성자에 전달할 수도 있습니다 . 다른 답변에서 제안한 것처럼 인코딩을으로 전달하는 것보다 안전 할 수 있습니다 .String

예를 들어 UTF-8 인코딩의 경우

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);

1
이것은 2011 년 답변의 반복입니다. -1
james.garriss

2
@ james.garriss Java 7에 도입 된 새로운 생성자를 언급하는 한, 인코딩이 상수로 전달되도록 허용하는 한, 내 생각에는 이전 API보다 더 좋고 안전하다고 생각합니다. 인코딩이 문자열로 전달 된 이전 답변에서 언급했습니다.
davnicwil 2015 년


5
public class Main {

    /**
     * Example method for converting a byte to a String.
     */
    public void convertByteToString() {

        byte b = 65;

        //Using the static toString method of the Byte class
        System.out.println(Byte.toString(b));

        //Using simple concatenation with an empty String
        System.out.println(b + "");

        //Creating a byte array and passing it to the String constructor
        System.out.println(new String(new byte[] {b}));

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Main().convertByteToString();
    }
}

산출

65
65
A

5
public static String readFile(String fn)   throws IOException 
{
    File f = new File(fn);

    byte[] buffer = new byte[(int)f.length()];
    FileInputStream is = new FileInputStream(fn);
    is.read(buffer);
    is.close();

    return  new String(buffer, "UTF-8"); // use desired encoding
}

3
이 코드는 read예외 가 발생하면 리소스를 유출합니다 .
Raedwald 2015

4

나는 제안한다 Arrays.toString(byte_array);

그것은 당신의 목적에 달려 있습니다. 예를 들어, 다음과 같은 디버그시 볼 수있는 형식과 똑같은 바이트 배열을 저장하고 싶었습니다 [1, 2, 3]. 바이트를 문자 형식으로 변환하지 않고 정확히 동일한 값을 저장하려면 Arrays.toString (byte_array)이렇게하십시오. 그러나 바이트 대신 문자를 저장하려면 String s = new String(byte_array). 이 경우 문자 형식 s과 동일합니다 [1, 2, 3].


이것을 제안하는 이유에 대해 더 많은 정보를 제공 할 수 있습니까? (문제가 해결 될까요? 문제가 해결되는 이유를 말씀해 주시겠습니까?) 감사합니다!
Dean J

그것은 당신의 목적에 달려 있습니다. 예를 들어 다음과 같이 디버그시 볼 수있는 형식과 똑같은 바이트 배열을 저장하고 싶었습니다. [1, 2, 3] 바이트를 문자 형식으로 변환하지 않고 정확히 동일한 값을 저장하려면, Arrays.toString (byte_array)이이를 수행합니다. 그러나 바이트 대신 문자를 저장하려면 String s = new String (byte_array)를 사용해야합니다. 이 경우 s는 문자 형식에서 [1, 2, 3]과 같습니다.
질문자

@sas,이 정보를 댓글이 아닌 편집하여 답변 자체에 추가해야합니다. 일반적으로 주석은 언제든지 삭제 될 수 있음을 항상 명심 해야 합니다. 정말 중요한 정보는 답변 자체에 있어야합니다.
Jeen Broekstra

3

Andreas_D의 이전 답변은 좋습니다. 출력을 표시 할 때마다 글꼴과 문자 인코딩이 있고 일부 문자를 지원하지 않을 수 있음을 추가하겠습니다.

Java인지 디스플레이인지 확인하려면 다음과 같이하십시오.

    for(int i=0;i<str.length();i++) {
        char ch = str.charAt(i);
        System.out.println(i+" : "+ch+" "+Integer.toHexString(ch)+((ch=='\ufffd') ? " Unknown character" : ""));
    }

Java는 이해할 수없는 모든 문자를 알 수없는 문자의 공식 문자 0xfffd로 매핑합니다. '?'가 보이면 출력에 있지만 0xfffd에 매핑되지 않은 경우 Java가 아닌 디스플레이 글꼴 또는 인코딩이 문제입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.