먼저 이것이 Java의 String을 의미한다는 점을 설명하겠습니다. C/C++로 전환하기로 결정했지만 오늘 문제가 발생하여 계속 살펴보고 싶습니다. 문자열의 정의는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다 .
공개 최종 클래스 문자열
{
개인 최종 문자 값[] // 저장된 문자열
private final int offset; // 시작 위치
개인 최종 정수 개수; //문자 수
private int hash; // 캐시된 해시 값
...
}
디버깅 시 다음과 같이 저장된 값을 확인할 수 있습니다.
hashCode()가 호출되지 않은 경우 해시 값은 0이라는 점에 유의해야 합니다. 여기서의 값은 실제 저장된 문자열 값(즉, "문자열 테스트")의 char 배열이라는 것을 쉽게 알 수 있으며, 각 char의 값은 무엇인가? 확인하기 쉬움: 유니코드.
이 시점에서 누구나 일반적으로 사용되는 subString이 어떻게 구현되었는지 추측할 수 있습니다. 이를 구현하려면 새 문자열에서 동일한 값(char 배열)을 사용하고 오프셋과 개수만 수정하도록 하세요. 이는 공간을 절약하고 빠르며(복사할 필요 없음) 실제로는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다 .
공개 문자열 하위 문자열(int startIndex) {
return 부분문자열(beginIndex, count);
}
공개 문자열 하위 문자열(int BeginIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) ?
new String(오프셋 + 시작 인덱스, 끝 인덱스 - 시작 인덱스, 값);
}
String(int 오프셋, int 개수, char 값[]) {
this.value = 값;
this.offset = 오프셋;
this.count = 개수;
}
문자열에 대해 논의하고 있으므로 JVM은 기본적으로 어떤 인코딩을 사용합니까? 디버깅을 통해 다음을 찾을 수 있습니다.
다음과 같이 코드 코드를 복사합니다 .
공개 정적 Charset defaultCharset() {
if (defaultCharset == null) {
동기화됨(Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
문자셋 cs = 조회(csn);
if (cs != null)
defaultCharset = cs;
또 다른
defaultCharset = forName("UTF-8");
}
}
defaultCharset 값을 전달할 수 있습니다.
-Dfile.encoding=utf-8
설정을 합니다. 물론 "abc"로 설정하고 싶다면 가능하지만 기본적으로 UTF-8로 설정됩니다. System.getProperty("file.encoding")을 통해 구체적인 값을 확인할 수 있습니다. defaultCharset이 표시되는 이유는 무엇입니까? 네트워크 전송 프로세스는 바이트 배열이어야 하기 때문에 서로 다른 인코딩 방법으로 얻은 바이트 배열이 다를 수 있습니다. 그럼, 인코딩 방식이 어떻게 얻어지는지 알아야겠죠? 바이트 배열을 가져오는 구체적인 방법은 getBytes이며, 아래에서 중점적으로 설명할 내용은 다음과 같이 CharsetEncoder의 인코딩 방법입니다.
다음과 같이 코드 코드를 복사합니다 .
공개 최종 CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) {
int newState = endOfInput ? ST_END : ST_CODING;
if ((상태 != ST_RESET) && (상태 != ST_CODING) && !(endOfInput && (상태 == ST_END)))
throwIllegalStateException(state, newState);
상태 = 새로운 상태;
을 위한 (;;) {
CoderResult cr;
노력하다 {
cr = encodeLoop(in, out);
} 잡기(BufferUnderflowException x) {
새로운 CoderMalfunctionError(x)를 발생시킵니다.
} 잡기(BufferOverflowException x) {
새로운 CoderMalfunctionError(x)를 발생시킵니다.
}
if (cr.isOverflow())
cr을 반환;
if (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} 또 다른 {
cr을 반환;
}
}
CodingErrorAction 작업 = null;
if (cr.isMalformed())
작업 = MalformedInputAction;
그렇지 않은 경우(cr.isUnmappable())
액션 = unmappableCharacterAction;
또 다른
거짓 주장 : cr.toString();
if (작업 == CodingErrorAction.REPORT)
cr을 반환;
if (액션 == CodingErrorAction.REPLACE) {
if (out.remaining() < 교체.길이)
CoderResult.OVERFLOW를 반환합니다.
out.put(교체);
}
if ((action == CodingErrorAction.IGNORE) || (action == CodingErrorAction.REPLACE)) {
in.position(in.position() + cr.length());
계속하다;
}
거짓을 주장하다;
}
}
물론 필요한 인코딩 형식에 따라 해당 CharsetEncoder가 먼저 선택되며 가장 중요한 것은 서로 다른 CharsetEncoder가 서로 다른 encodeLoop 메서드를 구현한다는 것입니다. 여기에 왜 for(;;)가 있는지 이해가 안 되시나요? 실제로 CharsetEncoder가 있는 패키지(nio)와 해당 매개변수를 보면 대략적으로 이해할 수 있습니다. 이 함수는 스트림을 처리할 수 있습니다(여기서 사용할 때 루프는 사용하지 않지만).
encodeLoop 메소드에서는 가능한 한 많은 문자가 바이트로 변환되며 new String은 위의 거의 반대 프로세스입니다.
실제 개발 과정에서 왜곡된 문자를 자주 접하게 됩니다.
파일을 업로드할 때 파일 이름을 가져옵니다.
JS가 백엔드로 전달한 문자열입니다.
먼저 다음 코드의 실행 결과를 시도해 보세요.
다음과 같이 코드 코드를 복사합니다 .
public static void main(String[] args)에서 예외가 발생합니다.
문자열 str = "문자열";
// -41 -42 -73 -5 -76 -82
printArray(str.getBytes());
// -27 -83 -105 -25 -84 -90 -28 -72 -78
printArray(str.getBytes("utf-8"));
// ???
System.out.println(new String(str.getBytes(), "utf-8"));
// 잉쥐안?
System.out.println(new String(str.getBytes("utf-8"), "gbk"));
//성격??
System.out.println(new String("瀛涓?".getBytes("gbk"), "utf-8"));
// -41 -42 -73 -5 63 63
printArray(new String("Yingjuan?".getBytes("gbk"), "utf-8").getBytes());
}
공개 정적 무효 printArray(바이트[] bs){
for(int i = 0; i < bs.length; i++){
System.out.print(bs[i] + " ");
}
System.out.println();
}
출력은 프로그램의 주석에 설명되어 있습니다.
GBK의 2바이트는 한자를 나타내므로 6바이트가 됩니다.
UTF-8의 3바이트는 한자를 나타내므로 9바이트가 됩니다.
GBK에서 생성할 수 없는 바이트 배열을 사용하여 UTF-8 규칙에 따라 문자열을 생성하므로 ???가 표시됩니다.
GBK가 UTF-8로 생성된 바이트를 사용하여 문자열을 생성하는 경우가 종종 발생하는 이유가 바로 이것이다.
위에서 생성된 코드는 왜곡되어 있지만 컴퓨터는 그렇게 생각하지 않으므로 getBytes를 통해 여전히 바이트 배열을 얻을 수 있으며 이 배열의 UTF-8을 인식할 수 있습니다.
마지막 두 63(?)은 인코딩으로 채워야 합니다(또는 직접 채울 수 있는 바이트가 부족하여 이 부분을 자세히 살펴보지 않았습니다).
GBK와 UTF-8은 문자와 숫자의 인코딩이 동일하기 때문에 이러한 문자를 처리할 때 문자가 깨질 수 없습니다. 그러나 한자의 인코딩이 실제로 다르기 때문에 많은 문제가 발생합니다. 아래 코드에서 :
new String(new String("we".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
분명히 이 코드의 결과는 "us"이지만, 이것이 우리에게 어떤 영향을 미치나요? 먼저 우리는 다음을 주목합니다:
new String("we".getBytes("UTF-8"), "GBK");
이 코드의 결과는 잘못된 코드이며, 많은 잘못된 코드가 "이렇게 엉망"입니다. 하지만 기억하세요: 여기의 혼돈은 우리를 위한 것이고, 컴퓨터에게는 그것이 "지저분"인지 "지저분하지 않은지"는 중요하지 않습니다. 우리가 거의 포기하더라도 여전히 "getBytes("를 통해 잘못된 코드에서 이를 얻을 수 있습니다. "GBK")”는 "백본"이며, "백본"을 사용하여 원래 문자열을 복원할 수 있습니다.
위의 코드는 "GBK"와 "UTF-8" 사이의 왜곡된 문제를 해결할 수 있는 것처럼 보이지만 이 솔루션은 특수한 경우에만 제한됩니다. 연속되는 모든 한자의 수가 짝수입니다! 그 이유는 위에서 언급했으므로 여기서는 반복하지 않겠습니다.
그렇다면 이 문제를 해결하는 방법은 무엇입니까?
첫 번째 솔루션: encodeURI 이 방법을 사용하는 이유는 무엇입니까? 이유는 매우 간단합니다. GBK와 UTF-8은 %, 숫자, 문자의 인코딩이 동일하므로 인코딩 후의 문자열은 이 두 인코딩에서 100% 동일함을 보장한 다음 디코딩하여 문자를 얻을 수 있습니다. .그냥 꼬치. String의 형식에 따르면 인코딩 및 디코딩의 효율성이 매우 높다는 것을 짐작할 수 있으므로 이 역시 좋은 솔루션입니다.
두 번째 해결 방법: 통합 인코딩 형식 <BR>여기서는 Webx 마이닝을 사용하고 있습니다. webx.xml에 defaultCharset="UTF-8"만 설정하면 됩니다.