특정 유니 코드 문자로 주석에서 Java 코드를 실행할 수있는 이유는 무엇입니까?


1356

다음 코드는 "Hello World!"출력을 생성합니다. (실제로 시도하십시오).

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

Java 컴파일러는 유니 코드 문자 \u000d를 새 행으로 구문 분석하고 다음 과 같이 변환하기 때문입니다.

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

따라서 주석이 "실행"됩니다.

이것은 악의적 인 코드 나 악의적 인 프로그래머가 생각할 수있는 것을 "숨기는"데 사용될 수 있기 때문에 왜 주석에 허용 됩니까?

Java 스펙에서 이것이 허용되는 이유는 무엇입니까?


44
"왜 이것이 허용됩니까?"는 나에게 너무 의견에 근거한 것 같습니다. 언어 설계자들이 결정을 내 렸습니다. 그 밖의 무엇을 알아야합니까? 해당 결정을 내리는 사람의 진술을 찾을 수 없다면, 우리는 추측 만 할 수 있습니다.
Ingo Bürk

194
흥미로운 점 중 하나는 OP의 IDE가 분명히 잘못되어 잘못된 강조 표시를 표시 한다는 것입니다 .
dhke


47
@Tobb 그러나 자바 디자이너는 SO 방문 은 그래서 가능 그들 중 하나에 의해 답변을 얻을 수 있습니다. 또한 이미이 질문에 대답하는 리소스가있을 수 있습니다.
Pshemo

41
간단한 대답은 언어 규칙에 따라 코드가 전혀 주석에 포함되어 있지 않으므로 질문이 잘못 작성되었다는 것입니다.
user207421

답변:


741

유니 코드 디코딩은 다른 어휘 변환 전에 발생합니다. 이것의 주요 이점은 ASCII와 다른 인코딩 사이를왔다 갔다하는 것이 사소한 것입니다. 주석이 어디서 시작하고 끝나는 지 알아낼 필요조차 없습니다!

JLS 섹션 3.3에 명시된 바와 같이 ASCII 기반 도구는 소스 파일을 처리 할 수 ​​있습니다.

[...] Java 프로그래밍 언어는 프로그램을 ASCII 기반 도구로 처리 할 수있는 형식으로 변경하는 유니 코드로 작성된 프로그램을 ASCII로 변환하는 표준 방법을 지정합니다. [...]

이것은 항상 Java 플랫폼의 주요 목표였던 플랫폼 독립성 (지원되는 문자 세트의 독립성)을 근본적으로 보증합니다.

파일의 어느 곳에서나 유니 코드 문자를 작성할 수있는 기능은 깔끔한 기능이며, 특히 비 라틴 언어로 코드를 문서화 할 때 주석에서 중요합니다. 그것이 미묘한 방식으로 의미론을 방해 할 수 있다는 사실은 (불행한) 부작용입니다.

이 주제에는 많은 문제가 있으며 Joshua Bloch와 Neal Gafter의 Java Puzzlers 는 다음 변형을 포함했습니다.

이것은 합법적 인 Java 프로그램입니까? 그렇다면 무엇을 인쇄합니까?

\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d

(이 프로그램은 일반 "Hello World"프로그램으로 판명되었습니다.)

퍼즐 러에 대한 해결책에서 그들은 다음을 지적합니다.

더 진지하게,이 퍼즐은 이전 세 가지 교훈을 강화하는 데 도움이됩니다 . 유니 코드 이스케이프는 다른 방식으로 표현할 수없는 문자를 프로그램에 삽입해야 할 때 필수적입니다. 다른 모든 경우에는 피하십시오.


출처 : 자바 : 주석에서 코드를 실행하고 있습니까?!


84
간단히 말해 Java는 의도적으로 허용합니다. "버그"가 OP의 IDE에 있습니까?
Bathsheba

60
@Bathsheba : 사람들의 머리에 더 있습니다. 사람들은 Java 파싱의 작동 방식을 이해하려고 시도하지 않으므로 IDE는 때때로 코드를 잘못된 방식으로 표시합니다. 위의 예에서 주석은 주석으로 \u000d끝나고 그 다음 부분에는 코드 강조 표시가 있어야합니다.
Aaron Digulla 2016 년

62
또 다른 일반적인 실수는 코드에 Windows 경로를 붙여 넣어 유효한 유니 코드 이스케이프 시퀀스가 ​​아니기 // C:\user\...때문에 컴파일 오류를 발생 \user시키는 것입니다.
Aaron Digulla 2016 년

50
일식에서 코드 이후 \u000d는 부분적으로 강조 표시됩니다. Ctrl + Shift + F를 누르면 문자가 새 줄로 바뀌고 나머지 줄은 줄 바꿈됩니다.
bluelDe

20
@TheLostMind 대답을 올바르게 이해하면 블록 주석으로도 이것을 재현 할 수 있어야합니다. \u002A/주석을 끝내야합니다.
Taemyr

141

이것이 아직 다루어지지 않았으므로 여기에 설명, 왜 다른 소스 코드 처리 전에 유니 코드 이스케이프 변환이 발생합니까?

그 뒤에 아이디어는 다른 문자 인코딩간에 Java 소스 코드를 무손실로 번역 할 수 있다는 것입니다. 오늘날 유니 코드 지원은 널리 보급되어 있지만 문제처럼 보이지는 않지만 서구의 개발자가 아시아 문자를 포함하는 아시아 동료로부터 소스 코드를 수신하는 것은 쉽지 않았습니다. (컴파일 및 테스트 포함) 결과를 손상시키지 않고 결과를 다시 보냅니다.

따라서 Java 소스 코드는 모든 인코딩으로 작성 될 수 있으며 식별자, 문자 및 String리터럴 및 주석 내의 광범위한 문자를 허용합니다 . 그런 다음 무손실로 전송하기 위해 대상 인코딩에서 지원하지 않는 모든 문자는 유니 코드 이스케이프로 대체됩니다.

이것은 뒤집을 수있는 프로세스이며 흥미로운 점은 변환 규칙이 종속되지 않기 때문에 Java 소스 코드 구문에 대해 아무것도 알 필요가없는 도구로 변환을 수행 할 수 있다는 것입니다. 이것은 컴파일러 내부의 실제 유니 코드 문자로의 변환으로 Java 소스 코드 구문과 독립적으로 발생합니다. 소스 코드의 의미를 변경하지 않고 양방향으로 임의의 수의 변환 단계를 수행 할 수 있음을 의미합니다.

이것은 언급되지 않은 또 다른 이상한 기능의 이유입니다. \uuuuuuxxxx구문 :

변환 도구가 문자를 이스케이프하고 이미 이스케이프 된 시퀀스 인 시퀀스를 발견하면를 추가 u하여 시퀀스에 추가해야 \ucafe합니다 \uucafe. 의미는 변하지 않지만 다른 방향으로 변환 할 때 도구는 하나만 제거 u하고 단일 u문자를 포함하는 시퀀스 만 유니 코드 문자로 바꿔야합니다 . 이렇게하면 유니 코드 이스케이프도 앞뒤로 변환 할 때 원래 형식으로 유지됩니다. 아무도 그 기능을 사용한 적이 없다고 생각합니다…


1
흥미롭게도 구문 native2ascii을 사용하지 않는 것 같습니다\uu...xxxx
ninjalj

5
예, 라틴 -1 만 읽도록 수정 된 native2ascii자원 번들을 iso-latin-1로 변환하여 자원 번들을 준비하는 데 도움을주기위한 것 Properties.load입니다. 그리고 규칙이 다르고 \uuu…구문이 없으며 초기 처리 단계가 없습니다. 속성 파일 property=multi\u000aline에서과 동일합니다 property=multi\nline. (문서의 "Java ™ 언어 스펙의 3.3 절에 정의 된 유니 코드 이스케이프 사용"구와
Holger

10
이 디자인 목표는 사마귀없이 달성 될 수 있습니다. 가장 쉬운 방법은 \uU + 0000–007F 범위의 문자를 생성하기 위해 이스케이프를 금지하는 것 입니다. (이러한 모든 문자는 1990 년대에 관련된 모든 국가 인코딩으로 기본적으로 표현 될 수 있습니다. 일부 제어 문자를 제외하고는 Java를 작성하는 데 필요하지 않습니다.)
zwol

3
@zwol : 음, 어쨌든 Java 소스 코드 내에서 허용되지 않는 제어 문자를 제외하면 맞습니다. 그럼에도 불구하고 규칙을 더 복잡하게 만들 수 있습니다. 그리고 오늘, 결정을 논의하기에는 너무 늦었습니다…
Holger

ah 라틴어 또는 다른 것이 아닌 utf8에 문서를 저장하는 문제. 이 서구의 넌센스로 인해 모든 데이터베이스가 손상되었습니다.
David 天宇 Wong

106

나는 나 자신을 도울 수 없기 때문에 완전히 비 효과적으로 포인트를 추가 할 것입니다. 아직 그것을 보지 못했습니다. 질문에 잘못된 전제가 포함되어 있기 때문에 질문이 유효하지 않다는 것입니다. 즉, 코드가 의견!

Java 소스 코드에서 \ u000d는 모든면에서 ASCII CR 문자와 동일합니다. 어디에서나 간단하고 끝이없는 줄입니다. 질문의 형식은 잘못된 것으로, 문자의 순서가 실제로 구문 상 일치하는 것은 다음과 같습니다.

public static void main(String... args) {
   // The comment below is no typo. 
   // 
 System.out.println("Hello World!");
}

IMHO의 가장 정확한 답은 다음과 같습니다. 코드가 주석에 없기 때문에 코드가 실행됩니다. 다음 줄에 있습니다. Java에서 "주석에서 코드 실행"은 예상 한대로 허용되지 않습니다.

혼동의 대부분은 구문 하이 라이터와 IDE가이 상황을 고려할만큼 정교하지 않다는 사실에서 비롯됩니다. 그들은 유니 코드 이스케이프를 전혀 처리하지 않거나 이전과 달리 코드를 구문 분석 한 후에 수행합니다 javac.


6
동의합니다. 이것은 Java "design error"는 아니지만 IDE 버그입니다.
bvdb

3
질문에 대해 오히려 왜 코드 있음 외모 , 언어의 특정 측면을 잘 알고 아마도 구문 강조를 참조하지 않고없는 사람에게 의견과 같은 사실에 없는 코멘트. 유효하지 않은 질문의 전제에 근거한 이의 제기는 불분명하다.
Phil

@ 필 : 특정 도구로 볼 때 주석처럼 보이며 다른 도구에서는 그렇지 않습니다.
jmoreno

1
@jmoreno 하나 안 가지고 코드를 읽을 수있는 더 많은 텍스트 편집기 이상 아무것도 할 수 있습니다. 적어도 스타일의 주석은 다음 \ n 문자까지 계속되고 결국 궁극적으로 \ n으로 대체되는 다른 시퀀스는 아닙니다. 주석은 제거 된 것 이외의 것으로 예상되지 않습니다. 프리 프로세서가 잘못되었습니다.
Phil

69

\u000d때문에 탈출 코멘트를 종료 \u이스케이프가 균일하게 대응하는 유니 코드 문자로 변환 하기 전에 프로그램이 토큰 화됩니다. 주석 을 시작 하는 \u0057\u0057대신 동등하게 사용할 수 있습니다 .//

이것은 IDE의 버그이며 \u000d주석을 끝내는 것을 분명히하기 위해 줄을 구문 강조 표시해야합니다 .

언어의 디자인 오류이기도합니다. 이제는 수정할 수 없습니다. 그에 의존하는 프로그램이 손상 될 수 있습니다. \u이스케이프는 "이해되는"문맥 (문자열 리터럴 및 식별자, 다른 곳은 아님)에서만 컴파일러가 해당 유니 코드 문자로 변환하거나 U + 0000–007F 범위의 문자를 생성하는 것이 금지되어야합니다 , 아니면 둘다. 이러한 의미 중 하나가 이스케이프가 유용한 \u000d경우를 방해하지 않고 이스케이프에 의해 주석이 종료되는 것을 방지했을 것 입니다. 비 라틴 스크립트에서 주석을 인코딩하는 방법으로 주석 내에 이스케이프를 사용 \u하는 것이 포함됩니다\u . 텍스트 편집기는 더 넓은 위치를 볼 수 있습니다\u이스케이프는 컴파일러보다 중요합니다. ( 어쨌든 모든 컨텍스트 \u에서 이스케이프를 해당 문자로 표시하는 편집기 또는 IDE는 알지 못합니다 .)

C 계열에는 유사한 설계 오류가 있습니다. 1 주석 경계를 결정하기 전에 백 슬래시-줄 바꾸기가 처리됩니다. 예 :

// this is a comment \
   this is still in the comment!

나는이 특정 디자인 오류를 만들기가 쉽다는 것을 설명하기 위해 이것을 가져오고, 토큰 화에 대해 생각하고 컴파일러 프로그래머가 생각하는 방식을 파싱하는 데 익숙하다면 오류를 수정하기에 너무 늦을 때까지 오류라는 것을 깨닫지 못합니다. 토큰 화 및 파싱에 대해 기본적으로 공식 문법을 이미 정의한 경우 누군가 문법적인 특수 사례 (예 : 3 부작, 백 슬래시-줄 바꿈, ASCII로 제한되는 소스 파일에서 임의의 유니 코드 문자를 인코딩하는 등 무엇이든 포함)를 작성하는 것이 더 쉽습니다. 토크 나이저 앞에 변형 패스 추가 하여 토크 나이저를 재정 의하여 특수한 경우를 사용하는 것이 적절한 곳에주의를 기울이십시오.

1 pedants : 저는 C의이 측면이 100 % 의도적이라는 것을 알고 있습니다. 이론적 근거는 아닙니다. 필자는 이것을 펀치 카드에 임의로 긴 줄로 코드를 기계적으로 적용 할 수 있다는 것을 알고 있습니다. 여전히 잘못된 디자인 결정이었습니다.


17
나는 그것이 디자인 오류 라고 말하지는 않을 것 입니다. 나는 그것이 좋지 않은 디자인 선택이거나 불행한 결과를 초래 한 선택이라는 것에 동의 할 수는 있지만 여전히 언어 디자이너가 의도 한대로 작동한다고 생각합니다 .ASCII 인코딩을 유지하면서 파일의 모든 유니 코드 문자를 사용할 수 있습니다 파일의.
aioobe 2016 년

12
말했듯이, \u8 단계 표기법에 선행 0을 사용하는 C의 리드를 따르 겠다는 결정보다 처리 단계의 선택 이 덜 모호 하다고 생각합니다 . 8 진수 표기법이 유용한 경우도 있지만, 나는 선행 0이 왜 그것을 나타내는 좋은 방법인지에 대한 논쟁을 다른 사람이 아직 듣지 못했습니다.
supercat

3
@supercat이 기능을 C89에 넣은 사람들은 처음부터 기능을 디자인하는 대신 원래 K & R 프리 프로세서의 동작을 일반화했습니다. 나는 그들이 펀치 카드 모범 사례에 익숙한 것 같지 않으며, 한두 번의 역 계산 연습을 제외하고 는 기능이 명시된 목적으로 사용 되었다는 것도 의심합니다 .
zwol

8
@supercat \uU + 0000..U + 007F 범위에서 문자를 생성하는 것이 금지 된 경우 사전 토큰 변환 으로 Java에 문제가 없습니다 . "이것은 모든 곳에서 작동합니다"와 "이것은 구문 상 의미가있는 ASCII 문자의 별명"의 조합입니다.
zwol

4
당신의 "pedant"에 대해 : 물론 당시에 //한 줄짜리 주석이 존재하지 않았습니다 . C는 새로운 라인이 아닌 문 터미네이터를 가지고 있기 때문에, 그것은 주로 지금까지의 내가 "문자열 리터럴 연결"을 결정할 수있는 것을 제외하고, 긴 문자열에 사용 될 수 있었다 K & R에서있다.
Mark Hurd

22

이것은 의도적 인 디자인 선택이었습니다. Java의 원래 디자인으로 거슬러 올라갑니다.

"코멘트에서 유니 코드 이스케이프를 원하는 사람?"을 묻는 사람들에게는 필자가 모국어가 라틴 문자 집합을 사용하는 사람들이라고 생각합니다. 다시 말해, 사람들은 Java 프로그램에서 합법적 인 곳이면 어디에서나 일반적으로 주석과 문자열로 임의의 유니 코드 문자를 사용할 수있는 Java의 원래 디자인에 내재되어 있습니다.

이러한 프로그램이 유니 코드 이스케이프를 해석 할 수없고 해당 글리프를 표시 할 수 없다는 소스 텍스트를 보는 데 사용되는 IDE (예 : IDE)와 같은 프로그램의 단점 일 수 있습니다.


8
오늘날 우리는 소스 코드에 UTF-8을 사용하며, 이스케이프 할 필요없이 유니 코드 문자를 직접 사용할 수 있습니다.
Paŭlo Ebermann

21

나는 이것이 @z 실수라는 것에 동의한다. 그러나 나는 훨씬 더 비판적입니다.

\u이스케이프는 문자열 및 문자 리터럴에 유용합니다. 그것이 존재해야하는 유일한 장소입니다. 다른 이스케이프와 같은 방식으로 처리해야합니다 \n. 그리고 "\u000A" 해야 정확히 의미한다 "\n".

\uxxxx의견을 말할 필요 는 없습니다 . 아무도 읽을 수 없습니다.

마찬가지로, \uxxxx프로그램의 다른 부분에서는 사용할 필요가 없습니다 . 유일한 예외는 아마도 비 ASCII 문자를 포함하도록 강제 된 퍼블릭 API 일 것입니다. 우리가 마지막으로 본 것은 무엇입니까?

디자이너들은 1995 년에 그 이유를 가지고 있었지만 20 년 후에는 잘못된 선택 인 것 같습니다.

(독자의 질문-왜이 질문이 계속 새로운 투표를 받습니까?이 질문은 다른 곳에서 연결 되었습니까?)


5
비 ASCII 문자가 API에 사용되는 곳에서 당신은 교수형에 처하지 않는 것 같습니다. 예를 들어 아시아 국가에서 사용하는 사람들이 있습니다. ASCII가 아닌 문자를 식별자로 사용하는 경우 문서 주석에서 해당 문자를 금지하는 것은 의미가 없습니다. 그럼에도 불구하고 토큰 내부에서 허용하고 토큰의 의미 또는 경계를 변경하도록 허용하는 것은 다릅니다.
Holger

15
적절한 파일 인코딩을 사용할 수 있습니다. int \u5431당신이 할 수있을 때 쓰는 이유int 整
ZhongYu

3
때 당신이 어떤 작업을 수행하는지 사용자가 자신의 API에 대한 코드를 컴파일해야하고 적절한 인코딩을 사용할 수 없습니다 (널리 없었다 가정 UTF-8지원 1995 년). 하나의 메소드를 호출하기 만하면 해당 단일 메소드에 대해 운영 체제의 아시아 언어 지원 팩 (90 년대를 기억하십시오)을 설치하고 싶지 않습니다…
Holger

5
1995 년보다 훨씬 더 분명한 것은 프로그램을하고 싶다면 영어를 더 잘 아는 것입니다. 프로그래밍은 국제적인 상호 작용이며 거의 모든 리소스가 영어로되어 있습니다.
ZhongYu

8
나는 이것이 바뀌 었다고 생각하지 않습니다. Java의 문서는 대부분 영어로되어있었습니다. 일본어 번역이 한동안 유지되었지만 언어를 유지한다고해서 세계의 모든 지역에 대해 유지한다는 아이디어를 실제로 뒷받침하지는 않습니다 (반증합니다). 그리고 그 전에는 식별자에서 유니 코드를 지원하는 주류 언어는 없었습니다. 내 생각 것이다 그래서, 누군가가 생각 하는 지역화 된 소스 코드는 다음 큰 일이었다. 나는 고맙게 말할 것 입니다.
Holger

11

유니 코드 이스케이프가 왜 사양을 작성했는지에 따라 구현 된 이유에 대해 대답 할 수있는 유일한 사람들입니다.

이에 대한 그럴듯한 이유는 전체 BMP를 Java 소스 코드의 가능한 문자로 허용하려는 요구가 있었기 때문입니다. 그래도 문제가 발생합니다.

  • 모든 BMP 문자를 사용할 수 있기를 원합니다.
  • BMP 캐릭터를 합리적으로 쉽게 입력 할 수 있기를 원합니다. 이를 수행하는 방법은 유니 코드 이스케이프를 사용하는 것입니다.
  • 사용자가 쉽게 읽고 쓸 수 있고 어휘도 구현하기 쉬운 어휘 사양을 유지하려고합니다.

이것은 유니 코드 이스케이프가 공격에 들어가면 엄청나게 어렵습니다. 새로운 어휘 규칙을 완전히 만듭니다.

쉬운 방법은 두 단계로 어휘를 수행하는 것입니다. 먼저 모든 유니 코드 이스케이프를 검색하고 나타내는 문자로 대체 한 다음 결과 문서를 유니 코드 이스케이프가 존재하지 않는 것처럼 구문 분석합니다.

이것의 장점은 쉽게 지정할 수 있기 때문에 사양이 더 간단 해지고 구현하기 쉽다는 것입니다.

단점은 예입니다.


2
또는 \ uxxxx 사용을 식별자, 문자열 리터럴 및 문자 상수로 제한하십시오. C11이하는 일입니다.
ninjalj 2016 년

그것은 파서 규칙을 복잡하게 만듭니다. 왜냐하면 그것들은 그것들을 정의하는 것이기 때문입니다. 그것이 제가 생각하는 이유는 그것이 그 방식의 일부입니다.
Martijn 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.