codeforces에서 몇 가지 문제를 해결하고있었습니다. 일반적으로 먼저 문자가 영문 또는 대문자인지 확인한 다음 빼거나 추가 32
하여 해당 문자로 변환하십시오. 그러나 나는 누군가가 ^= 32
똑같은 일을한다는 것을 알았 습니다. 여기있어:
char foo = 'a';
foo ^= 32;
char bar = 'A';
bar ^= 32;
cout << foo << ' ' << bar << '\n'; // foo is A, and bar is a
이에 대한 설명을 검색했지만 찾지 못했습니다. 왜 이것이 효과가 있습니까?
답변
ASCII 코드 테이블을 바이너리로 살펴 보자.
A 1000001 a 1100001
B 1000010 b 1100010
C 1000011 c 1100011
...
Z 1011010 z 1111010
32는 0100000
소문자와 대문자의 유일한 차이점입니다. 따라서 비트를 토글하면 문자의 대소 문자가 토글됩니다.
답변
이것은 실제로 똑똑한 사람들이 선택한 ASCII 값보다 사실을 사용합니다.
foo ^= 32;
이 제 6 최하위 비트 뒤집 하나 의 foo
하부 케이스에 ASCII 상부 케이스 변형 (ASCII의 정렬의 대문자 플래그)을 그 반대 .
+---+------------+------------+
| | Upper case | Lower case | 32 is 00100000
+---+------------+------------+
| A | 01000001 | 01100001 |
| B | 01000010 | 01100010 |
| ... |
| Z | 01011010 | 01111010 |
+---+------------+------------+
예
'A' ^ 32
01000001 'A'
XOR 00100000 32
------------
01100001 'a'
XOR의 속성에 따라 'a' ^ 32 == 'A'
.
주의
C ++은 ASCII를 사용하여 문자를 나타내지 않아도됩니다. 또 다른 변형은 EBCDIC 입니다. 이 트릭은 ASCII 플랫폼에서만 작동합니다. 더 휴대용 솔루션을 사용하는 것 std::tolower
과 std::toupper
(의견을 참조하지만 그것은 자동적으로 모든 문제가 해결되지 않음) 로케일 인식하기 위해 제공되는 보너스 :
bool case_incensitive_equal(char lhs, char rhs)
{
return std::tolower(lhs, std::locale{}) == std::tolower(rhs, std::locale{}); // std::locale{} optional, enable locale-awarness
}
assert(case_incensitive_equal('A', 'a'));
1) 32는 1 << 5
(2에서 5로) 6 번째 비트를 뒤집습니다 (1부터 계산).
답변
이것이 똑똑해 보이지만 실제로는 정말 어리석은 핵이라고 말할 수 있습니다. 누군가 2019 년에 당신에게 이것을 추천한다면, 그를 때리십시오. 당신이 할 수있는 한 열심히 그를 때려.
물론 어쨌든 영어 이외의 다른 언어를 사용하지 않을 것이라는 것을 알고 있다면 자신과 다른 사람이 사용하지 않는 소프트웨어를 자신의 소프트웨어에서 사용할 수 있습니다. 그렇지 않으면 갈 수 없습니다.
해킹은 컴퓨터가 정말하지 않았다 ASCII에 많이 있지만 영어를 수행 할 때 일부 30~35년 전에 “OK”논쟁의 여지가, 그리고 어쩌면 하나 또는 두 개의 주요 유럽 언어. 하지만 … 더 이상은 그렇지 않습니다.
해킹은 US-Latin 대문자와 소문자가 서로 정확히 0x20
떨어져 있고 동일한 순서로 나타나기 때문에 한 비트 차이이므로 작동합니다. 실제로이 비트 핵은 토글됩니다.
이제 서유럽 및 나중에 유니 코드 컨소시엄을위한 코드 페이지를 작성하는 사람들은 독일 움라우트 (Umlauts) 및 프랑스 식 모음과 같은 체계를 유지할만큼 똑똑했습니다. 그리 (사람이 2017 년 유니 코드 컨소시엄을 확신 할 때까지, 실제로 Duden 설득, 그것에 대해 쓴 많은 가짜 뉴스 인쇄 잡지 – 그에 노 코멘트)을 ß에 대한 도 존재하지 않는 versal로 (SS로 변환) . 지금은 않는 등 versal 존재하지만, 두는 0x1DBF
떨어져 위치하지 0x20
.
그러나 구현 자들은 이것을 계속할만큼 충분히 배려 하지 않았다 . 예를 들어, 동유럽 언어 등에서 해킹을 적용하면 (키릴 자모에 대해서는 몰랐습니다) 놀라 울 정도입니다. 이러한 “도끼”문자는 그 예이며 소문자와 대문자는 서로 다릅니다. 따라서 해킹이 제대로 작동 하지 않습니다 .
예를 들어, 일부 문자는 단순히 소문자에서 대문자로 변환되지 않거나 (서로 다른 시퀀스로 대체 됨) 형식이 변경 될 수 있습니다 (서로 다른 코드 포인트 필요).
이 핵이 태국이나 중국과 같은 것들에 대해 어떻게 할 것인지 생각조차하지 마십시오 (완전한 말도 안됩니다).
수백 개의 CPU 사이클을 절약하는 것은 30 년 전에는 매우 가치가 있었지만 현재는 문자열을 올바르게 변환 할 수있는 변명이 없습니다. 이 사소한 작업을 수행하기위한 라이브러리 기능이 있습니다. 오늘날
수십 킬로바이트의 텍스트를 올바르게 변환하는 데 걸리는 시간 은 무시할 만합니다.
답변
ASCII와 파생 인코딩에서 ‘a’와 A ‘의 차이는 32이고 32는 6 번째 비트의 값이기 때문에 작동합니다. 배타적 OR로 6 번째 비트를 뒤집 으면 상한과 하한 사이에서 변환됩니다.
답변
문자 집합의 구현은 ASCII 일 가능성이 큽니다. 우리가 테이블을 보면 :
우리 32
는 소문자와 대문자의 값이 정확히 다르다는 것을 알 수 있습니다. 따라서 우리가 할 경우 ^= 32
(6 번째 최하위 비트를 토글하는 것과 동일) 소문자와 대문자 사이에서 변경됩니다.
문자뿐만 아니라 모든 기호와 함께 작동합니다. 6 번째 비트가 다른 각각의 문자로 문자를 토글하여 한 쌍의 문자가 앞뒤로 토글됩니다. 문자의 경우 각각의 대문자 / 소문자가 이러한 쌍을 형성합니다. A NUL
는 Space
다른 방향으로 바뀌고 @
백틱으로 토글됩니다. 기본적으로이 차트의 첫 번째 열에있는 모든 문자는 한 열 위에있는 문자로 전환되며 세 번째 및 네 번째 열에도 동일하게 적용됩니다.
그래도 어떤 시스템에서도 작동한다는 보장이 없기 때문에이 핵을 사용하지는 않을 것입니다. 그냥 사용 의 ToUpper 및 tolower를을 대신하고, 같은 쿼리 isupper .
답변
여기에 이것이 작동하는 방법을 설명하는 좋은 답변이 많지만 왜 이렇게 작동하는지는 성능을 향상시키는 것입니다. 비트 단위 연산은 프로세서 내 대부분의 다른 연산보다 빠릅니다. 대소 문자를 결정하는 비트를 보지 않고 단순히 비트를 뒤집어 대 / 소문자를 대 / 소문자로 변경하여 대소 문자를 구분하지 않는 비교를 신속하게 수행 할 수 있습니다 (ASCII 테이블을 디자인 한 사람들은 꽤 똑똑했습니다).
분명히 이것은 빠른 프로세서와 유니 코드로 인해 1960 년 (ASCII에서 작업을 시작했을 때)으로 돌아 왔을 때 오늘날 큰 문제는 아니지만 여전히 상당한 차이를 만들 수있는 저렴한 프로세서가 있습니다. ASCII 문자 만 보장 할 수있는 한.
https://ko.wikipedia.org/wiki/Bitwise_operation
간단한 저비용 프로세서에서는 일반적으로 비트 단위 연산이 나누기보다 훨씬 빠르며 곱셈보다 몇 배 빠르며 때로는 덧셈보다 훨씬 빠릅니다.
참고 : 여러 가지 이유로 (가독성, 정확성, 이식성 등) 문자열 작업에 표준 라이브러리를 사용하는 것이 좋습니다. 성능을 측정했으며 이것이 병목 현상 인 경우에만 비트 뒤집기를 사용하십시오.
답변
이것이 ASCII가 작동하는 방식입니다.
그러나 이것을 이용하면 C ++이 ASCII를 인코딩으로 주장하지 않기 때문에 이식성 을 포기합니다 .
이것이 함수 std::toupper
와 std::tolower
C ++ 표준 라이브러리에서 구현되는 이유입니다. 대신이 함수 를 사용해야합니다.