[만화가 있는 C] 5. Escape Sequence

5. 이스케이프 절차Escape Sequence

  A와 B 두 대의 컴퓨터가 통신을 이용하여 숫자 데이터를 서로 교환한다고 가정해 봅시다. 이러한 컴퓨터 통신에 사용되는 규약을 통신 규약(프로토콜, Protocol)이라고 합니다.

 통신규약Protocol: 컴퓨터 사이의 데이터 전송을 위해 두 대의 컴퓨터는 미리 정해진 약속에 따라 서로 통신합니다.

  우리가 사용할 프로토콜은 숫자만 데이터로 사용하므로, a를 시작 바이트start byte, b를 끝 바이트stop byte로 사용하기로 하였습니다. 그러므로 123을 전송하려면 아래와 같이 데이터를 전송합니다.

a123b

위 표현은 가장 왼쪽 바이트가 가장 먼저 전송될 때의 표현입니다. 왼쪽에서 오른쪽으로 데이터가 전송된다면 아래와 같이 나타내야 할 것입니다.

b 3 2 1 a

—–>

  그런데 아무런 문제없이 사용하던 이 프로토콜에 문제가 생기게 되었습니다. 영문자도 데이터에 포함시키도록 요구사항이 추가된 것입니다. 단순하게 위의 프로토콜을 사용하면, 12a3을 데이터로 전송하는 경우, 아래와 같이 전송해야 합니다.

a12a3b

하지만 위와 같은 데이터의 전송은 a를 구분하는 방법이 모호해지므로 사용할 수 없습니다. 그래서 다음과 같이 프로토콜을 개선하였습니다.

  (1) 시작 바이트와 끝 바이트는 각각 ‘ea’, ‘eb’로 나타냅니다.

  (2) e가 데이터에 포함되는 경우는 ‘ee’로 나타냅니다.

위와 같은 규칙을 사용하는 경우, 전송해야할 데이터가, 12a3인 경우는 아래와 같이 표현할 수 있습니다.

ea12a3eb

  이 표현에서는 e다음에 오는 문자를 확인함으로써 시작 바이트와 끝 바이트를 구분하는 것이 가능합니다. 보내고자 하는 데이터가 12e3a인 경우는 아래와 같이 전송합니다.

ea12ee3aeb

  이 데이터를 전송 받는 측에서는 e다음에 오는 문자를 검사하여 다음과 같이, 시작 바이트, 끝 바이트 혹은 데이터 자체 e를 구분하는 것이 가능합니다.

  (1) e 다음에 a가 오면 시작 바이트

  (2) e 다음에 b가 오면 끝 바이트

  (3) e 다음에 e가 오면 데이터 e

  실제 데이터가 주어졌을 때 e를 덧붙이는 일은 하드웨어의 특정 장치나 소프트웨어의 전송 모듈module에서 자동으로 진행할 수 있습니다. 이러한 데이터 변경 절차를 Esc 절차Escape Sequence라고 합니다.

 모호함을 탈출Escape한다는 의미로 아스키 27번 문자인 Esc를 사용한데서 붙여진 이름입니다.

  C/C++에서 스트링string은 이러한 Esc 절차를 이용하여 표현합니다. 어떠한 표현이 Esc 절차를 사용한다고 하면, 그 표현이 사용하는 Esc 문자가 무엇인지를 확인해야 하며, Esc 다음에 사용하는 약속된 문자에는 무엇이 있는지 확인해야 합니다. C++에서는 역슬래시back-slash ‘\’를 Esc 문자로 사용합니다.

 C++는 몇 개의 상수 표현을 지원하는데, 10진수, 8진수와 16진수 등이 그것입니다. 또한 문자 상수를 지원합니다. 이것은 ASCII 한개의 문자에 해당하는 수 값을 ASCII 문자로 표현하는데, 문자 앞뒤에 단일 인용 부호single quotation mark를 사용하여 나타냅니다. 예를 들면 문자 A는 ‘A’처럼 나타냅니다. 하지만, ‘A’는 문자 A가 아니라, ASCII 값인 65의 상수 표현입니다. 즉, ‘A’=65인 것입니다. Escape 절차에서는 한 개의 문자가 두개 이상의 문자로 구성되므로, \n은 ‘\n’처럼 사용해야 합니다. 이것은 비록 2개의 문자로 구성되었지만, ‘New Line’이라는 10번 문자를 나타내는 것이기 때문입니다. 만약 이러한 사실을 모르는 개발자가 \n을 “\n”처럼 사용하면 어떻게 될까요? 즉 “A”와 ‘A’의 차이점은 무엇일까요? 이러한 주제는 이어지는 장chapter에서 자세히 다룰 것입니다.

특별히 printf() 같은 몇 함수는 숫자를 나타내기 위해 ‘%’도 Esc 문자로 사용합니다. 그리고 Esc 다음에 올 수 있는 약속된 문자에는 다음과 같은 문자가 있습니다.

n, a, b, t, v

 New line, Alert(beep), Back space, Tab 그리고 Vertical tab 문자를 나타냅니다.

  예를 들어 “\aa\\\nn”을 해석해 보면, 아래와 같은 Esc 절차로 분리됩니다.

\a + a + \\ + \n + n

  그러므로 다음과 같은 의미입니다.

“삑 소리, ‘a’ 출력, ‘\’ 출력, 줄 바꿈, ‘n’ 출력”

  이러한 Esc 절차의 개념은 여러 곳에 다양하게 응용되어 사용되므로 개념을 익혀두어 우리가 필요할 때 사용할 수 있어야 합니다.

  이제 printf()함수의 기능을 다시 살펴봅시다. 화면console에 아래의 문자열을 출력하기를 원합니다.

        hello

        world

  그러기 위해서 printf()를 이용하여 다음과 같이 코딩하는 것은 잘못입니다.

        printf("hello");
        printf("world");

  위와 같은 printf()의 사용은 아래와 같이 출력합니다.

  helloworld

  위의 printf()에는 줄을 바꾼다는 표현이 빠져있기 때문에 두 개의 문자열이 붙어서 출력된 것입니다. 제대로 출력 결과를 얻기 위해서는, 줄바꿈 문자를 문자열에 포함해 주어야 합니다. 줄바꿈 문자는 ASCII 10번 문자인 ‘\n’을 사용해야 합니다. 그러므로, 아래와 같이 printf()를 구성할 수 있습니다.

  printf(“hello\nworld”);

  위의 문자열 표현 “hello\nworld”를 구성하는 문자는 모두 몇 개일까요? 단순하게 문자의 개수를 세어서, 12개라고 대답해서는 안 됩니다. \n은 10번 문자 하나를 나타내므로, 11개의 문자로 구성된 문자열입니다.

  우리는 또한 “…\n…”이 항상 10번 문자로 해석될 것을 기대해서도 안 됩니다. 아래의 소스를 봅시다.

  printf(“hello\\nworld”);

  위의 소스는 다음과 같이 출력됩니다.

  hello\nworld

  이것은 Escape 절차가 문자열의 첫 문자부터 해석되기 때문에 그렇습니다. 첫 문자부터 Escape 절차를 적용하면, \\n에서 Escape 절차로 해석되는 문자는 ‘\\’입니다.

  printf()는 현재 커서 위치에 문자열(string)을 출력합니다. 콘솔 화면의 임의의 위치에 문자열을 출력하기 위해서는 SetConsoleCursorPosition()이라는 Win32 Api(Application Programming Interface) 함수를 사용해야 합니다. 콘솔에서 커서의 위치를 지정하는 함수를 다음과 같이 작성할 수 있습니다.

void GotoXy( int x, int y )
{
    COORD p = { x, y };
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), p );
}//GotoXy()

  위의 소스는 구조체structure와 핸들handle을 사용했습니다. 아직 배우지 않은 요소들이 사용되었으므로, 지금은 ‘커서를 제어하는 방법이 있구나’라는 정도로만 이해하면 되겠습니다.


 특정한 문자를 강조(Highlight)하기

  윈도우즈 메뉴 항목menu item에서 접근 키access key로 알려진 문자값을 나타내는데, Escape 절차를 사용합니다. 예를 들면 메뉴 항목을 지정할 때 다음과 같이 지정할 수 있습니다.

“&New…”

  위와 같은 메뉴 항목은 N을 접근키로 등록하라는 지시입니다. 그러면 N은 밑줄이 그어진 채로 화면에 표시되며, 동시에 접근키로 등록됩니다. 즉 메뉴 항목의 문자열을 표현하는데 &를 Escape 절차의 Esc키로 사용하는 것입니다.

윈도우즈 메뉴 항목menu item에서의 Escape Sequence 사용: 밑줄 그어진 문자는 메뉴 항목에서 접근 키Access Key라고 합니다. 윈도우즈 프로그래밍에서 메뉴 항목의 문자열을 지정할 때, Access Key로 등록하는 문자 앞에 &를 붙입니다. “&New…” 로 메뉴 항목을 등록하면, N은 밑줄로 표시되며, 이 값은 Access Key로 등록됩니다.

  “&Hello”라고 표현한 경우, H를 특별히 강조하여 찍고, 나머지는 평범한 회색을 출력하는 문자열 출력 함수를 만들려고 합니다. 즉 &가 Esc 문자로 사용된 것입니다. 이것을 구현한 함수 PutHighlightedString()의 소스를 아래에 리스트 하였습니다.


#include <stdio.h>
#include <conio.h>
#include <windows.h>

enum EConsoleTextColor
{
    BLACK,
    BLUE,
    GREEN,
    CYAN,
    RED,
    MAGENTA,
    YELLOW,
    WHITE,
};//enum EConsoleTextColor

void GotoXy( int x, int y )
{
    COORD p = { x, y };
    SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), p );
}//GotoXy()

void SetColor( EConsoleTextColor iForegroundColor_, bool bForegroundLighten_
    , EConsoleTextColor iBackgroundColor_, bool bBackgroundLighten_ )
{
    WORD  wForeground = iForegroundColor_
    if(bForegroundLighten_ == true)
        wForeground |= 0x8;
    WORD wBackground = iBackgroundColor_ << 4;
    if(bBackgroundLighten_ == true)
        wBackground |= 0x80;
    SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), wForeground | wBackground );
}//SetColor()

void PutHighlightedString( char* pszText_, EConsoleTextColor eHighlightColor_ )
{
    const EConsoleTextColor eNormalColor = WHITE
    const EConsoleTextColor eBackColor = BLACK
    const int ciStrLength = strlen( pszText_ );
    int iPos = 0;
    while( iPos < ciStrLength )
    {
        char ch = pszText_[ iPos ];
        if( ch == '&' )
        {
            iPos += 1;
            ch = pszText_[ iPos ];
            SetColor( eHighlightColor_, true, eBackColor, false );
        }
        else
        {
            SetColor( eNormalColor, false, eBackColor, false );
        }//if.. else..
        printf( "%c", ch );
        iPos += 1;
    }//while
}//PutHighlightedString()

void main()
{
    //system( "CLS" );
    GotoXy( 5, 5 );
    PutHighlightedString( "&Hello", GREEN );
}//main()

  위 프로그램의 출력결과는 아래 그림과 같습니다.

Esc절차의 구현: “&Hello”로 표현된 문자열에서 &를 Esc문자로 간주하여 처리하였습니다.

  위의 소스는 아직 우리가 배우지 않은 많은 개념을 포함하고 있습니다. 나중에 구조체, enum, 제어구조, const, 포인터pointer 및 배열array 등을 모두 배운 다음에 소스를 살펴보기 바랍니다.

  Esc 절차는 많은 영역에서 다양하게 응용되므로, 개념과 사용법을 이해하는 것은 중요합니다. 그리고 Msdn등의 도움말을 참고하여 printf()에서
Esc 문자 다음에 예약된 문자와 그 역할을 이해하기를 바랍니다.


 C++11: 원시 문자열raw string

  C++11은 원시 문자열을 지원합니다. 원시 문자열은 다음과 같이 표현해야 합니다.

R”[id](<문자>[…])[id]”

  대문자 R다음에 “가 위치합니다. 그리고 사용자가 자유롭게 정할 수 있는 id가 위치하거나 생략할 수 있습니다. 그리고 (를 명시합니다. 여기까지가 원시 문자열의 시작을 나타내는 문자순서입니다. 그리고 문자열을 구성하는 문자들을 자유롭게 구성합니다.

  마지막에는 )를 적고, 반드시 원시 문자열의 시작을 명시할 때 사용한 id를 적어준 다음에 “를 적습니다. 아래의 표현은 모두 같은 문자열입니다.

R”idhere(Digital \r\n Contents2\r\n)idhere”

R”(Digital \r\n Contents2\r\n)”

“Digital \\r\\n Contents2\\r\\n”

  아래의 코드는 간단한 문자열 리터럴을 출력합니다. 원시 문자열의 \r\n이 Escape 절차의 줄바꿈으로 취급된 것이 아니라, 문자로 출력된 사실에 주목하세요.

#include <stdio.h>
#include <iostream>

void main()
{
    char* pText = "Digital \r\n Contents\r\n"
    char* pText2 = R"(Digital \r\n Contents2\r\n)"
    printf("%s\r\n", pText);
    printf("%s\r\n", pText2);
    /*
    Digital
     Contents

    Digital \r\n Contents2\r\n
    */
}

 실습문제

1. 위에서 문자열을 출력하는 함수 PutHighlightedString()은 “H&&ello” 처럼 연속된 & 가 사용된 것은 처리하지 않습니다. Esc 절차의 정의에 맞게 동작하도록 PutHighlightedString()함수를 수정하세요.

@

만화가 있는 C

Leave a Reply