Uint8Array를 Javascript의 문자열로


122

Javascript의 Uint8Array 요소 범위에있는 UTF-8 인코딩 데이터가 있습니다. 이것을 일반 자바 스크립트 문자열로 디코딩하는 효율적인 방법이 있습니까 (Javascript가 16 비트 유니 코드를 사용한다고 생각합니다)? 문자열 결합이 CPU를 많이 사용하므로 한 번에 한 문자를 추가하고 싶지 않습니다.


작동하는지 확실하지 않지만 u8array.toString()을 호출 할 때 Uint8Array 객체를 노출하는 BrowserFS에서 파일을 읽을 때 사용합니다 fs.readFile.
jcubic

1
@jcubic for me, toStringon Uint8Array"91,50,48,49,57,45"(Chrome 79) 와 같이 쉼표로 구분 된 숫자 를 반환합니다.
kolen

답변:


171

TextEncoderTextDecoder로부터 인코딩 표준 에 의해 polyfilled된다 stringencoding 라이브러리 현과 ArrayBuffers간에 변환 :

var uint8array = new TextEncoder("utf-8").encode("¢");
var string = new TextDecoder("utf-8").decode(uint8array);

40
나처럼 게으른 사람이라면 npm install text-encoding, var textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;. 고맙지 만 사양 할게.
에반 후

16
npm 텍스트 인코딩 라이브러리에주의하십시오. webpack 번들 분석기는 라이브러리가 거대하다는 것을 보여줍니다.
wayofthefuture

3
@VincentScheib Browsers는 utf-8. 따라서 TextEncoder논쟁은 불필요합니다!
tripulse

1
예제의 nodejs.org/api/string_decoder.html : const {StringDecoder} = require ( 'string_decoder'); const 디코더 = new StringDecoder ( 'utf8'); const cent = Buffer.from ([0xC2, 0xA2]); console.log (decoder.write (cent));
curist 19

4
Node.js 는 v11에 TextEncoder/ TextDecoderAPI를 추가 했으므로 현재 Node 버전 만 대상으로하는 경우 추가 패키지를 설치할 필요가 없습니다.
Loilo

42

이것은 작동합니다.

// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt

/* utf.js - UTF-8 <=> UTF-16 convertion
 *
 * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
 * Version: 1.0
 * LastModified: Dec 25 1999
 * This library is free.  You can redistribute it and/or modify it.
 */

function Utf8ArrayToStr(array) {
    var out, i, len, c;
    var char2, char3;

    out = "";
    len = array.length;
    i = 0;
    while(i < len) {
    c = array[i++];
    switch(c >> 4)
    { 
      case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
        // 0xxxxxxx
        out += String.fromCharCode(c);
        break;
      case 12: case 13:
        // 110x xxxx   10xx xxxx
        char2 = array[i++];
        out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
        break;
      case 14:
        // 1110 xxxx  10xx xxxx  10xx xxxx
        char2 = array[i++];
        char3 = array[i++];
        out += String.fromCharCode(((c & 0x0F) << 12) |
                       ((char2 & 0x3F) << 6) |
                       ((char3 & 0x3F) << 0));
        break;
    }
    }

    return out;
}

해킹을 사용하지 않고 브라우저 JS 기능에 의존하지 않기 때문에 다른 솔루션보다 다소 깨끗합니다. 예를 들어 다른 JS 환경에서도 작동합니다.

JSFiddle 데모를 확인하십시오 .

관련 질문도 참조하십시오 : 여기여기


6
이것은 다소 느린 것 같습니다. 그러나 내가 찾은 우주에서 유일한 스 니펫은 작동합니다. 좋은 찾기 + 입양!
Redsandro

6
왜 이것이 더 많은 찬성표가 없는지 이해할 수 없습니다. 작은 스 니펫에 대해 UTF-8 규칙을 통과하는 것이 현명하게 보입니다. Async Blob + Filereader는 다른 사람들이 지적했듯이 큰 텍스트에 적합합니다.
DanHorner

2
문제는 문자열 연결하지 않고이 작업을 수행하는 방법이었다
잭 Wester은

5
4 바이트 이상의 시퀀스를 처리하지 않는다는 점을 제외하면 훌륭하게 작동합니다. 예를 들어 fromUTF8Array([240,159,154,133])비어 있음 (while fromUTF8Array([226,152,131])→"☃")
unhammer

1
사례 8, 9, 10 및 11이 제외되는 이유는 무엇입니까? 특별한 이유가 있습니까? 케이스 15도 가능 하죠? 15 (1111)은 4 바이트가 사용되었음을 나타냅니다.
RaR

31

내가 사용하는 것은 다음과 같습니다.

var str = String.fromCharCode.apply(null, uint8Arr);

7
로부터 문서 , 이것은 디코딩 UTF8하지 않는 것.
알버트

29
이것은 RangeError더 큰 텍스트 를 던질 것 입니다. "최대 호출 스택 크기 초과"
Redsandro

1
큰 Uint8Array를 이진 문자열로 변환하고 RangeError가 발생하는 경우 stackoverflow.com/a/12713326/471341 에서 Uint8ToString 함수를 참조하세요 .
yonran

IE 11은 SCRIPT28: Out of stack space300 + k 문자를 공급하거나 RangeErrorChrome 39의 경우 발생합니다. Firefox 33은 괜찮습니다. 100 + k는 세 가지 모두에서 정상적으로 실행됩니다.
Sheepy 2014

이것은 en.wikipedia.org/wiki/UTF-8 의 예제 유니 코드 문자에서 올바른 결과를 생성하지 않습니다 . 예를 들어, String.fromCharCode.apply (null, new Uint8Array ([0xc2, 0xa2]))는 ¢을 생성하지 않습니다.
Vincent Scheib

16

Chrome 샘플 애플리케이션 중 하나에서 찾을 수 있지만 이는 비동기 변환에 문제가없는 더 큰 데이터 블록을위한 것입니다.

/**
 * Converts an array buffer to a string
 *
 * @private
 * @param {ArrayBuffer} buf The buffer to convert
 * @param {Function} callback The function to call when conversion is complete
 */
function _arrayBufferToString(buf, callback) {
  var bb = new Blob([new Uint8Array(buf)]);
  var f = new FileReader();
  f.onload = function(e) {
    callback(e.target.result);
  };
  f.readAsText(bb);
}

2
당신이 말했듯이, 이것은 변환 할 버퍼가 정말로 크지 않으면 끔찍하게 수행 될 것입니다. 예를 들어 V8에서 구현 된 간단한 문자열 (예 : 10-40 바이트)의 wchar에 대한 동기식 UTF-8 변환은 마이크로 초보다 훨씬 작아야하지만 코드에는 수백 배가 필요하다고 생각합니다. 모두 똑같이 감사합니다.
Jack Wester 2013 년

15

노드에서 " Buffer인스턴스도 Uint8Array인스턴스 " buf.toString()이므로이 경우에 작동합니다.


나를 위해 잘 작동합니다. 그리고 너무 간단합니다! 그러나 실제로 Uint8Array 에는 toString () 메서드가 있습니다.
doom

단순하고 우아한, Buffer몰랐던 Uint8Array도 있습니다. 감사!
LeOn-Han Li

1
@doom 브라우저 측에서 Uint8Array.toString ()은 utf-8 문자열을 컴파일하지 않고 배열의 숫자 값을 나열합니다. 따라서 가지고있는 것이 버퍼가 아닌 다른 소스의 Uint8Array 인 경우 마법을 수행하기 위해 하나를 만들어야합니다.Buffer.from(uint8array).toString('utf-8')
Joachim Lous 19

12

Albert가 제공 한 솔루션은 제공된 함수가 자주 호출되지 않고 적당한 크기의 배열에만 사용되는 한 잘 작동합니다. 그렇지 않으면 매우 비효율적입니다. 다음은 Node와 브라우저 모두에서 작동하며 다음과 같은 장점이있는 향상된 바닐라 JavaScript 솔루션입니다.

• 모든 옥텟 배열 크기에 대해 효율적으로 작동

• 중간에 던져 버리는 문자열을 생성하지 않습니다.

• 최신 JS 엔진에서 4 바이트 문자 지원 (그렇지 않으면 "?"로 대체 됨)

var utf8ArrayToStr = (function () {
    var charCache = new Array(128);  // Preallocate the cache for the common single byte chars
    var charFromCodePt = String.fromCodePoint || String.fromCharCode;
    var result = [];

    return function (array) {
        var codePt, byte1;
        var buffLen = array.length;

        result.length = 0;

        for (var i = 0; i < buffLen;) {
            byte1 = array[i++];

            if (byte1 <= 0x7F) {
                codePt = byte1;
            } else if (byte1 <= 0xDF) {
                codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
            } else if (byte1 <= 0xEF) {
                codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
            } else if (String.fromCodePoint) {
                codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
            } else {
                codePt = 63;    // Cannot convert four byte code points, so use "?" instead
                i += 3;
            }

            result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
        }

        return result.join('');
    };
})();

2
4 바이트 문자 (예 : 이모 지)도 처리하므로 여기에서 최고의 솔루션입니다. 감사합니다!
fiffy

1
그리고 이것의 역은 무엇입니까?
simbo1905

6

@Sudhir가 말한 것을 한 다음 쉼표로 구분 된 숫자 목록에서 문자열을 얻으려면 다음을 사용하십시오.

for (var i=0; i<unitArr.byteLength; i++) {
            myString += String.fromCharCode(unitArr[i])
        }

여전히 관련이있는 경우 원하는 문자열을 제공합니다.


죄송합니다. 한 번에 하나의 문자를 추가하고 싶지 않다고 말한 마지막 감각을 찾지 못했습니다. 그러나 이것이 CPU 사용에 문제가없는 다른 사람들에게 도움이되기를 바랍니다.
shuki

14
이것은 UTF8 디코딩을 수행하지 않습니다.
알버트

더 짧게 : String.fromCharCode.apply(null, unitArr);. 언급했듯이 UTF8 인코딩을 처리하지 않지만 ASCII 지원 만 필요하지만 TextEncoder / TextDecoder에 대한 액세스 권한이없는 경우이 방법이 충분히 간단합니다.
Ravenstine

대답에는 @Sudhir가 언급되어 있지만 페이지를 검색 한 결과 그런 대답을 찾았습니다. 그래서 그가 말한 것을 인라인하는 것이 더 나을 것입니다
Joakim

이것은 긴 문자열에서 끔찍한 성능을 발휘합니다. 문자열에 + 연산자를 사용하지 마십시오.
Max

3

당신은 사용할 수없는 경우 TextDecoder API를 하기 때문에 그것이 IE에서 지원되지 않습니다 :

  1. Mozilla 개발자 네트워크 웹 사이트 에서 권장 하는 FastestSmallestTextEncoderDecoder 폴리 필을 사용할 수 있습니다 .
  2. MDN 웹 사이트 에서도 제공되는이 기능을 사용할 수 있습니다 .

function utf8ArrayToString(aBytes) {
    var sView = "";
    
    for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
        nPart = aBytes[nIdx];
        
        sView += String.fromCharCode(
            nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
                /* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
                (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
                (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
                (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
                (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
            : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
                (nPart - 192 << 6) + aBytes[++nIdx] - 128
            : /* nPart < 127 ? */ /* one byte */
                nPart
        );
    }
    
    return sView;
}

let str = utf8ArrayToString([50,72,226,130,130,32,43,32,79,226,130,130,32,226,135,140,32,50,72,226,130,130,79]);

// Must show 2H₂ + O₂ ⇌ 2H₂O
console.log(str);


2

이 기능을 시도해보십시오.

var JsonToArray = function(json)
{
    var str = JSON.stringify(json, null, 0);
    var ret = new Uint8Array(str.length);
    for (var i = 0; i < str.length; i++) {
        ret[i] = str.charCodeAt(i);
    }
    return ret
};

var binArrayToJson = function(binArray)
{
    var str = "";
    for (var i = 0; i < binArray.length; i++) {
        str += String.fromCharCode(parseInt(binArray[i]));
    }
    return JSON.parse(str)
}

출처 : https://gist.github.com/tomfa/706d10fed78c497731ac , Tomfa에 대한 찬사


2

사람들이 두 가지 방법을 모두 보여주지 않거나 사소한 UTF8 문자열에서 작동하지 않는다는 것을 보여주지 않아서 실망했습니다. 잘 작동하는 코드가있는 codereview.stackexchange.com 에서 게시물을 찾았 습니다 . 고대 룬을 바이트로 바꾸고, 바이트에 대한 크라이 포를 테스트 한 다음, 다시 문자열로 변환하는 데 사용했습니다. 작업 코드는 여기 github에 있습니다 . 명확성을 위해 메서드 이름을 변경했습니다.

// https://codereview.stackexchange.com/a/3589/75693
function bytesToSring(bytes) {
    var chars = [];
    for(var i = 0, n = bytes.length; i < n;) {
        chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff));
    }
    return String.fromCharCode.apply(null, chars);
}

// https://codereview.stackexchange.com/a/3589/75693
function stringToBytes(str) {
    var bytes = [];
    for(var i = 0, n = str.length; i < n; i++) {
        var char = str.charCodeAt(i);
        bytes.push(char >>> 8, char & 0xFF);
    }
    return bytes;
}

단위 테스트는 다음 UTF-8 문자열을 사용합니다.

    // http://kermitproject.org/utf8.html
    // From the Anglo-Saxon Rune Poem (Rune version) 
    const secretUtf8 = `ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬`;

문자열 길이는 117 자이지만 인코딩 된 경우 바이트 길이는 234입니다.

console.log 줄의 주석 처리를 제거하면 디코딩 된 문자열이 인코딩 된 문자열과 동일하다는 것을 알 수 있습니다 (Shamir의 비밀 공유 알고리즘을 통해 전달 된 바이트 포함!).

인코딩 및 디코딩을 시연하는 단위 테스트


String.fromCharCode.apply(null, chars)chars너무 크면 오류가 발생합니다 .
Marc J. Schmidt

1

NodeJS에는 사용할 수있는 버퍼가 있으며 문자열 변환이 정말 쉽습니다. 더 나은 것은 Uint8Array를 버퍼로 변환하는 것이 쉽습니다. 이 코드를 사용해보십시오, 기본적으로 Uint8Arrays와 관련된 모든 변환을 위해 Node에서 저에게 효과적입니다.

let str = Buffer.from(uint8arr.buffer).toString();

Uint8Array에서 ArrayBuffer를 추출한 다음 적절한 NodeJS 버퍼로 변환합니다. 그런 다음 버퍼를 문자열로 변환합니다 (원하는 경우 16 진수 또는 base64 인코딩을 사용할 수 있음).

문자열에서 Uint8Array로 다시 변환하려면 다음을 수행합니다.

let uint8arr = new Uint8Array(Buffer.from(str));

문자열로 변환 할 때 base64와 같은 인코딩을 선언 한 경우 base64를 사용 Buffer.from(str, "base64")했거나 사용한 다른 인코딩을 사용해야합니다.

모듈이없는 브라우저에서는 작동하지 않습니다! NodeJS 버퍼는 브라우저에 존재하지 않으므로 브라우저에 버퍼 기능을 추가하지 않으면이 방법이 작동하지 않습니다. 즉, 생각해야 할 아주 쉽게 사실이다, 다만 같은 모듈 사용 작고 빠른 둘 다!


0
class UTF8{
static encode(str:string){return new UTF8().encode(str)}
static decode(data:Uint8Array){return new UTF8().decode(data)}

private EOF_byte:number = -1;
private EOF_code_point:number = -1;
private encoderError(code_point) {
    console.error("UTF8 encoderError",code_point)
}
private decoderError(fatal, opt_code_point?):number {
    if (fatal) console.error("UTF8 decoderError",opt_code_point)
    return opt_code_point || 0xFFFD;
}
private inRange(a:number, min:number, max:number) {
    return min <= a && a <= max;
}
private div(n:number, d:number) {
    return Math.floor(n / d);
}
private stringToCodePoints(string:string) {
    /** @type {Array.<number>} */
    let cps = [];
    // Based on http://www.w3.org/TR/WebIDL/#idl-DOMString
    let i = 0, n = string.length;
    while (i < string.length) {
        let c = string.charCodeAt(i);
        if (!this.inRange(c, 0xD800, 0xDFFF)) {
            cps.push(c);
        } else if (this.inRange(c, 0xDC00, 0xDFFF)) {
            cps.push(0xFFFD);
        } else { // (inRange(c, 0xD800, 0xDBFF))
            if (i == n - 1) {
                cps.push(0xFFFD);
            } else {
                let d = string.charCodeAt(i + 1);
                if (this.inRange(d, 0xDC00, 0xDFFF)) {
                    let a = c & 0x3FF;
                    let b = d & 0x3FF;
                    i += 1;
                    cps.push(0x10000 + (a << 10) + b);
                } else {
                    cps.push(0xFFFD);
                }
            }
        }
        i += 1;
    }
    return cps;
}

private encode(str:string):Uint8Array {
    let pos:number = 0;
    let codePoints = this.stringToCodePoints(str);
    let outputBytes = [];

    while (codePoints.length > pos) {
        let code_point:number = codePoints[pos++];

        if (this.inRange(code_point, 0xD800, 0xDFFF)) {
            this.encoderError(code_point);
        }
        else if (this.inRange(code_point, 0x0000, 0x007f)) {
            outputBytes.push(code_point);
        } else {
            let count = 0, offset = 0;
            if (this.inRange(code_point, 0x0080, 0x07FF)) {
                count = 1;
                offset = 0xC0;
            } else if (this.inRange(code_point, 0x0800, 0xFFFF)) {
                count = 2;
                offset = 0xE0;
            } else if (this.inRange(code_point, 0x10000, 0x10FFFF)) {
                count = 3;
                offset = 0xF0;
            }

            outputBytes.push(this.div(code_point, Math.pow(64, count)) + offset);

            while (count > 0) {
                let temp = this.div(code_point, Math.pow(64, count - 1));
                outputBytes.push(0x80 + (temp % 64));
                count -= 1;
            }
        }
    }
    return new Uint8Array(outputBytes);
}

private decode(data:Uint8Array):string {
    let fatal:boolean = false;
    let pos:number = 0;
    let result:string = "";
    let code_point:number;
    let utf8_code_point = 0;
    let utf8_bytes_needed = 0;
    let utf8_bytes_seen = 0;
    let utf8_lower_boundary = 0;

    while (data.length > pos) {
        let _byte = data[pos++];

        if (_byte == this.EOF_byte) {
            if (utf8_bytes_needed != 0) {
                code_point = this.decoderError(fatal);
            } else {
                code_point = this.EOF_code_point;
            }
        } else {
            if (utf8_bytes_needed == 0) {
                if (this.inRange(_byte, 0x00, 0x7F)) {
                    code_point = _byte;
                } else {
                    if (this.inRange(_byte, 0xC2, 0xDF)) {
                        utf8_bytes_needed = 1;
                        utf8_lower_boundary = 0x80;
                        utf8_code_point = _byte - 0xC0;
                    } else if (this.inRange(_byte, 0xE0, 0xEF)) {
                        utf8_bytes_needed = 2;
                        utf8_lower_boundary = 0x800;
                        utf8_code_point = _byte - 0xE0;
                    } else if (this.inRange(_byte, 0xF0, 0xF4)) {
                        utf8_bytes_needed = 3;
                        utf8_lower_boundary = 0x10000;
                        utf8_code_point = _byte - 0xF0;
                    } else {
                        this.decoderError(fatal);
                    }
                    utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed);
                    code_point = null;
                }
            } else if (!this.inRange(_byte, 0x80, 0xBF)) {
                utf8_code_point = 0;
                utf8_bytes_needed = 0;
                utf8_bytes_seen = 0;
                utf8_lower_boundary = 0;
                pos--;
                code_point = this.decoderError(fatal, _byte);
            } else {
                utf8_bytes_seen += 1;
                utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);

                if (utf8_bytes_seen !== utf8_bytes_needed) {
                    code_point = null;
                } else {
                    let cp = utf8_code_point;
                    let lower_boundary = utf8_lower_boundary;
                    utf8_code_point = 0;
                    utf8_bytes_needed = 0;
                    utf8_bytes_seen = 0;
                    utf8_lower_boundary = 0;
                    if (this.inRange(cp, lower_boundary, 0x10FFFF) && !this.inRange(cp, 0xD800, 0xDFFF)) {
                        code_point = cp;
                    } else {
                        code_point = this.decoderError(fatal, _byte);
                    }
                }

            }
        }
        //Decode string
        if (code_point !== null && code_point !== this.EOF_code_point) {
            if (code_point <= 0xFFFF) {
                if (code_point > 0)result += String.fromCharCode(code_point);
            } else {
                code_point -= 0x10000;
                result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff));
                result += String.fromCharCode(0xDC00 + (code_point & 0x3ff));
            }
        }
    }
    return result;
}

`


답변에 대한 설명을 추가하십시오. @terran
Rohit Poudel

-3

이 Typescript 스 니펫을 사용하고 있습니다.

function UInt8ArrayToString(uInt8Array: Uint8Array): string
{
    var s: string = "[";
    for(var i: number = 0; i < uInt8Array.byteLength; i++)
    {
        if( i > 0 )
            s += ", ";
        s += uInt8Array[i];
    }
    s += "]";
    return s;
}

JavaScript 버전이 필요한 경우 유형 주석을 제거하십시오. 도움이 되었기를 바랍니다!


3
OP는 한 번에 하나의 문자를 추가하지 않도록 요청했습니다. 또한 그는 그것을 목록의 문자열 표현으로 표시하지 않고 문자열로 표시하기를 원합니다. 또한 이것은 문자를 문자열로 변환하지 않지만 숫자를 표시합니다.
알버트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.