Section 11.2: 문자열을 올바른 방법으로 뒤집기 (reverse)

많은 경우 문자열을 뒤집는 (reverse) 동작이 필요로 할 때, 아래와 같은 방법을 사용하게 된다:

char[] a = s.ToCharArray(); System.Array.Reverse(a); string r = new string(a);

그러나, 많은 사람들이 이러한 구현 방식에서 간과하고 있는 사실은 이러한 접근법 자체가 올바른 방법이 아니라는 것이며, 이는 NULL 체크가 빠졌다거나 하는 이유에 기인한 것이 아니다.

위 방식이 올바르지 않은 이유는 바로 Glyph 혹은 Grapheme Cluster 가 여러개의 code point (character 라고도 불리는) 로 이루어져 있을 수 있기 때문이다.

왜 이것이 문제가 되는지를 이해하려면, 우선적으로 우리가 일반적으로 "문자 (character)" 라고 지칭하는 용어가 실제로 어떤 것을 의미하는지를 알아야 한다.

인용 문서:

Character 라는 것은 overload 된 용어로써 여러가지를 의미할 수가 있다.
Code point 는 정보를 표현하는 가장 작은 단위이며, 텍스트라는 것은 연속된 code point 의 집합이다. 각각의 code point 는 유니코드 표준에 의해 의미가 부여된 숫자를 나타내게 된다.
Grapheme 이라는 것은 하나 혹은 그 이상의 연속된 code point 의 집합으로써, 읽는 사람으로 하여금 기록 시스템상에 존재하는 단일 요소로 인식하게 만드는 하나의 기호화된 단위이다. 예를 들어, aä 는 모두 grapheme 이나, 이들은 복수개의 code point 로 이루어져 있을 수 있다 (예를 들어, ä 는 기반이 되는 글자 a 와 diaresis 라고 하는 상단 점 두개를 나타내는 기호 글자 두개의 code point 로 이루어져 있을 수 있다; 그러나 해당 grapheme 을 나타내는 대안의, legacy 인, 단일 code point 역시 존재한다). 일부 code point 들은 어떠한 grapheme 에도 속하지 않는 경우도 있다 (예: zero-width non-joiner, 혹은 directional override 들).
Glyph 는 일종의 이미지로써, 많은 경우 font (glyph 들의 모음인) 에 저장되어 있으며, grapheme 들 혹은 그 일부를 표현하기 위해 사용된다. 폰트들은 복수개의 glyph 를 조합하여 하나의 표현을 나타낼 수 있는데, 예를 들어 위에서 설명한 ä 가 만약 단일 code point 라고 할 때, 폰트가 두개의 공간적으로 중첩된 glyph 를 이용하여 렌더링을 하도록 선택할 수 있다. OTF 의 경우에는, 폰트 내의 GSUB 과 GPOS 테이블이 이러한 용도로 사용될 수 있는 치환/배치 정보를 가지고 있게 된다. 또한 폰트는 하나의 grapheme 에 대하여 복수개의 대체 가능한 glyph 들을 가지고 있을 수도 있다.

따라서 C# 에서는, 문자 (character) 는 실제적으로 Code Point 를 의미한다.

이것이 뜻하는 바는, 만약 사용자가 Les Misérables 와 같은 유효한 문자열을 아래에 나타난대로 문자들의 배열로 표현하여 뒤집으려고 할 경우,

string s = "Les Mise\u0301rables";

아래와 같은 결과를 얻게 될 것이다:

selbaŕesiM seL

위에서 볼 수 있듯이, 강세가 문자 e 가 아닌 r 에 가 있음을 확인할 수 있다.

비록 char 배열을 두번 뒤집는 string.reverse.reverse 가 원래의 문자열을 정상적으로 반환한다 할지라도, 이러한 방식의 뒤집기는 원본 문자열을 뒤집는 올바른 방법이라고 볼 수 없다.

실제 수행이 되어야 할 작업은 각 Grapheme Cluster 를 따로 뒤집는 것이다. 따라서, 제대로 된 방법이라고 한다면 다음과 같이 뒤집을 수 있을 것이다:

private static System.Collections.Generic.List < string > GraphemeClusters(string s) { System.Collections.Generic.List < string > ls = new System.Collections.Generic.List < string > (); System.Globalization.TextElementEnumerator enumerator = System.Globalization.StringInfo.GetTextElementEnumerator(s); while (enumerator.MoveNext()) { ls.Add((string) enumerator.Current); } return ls; } private static string ReverseGraphemeClusters(string s) { if (string.IsNullOrEmpty(s) || s.Length == 1) return s; System.Collections.Generic.List < string > ls = GraphemeClusters(s); ls.Reverse(); return string.Join("", ls.ToArray()); } public static void TestMe() { string s = "Les Mise\u0301rables"; // s = "noël"; string r = ReverseGraphemeClusters(s); // 다음은 잘못된 방식을 나타낸다: // char[] a = s.ToCharArray(); // System.Array.Reverse(a); // string r = new string(a); System.Console.WriteLine(r); }

이러한 방식으로 올바른 뒤집기를 구현하였다면,이는 아시아 / 동남아시아 언어들을 비롯한 불어 / 스웨덴어 / 노르웨이어 등등 다양한 언어에 대해서도 정상 동작함을 확인할 수 있을 것이다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 11.1: 문자열 서식 (format) 사용하기

String.Format()메소드를 이용하면 문자열 내의 하나 이상의 항목들을 문자열 형식으로 표현된 특정 객체들로 대체할 수 있다:

String.Format("Hello {0} Foo {1}", "World", "Bar") //Hello World Foo Bar
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 10.4: 다중행 (multi-line) 문자열 처리하기

var multiLine = @"This is a multiline paragraph";

출력 결과:

This is a multiline paragraph

.NET Fiddle 에서 라이브 데모 확인하기

축자 문자열을 이용하면 쌍따옴표가 다중행 (multi-line) 문자열에 포함된 경우에도 하나의 행에 쌍따옴표가 포함되었을때처럼 escape 처리가 가능하다.

var multilineWithDoubleQuotes = @"I went to a city named ""San Diego"" during summer vacation.";

.NET Fiddle 에서 라이브 데모 확인하기

여기서 주의해야 할 점은 2번째와 3번째 행 시작 부분의 들여쓰기를 위한 공백문자들 역시 변수 내용에 실제 포함이 되어버린다는 것이다; 이 질문 게시글 을 참고하여 이러한 경우에 대한 해결책을 찾아볼 수 있다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 10.3: 축자 문자열을 이용하여 컴파일러로 하여금 escape 문자를 사용하지 않도록 하기

일반 문자열에서 역슬래시 문자는 escape 문자로써, 컴파일러로 하여금 다음 문자나 문자들을 확인한 후 해당 문자열 내에서 실제 출력될 문자를 결정하게 만드는 역할을 한다.

축자 문자열 (verbatim string) 에서는 escape 문자라는 것이 존재하지 않는다 (" 로의 변환을 위한 "" 는 예외로 한다). 축자 문자열 사용을 위해서는, 문자열 시작 따옴표 앞에 @ 을 붙이기만 하면 된다.

아래의 축자 문자열은

var filename = @"c:\temp\newfile.txt"

다음과 같은 결과를 출력한다:

c:\temp\newfile.txt

대조적으로, 일반 (축자 문자열이 아닌) 문자열의 경우에는:

var filename = "c:\temp\newfile.txt"

다음과 같은 결과를 출력하게 된다:

c: emp ewfile.txt

이는 escape 문자가 사용되었기 때문인데, \t 는 tab 문자로 변환되며 \n 은 줄바꿈 문자로 변환된다.

.NET Fiddle 에서 라이브 데모 확인하기

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 10.2: 쌍따옴표 escape 처리하기

축자 문자열 (verbatim string) 내의 쌍따옴표의 경우, 두개의 연속된 쌍따옴표 "" 를 이용하여 실제 결과 문자열 상에 하나의 쌍따옴표 " 를 출력하도록 escape 처리할 수 있다.

var str = @"""I don't think so,"" he said."; Console.WriteLine(str);

출력 결과:

"I don't think so," he said.

.NET Fiddle 에서 라이브 데모 확인하기

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 10.1: 보간된 축자 문자열 (Interpolated Verbatim String)

축자 문자열 (Verbatim string) 은 C#6 에서 새롭게 추가된 문자열 보간 (String interpolation) 기능과 혼합하여 사용할 수 있다.

역주: 아래 예제에서 "" 앞에 붙은 $ 은 보간 문자열을, @ 은 축자 문자열을 사용할 것임을 나타냅니다.

Console.WriteLine($@"Testing \n 1 2 {5 - 2} New line");

결과:

Testing \n 1 2 3 New line

.NET Fiddle 에서 라이브 데모 확인하기

축자 문자열 사용에서 기대되는 동작대로, 역슬래시 \ 는 escape 문자로 취급되지 않는다. 또한 보간 문자열 사용으로 인해 기대되는 동작대로, 중괄호로 둘러싸여진 표현식은 해당 위치에 문자열로 삽입되기 전에 실제 값이 평가 (evaluate) 된다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 9.8: 정규화된 이름 (qualified identifier) 에 nameof 이용하기

코드

Console.WriteLine(nameof(CompanyNamespace.MyNamespace)); Console.WriteLine(nameof(MyClass)); Console.WriteLine(nameof(MyClass.MyNestedClass)); Console.WriteLine(nameof(MyNamespace.MyClass.MyNestedClass.MyStaticProperty));

콘솔 출력

MyNamespace MyClass MyNestedClass MyStaticProperty
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 9.7: 인자 이름 출력하기

코드

public void DoSomething(int paramValue) { Console.WriteLine(nameof(paramValue)); } ... int myValue = 10; DoSomething(myValue);

콘솔 출력

paramValue
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

+ Recent posts