Network2022. 10. 25. 16:32

정말 오랜만에 AWS에서 인스턴스를 띄워서 PuTTY로 접속해보려고 하는데,

콘솔에서 방금 만든 키 페어를 인스턴스에 설정해주고 접속을 하려고 해도 아래 오류로 인해 접속이 되지 않았다.

'Error: No supported authentication methods available (server sent: publickey)'

 

여러 글들을 찾아봐도 .pem을 PuTTYgen으로 변환해 사용하라거나 계정 이름을 확인하라고들 하는데,

도저히 안돼서 AWS의 CLI를 사용하려 해도 auth failed만 뱉는 답답한 상황이었다.

(루트 사용자 계정이 아니라 IAM 계정이라...)

 

EC2 접속 메뉴얼을 천천히 읽어보니 최신 버전 PuTTY를 사용하라는 말이 있어 업데이트 했더니 문제 없이 접속이 된다.

올해 초에 설치한 PuTTY(v0.74)인데 뭐가 문제였던건진 모르겠지만...

 

 

요약:

구버전 PuTTY에서 EC2에 private key를 사용한 접속이 안될 수 있으니,

username도 키 페어도 모두 확인했는데 안된다면 PuTTY를 최신 버전으로 업데이트해보자.

필자는 v0.77로 업데이트해 해결했다.

'Network' 카테고리의 다른 글

AWS를 활용한 Web/FTP Server 제작  (0) 2017.08.05
RaspberryPi를 이용한 Web/FTP 서버 구축  (0) 2017.08.04
Posted by BinZIP
Programming/Programming2018. 12. 23. 17:44

1.     컴파일 환경


OS Windows 7, Visual Studio 2015 (_MSC_VER==1900)에서 코딩 및 컴파일했다.

 

2.     연산자 설정


통상적인 계산이 아닌 @, #, &이라는 특수한 연산 기호를 사용해 a, b, c를 처리하는 것이므로 연산 자체를 어떻게 처리할 것인지부터 생각해야 했다. 그래서 내린 결론은 전역으로 각각 operationAt, operationSharp, operationAnd라는 char형 배열을 정의해 연산 결과를 매칭시킨다였다. 이를 그림으로 표현하면 아래와 같다.



이렇게 구현한다면 연산하는 과정 없이 그대로 매칭만 시켜주면 된다. 그래서 전역으로 각각 연산자마다의 연산 결과를 담은 배열을 정의했다.



계산기가 동작하기 전에, 연산자들(@, #, &)의 연산 결과를 operations.txt에서 받아와서 등록하는 과정이 필요하다. 그래서 ifstream을 활용해 파일을 읽어내 각각 배열에 대입해주었다.



위와 같이 정의하고 계산 기능을 하는 코드가 실행되기 전에 setOperation 함수를 호출한 후, 전역에 정의된 배열의 상태를 보면 operations.txt data와 일치하게 설정된 것을 볼 수 있다.



이것으로 연산자의 연산 처리에 대한 설정은 끝마쳤다.

 


3.     계산


이제 입력 받은 수식을 계산해야 한다. 입력은 cin으로 받아 string vector eq에 넣고, “EOI”를 입력 받으면 입력을 종료한다.



그리고 eq에 있는 요소를 하나씩, 계산하는 함수 Calculate에 넘겨준다.


 

Calculate 함수는 다음과 같이 이루어져있다.



isValidEq 함수에서 true를 받으면 수식이 유효하다는 의미이다. 유효할 경우는 postfix의 길이와 같은 크기로 공간을 동적으로 할당받아 그 공간에 postfix로 변환된 수식을 저장하고, 변환된 수식을 가지고 실제 계산을 진행한다.

 


A.     수식의 유효성 검사


Calculate 함수는 입력 받은 수식의 연산 결과를 출력한다. 하지만 연산을 시행해 결과를 내기 전에, 입력 받은 수식이 유효한지부터 검사해야 한다. 잘못된 수식을 즉시 연산하다가는 오류가 발생할 수 있기 때문이다.

수식의 유효성을 검사하기 위해서 isValidEq 함수를 정의했다.



유효성 검사 코드는 수식 자체가 유효한지 더 자세히 검사한다. , 괄호를 제대로 썼는지, 연산자를 연속해서 사용하지는 않았는지, 연산자로 수식이 시작하거나 끝나지는 않았는지, 그리고 연산자에 피연산자를 제대로 작성했는지이다.



좌측 괄호가 들어오면 스택에 push해놓고, 우측 괄호가 들어오면 괄호의 유효성을 검사한다. 만약 스택이 비어있으면 당연히 괄호의 쌍이 맞지 않는다는 것이므로 false return한다. 그렇지 않을 경우에는 우리가 비교하고자 하는 것은 스택에서 가장 위에 있는 좌측 괄호이다. 스택의 맨 위에 있는 괄호가 알맞은 짝인지 판단해 다르면 false return하고, 올바르면 스택에서 빼낸 후에 검사를 계속 진행한다.



연산자에 대한 처리는 먼저 연산자가 수식의 맨 앞이나 뒤에 왔는지 검사한다. 좌측 피연산자나 우측 피연산자가 존재하지 않는 수식이라는 의미이기 때문에 반드시 올바른 수식이 아니기 때문에 false return한다. 그렇지 않으면 수식의 현재 검사하고 있는 문자의 앞과 뒤의 문자로 괄호의 시작과 끝과 맞닿아 있을 경우에 피연산자가 빠지지는 않았는지, 연산자가 연속해서 오지는 않았는지 검사한다. 이 검사를 모두 통과하면 수식 검사를 계속한다.



수식 검사가 1차적으로 끝난 후엔 스택이 비어있는지 확인한다. ‘(a#a)&(a#a‘와 같은 수식은 이상의 테스트를 통과하지만, 쌍이 맞지 않는 불완전한 수식이기 때문이다.

주석 처리된 부분은 시행 착오를 여러 번 거치다 필요 없는 구문으로 판단되어 지워진 코드들이다. 먼저, a부터 c까지의 operands들이 들어오면 원래는 numCount라는 변수로 피연산자들이 몇 개나 되는지 카운트했었다. ‘a#’과 같이 1개 이하의 피연산자가 있다는 것은 구문에 오류가 있다는 의미이기 때문이다. 하지만 이는 연산자의 위치가 수식의 제일 앞이거나 뒤인지 판단하는 구문에서 이미 걸러지기 때문에 필요하지 않은 코드가 되었다. 또한, 원래는 연산자를 확인한 후에는 스택에 넣었었다. 처음에는 올바르게 사용된 연산자를 스택에 넣어놓고 나중에 다른 어떤 부분에서 확인하는데 사용할 수 있을 것이라 생각했다. 하지만 기능을 모두 구현한 다음에 살펴보니 연산자를 스택에 넣는 행위 자체가 의미 없는 코드가 되었다. 괄호를 검사할 때도 이 때 넣어놓은 연산자 때문에 따로 연산자를 모두 꺼내야 되는 코드가 추가적으로 필요했고, 연산자를 잘못 사용한 경우는 이미 함수가 false return되기 때문이었다. 그래서 연산자를 스택에 넣어두는 코드 자체를 아예 없애 불필요한 코드를 줄였다.

 


B.      Postfix로 변환


검사를 마친 수식은 postfix로 바뀌어도 문제가 없다. 스택 계산기를 구현하려면 수식을 postfix 형태로 만들어내는 것이 계산 과정에서 훨씬 편리하다. makePostfix 함수는 바꿀 수식 string을 전달받아 bufpostfix로 바뀐 수식을 저장해준다.



피연산자가 들어오면 작성하고 있는 buf의 현재 위치에 그대로 넣어준다. 연산자가 들어오면 먼저 스택이 비어있는지, 혹은 스택의 맨 위에 좌측 괄호가 있는지 확인해 그렇다면 스택에 바로 넣어준다. 그렇지 않다면 & #, @로 나누어 처리해준다. & # @보다 높은 연산 우선순위를 가지고 있다. 스택의 맨 위에 있는 연산자보다 연산 우선순위가 같거나 높은 연산자를 만나면 스택에서 맨 위에 있는 연산자를 꺼내 buf에 넣어줘야 한다. 그러므로, # @는 가장 낮은 연산 우선순위를 가지므로 같거나 높은 연산자를 만날 수 밖에 없으므로, 연산자를 만날 때 마다 스택 맨 위의 연산자를 buf에 넣고 pop한다. 그리고 &은 같은 &을 만났을 때는 @ #과 마찬가지로 바로 buf에 넣고 pop, &이 아닌 다른 연산자를 만났을 때는 항상 더 높은 연산순위를 가지므로 스택에 넣어준다.



좌측 괄호를 만나면 스택에 넣어준다. 우측 괄호를 만나면 적절한 좌측 괄호를 만날 때까지 연산자를 꺼내 buf에 넣어준다.

위의 for문이 끝났을 때 스택이 비어있으면 그대로 return, 아직 남아있으면 들어있는 연산자를 모두 꺼내 buf에 넣어주고 return한다. 이렇게 수식을 Postfix로 만들어내는 과정이 끝났다.

 


C.      Postfix문 계산


이제 스택 계산기로 계산하기 상대적으로 쉬운 postfix문까지 만들어냈으니, 계산만 해서 그 값을 출력하면 된다. Postfix문을 계산해 출력하는 함수 calcPostfix는 다음과 같다.



피연산자(a, b, c)를 만나면 스택에 넣고, 연산자를 만나면 newCalc 함수를 호출한다. newCalc 함수는 스택 맨 위의 두 피연산자를 꺼내 cursor에 해당하는 연산자로 연산 결과 값을 구해 스택에 넣어주는 함수이다.



상술했듯이, 미리 설정해놓은 배열들에서 대응하는 값을 스택에 넣어주는 것으로 연산을 진행한다.

이렇게 연산이 모두 끝나고 나면 스택의 맨 위에는 연산 결과만이 남아있을 것이다. 이 결과를 출력해주고 calcPostfix 함수를 마친다. 우리는 입력 받은 수식을 계산해 출력하는 과정까지 끝냈다.

 


4.     다른 예시 입력


-       a#(a@b&c)

-       a&b&c@(a&b&c)

-       a&&b

-       a#b#(a&c#)



모두 정상적으로 출력되는 것을 확인할 수 있었다.



GitHub : https://github.com/BinZlP/StackCalculator

Posted by BinZIP
Programming/Programming2018. 12. 23. 17:31

1.     컴파일 환경

Visual C++ 2015 (_MSC_VER==1900) 환경에서 디버그 및 컴파일했다.

 

2.     클래스 설계

MyIntVector.h 헤더 파일의 소스코드를 일부씩 가지고 와서 설명하겠다. 크게 멤버변수, 생성자, 멤버함수로 나누어 설명한다. 생성자와 멤버함수의 precondition postcondition을 헤더 파일에 작성하였으나, precondition을 만족시키지 못할 경우의 예외처리를 최대한 하는 방향으로 코드를 작성했다.

 

A.     멤버변수



멤버 변수는 데이터를 담는 int*형 변수 data data의 최대 수용할 수 있는 원소의 개수를 값으로 가지는 capacity, 그리고 저장된 원소의 개수를 값으로 갖는 size로 구성했다.

 

B.      생성자 및 소멸자



생성자는 총 2개로, 첫 번째 생성자는 정수를 전달받아 그 정수를 capacity의 값으로 초기화한다. 이 생성자는 기본값으로 DEFAULT_CAPACITY를 가지므로 아무것도 전달되지 않았을 때는 매크로 상수로 정의된 DEFAULT_CAPACITY의 값으로 capacity 값을 초기화 하게 된다. 현재 설정해놓은 DEFAULT_CAPACITY 32이다.



나머지 한 생성자는 copy constructor이다. 인자로 MyIntVector의 참조자를 받아 그 참조하는 MyIntVector와 완전히 동일한 새로운 객체를 만든다. , 생성되는 객체의 data에 참조한 data deep copy하고 capacity size를 그대로 복사한다.

마지막으로 소멸자는 동적 할당된 data delete해준다.

 

C.      연산자


연산자는 =,+=, [], +, -, *, -(Unary), ==, ()를 재정의했다.


= 연산자이다. 좌항에 있는 MyIntVector 객체에 우항의 MyIntVector deep copy한다. 좌항과 우항에 같은 MyIntVector이면 불필요한 복사 작업을 건너뛰기 위해 분기를 넣어주고, capacity가 다를 때만 data를 다시 할당 받도록 했다.

return 타입을 MyIntVector& 타입으로 하는 이유는 a=b=c와 같이 연쇄적으로 사용할 수 있도록 하기 위해서뿐만이 아니다. 만약 단순히 참조자 타입이 아니라 value return하면, = 연산자가 return될 때 복사 생성자로 결과 객체와 동일한 객체를 생성해낸다. , 호출될 때마다 생성자와 소멸자가 호출된다는 것이다. 지금 우리가 구현하는 간단한 클래스에서는 value return하더라도 당장 큰 문제는 없겠지만, 다른 복잡한 클래스를 구현했을 때 불필요한 오버헤드가 많이 들 수 있다.



+= 연산자는 좌항의 MyIntVector에 우항의 MyIntVector element를 이어 붙인다. 만약 우항의 객체가 비어있다면 return하고, 이어 붙일 공간이 부족할 경우 추후 설명할 reserve 함수를 호출해 capacity를 확장했다. 처음에 설계했을 때는 공간을 2배로 확장하고 이어 붙이는 작업을 진행하도록 했는데, 좌항 객체의 capacity 2배를 해도 우항 객체의 데이터를 모두 담아내지 못할 정도로 우항 객체에 데이터가 많을 때 오류가 발생했다. 그래서 충분히 확보될 때까지 2배씩 확장하는 작업을 반복하도록 코드를 구성했다.



+, -, * 연산자는 두 MyIntVector의 스칼라 합, , 곱 연산을 한다. 이 때, MyIntVector는 가지고 있는 element의 개수가 동일해야 연산이 가능하므로, 다를 때에는 오류로 판단하도록 코드를 작성하였다. 연산 결과를 tmp에 저장해 return한다.

처음에 함수를 작성할 때는 해당 연산자를 사용할 때마다 a=a+b와 같은 형식으로 사용할 것으로 생각했다. 그래서 return tmp가 대입되어 들어가게 될 경우, a data memory leak을 유발할 가능성이 있다고 생각해 a의 소멸자를 호출해줘야 한다고 생각했다. 하지만 애초에 =연산자는 좌항의 객체에 deep copy하는 것이라 아무 상관 없었다. 이런 생각이 든 이후엔 a+b의 결과로 return되는 tmp도 소멸자를 호출하지 않으면 memory leak을 유발하지 않을까 하는 생각이 들었는데, tmp는 대입 연산이 끝난 후에 함수의 지역 변수이므로 free될 것이므로 그럴 필요 없다는 결론을 내려 위와 같이 코드를 구성했다.



(Unary)- 연산자는 MyIntVector 객체의 data 안의 모든 element 값의 부호를 바꾼다. 비어있을 경우는 할게 없으므로 그대로 return하고, 그 외의 경우는 모든 원소에 -1을 곱한 값을 가지는 tmp return한다.



== 연산자는 두 객체가 완전히 동일한 데이터를 가지는지 확인한다. 두 객체의 size가 다르면 당연히 다른 객체이므로 false return한다. Size가 같으면 데이터를 하나하나 대조해서 서로 다른 값이 발견되면 false return하고, 모든 element가 같으면 true return한다.



() 연산자는 호출한 객체의 모든 element 값을 전달받은 x의 값으로 바꾼다. for문으로 객체의 모든 저장된 data의 값에 x를 대입해 return한다.



마지막으로 [] 연산자는 x번째 인덱스에 있는 element 값을 반환한다. 만약 x값이 음수이거나 element의 인덱스 범위를 넘어갈 경우는 오류이므로 프로그램을 종료하도록 했다.

 

D.     멤버함수

멤버함수는 pop_back, push_back, capacity, size, reserve, isEmpty, clear 함수를 만들었다.



pop_back 함수는 MyIntVector 객체 데이터의 맨 뒤에 있는 element를 제거하는 함수이다. 만약 데이터가 비어있으면 아무것도 하지 않고, 그 외의 경우는 size의 값을 1 줄이는 것으로 맨 뒤의 element 제거를 구현했다.



push_back 함수는 MyIntVector 객체에 데이터를 하나 삽입한다. 전달받은 x를 데이터의 맨 뒤에 추가하는 함수이다. 만약 객체에 데이터를 담을 공간이 부족하면 reserve 함수를 이용해 공간을 확보한 뒤 데이터를 삽입한다.



isEmpty 함수는 데이터가 없으면 true, 하나라도 존재하면 false를 반환한다.



멤버함수 중에서 가장 중요하다고 할 수 있는 reserve 함수는 MyIntVector capacity를 확장해주는 함수이다. 먼저 reserve 하려는 크기가 지금 저장되어 있는 element의 개수보다 작을 경우는 데이터를 잘라내려는 의도보다 잘못된 값을 입력했을 가능성이 더 높다고 판단해 그대로 return했다.

새로운 n 크기의 int형 배열을 동적으로 할당해, 새로운 배열에 기존 데이터를 copy한다. 마지막으로 새로운 배열의 주소를 data에 저장하고 기존 data delete 해주는 것으로 확장 작업을 마친다.



clear 함수는 MyIntVector 객체의 모든 데이터를 삭제한다. size 0으로 만들어주면 pop_back으로 모든 element를 제거한 것과 같은 것이므로 size 0으로 설정해줬다.



Capacity, Size 함수는 객체의 capacity 값 혹은 size 값을 반환하는 함수이다.

 


추가적으로, 이후 테스트를 진행할 때 데이터의 상태를 쉽게 확인하기 위해 데이터를 한 개씩 출력해주는 멤버함수 printElements를 만들었다.

 

3.     Main 함수


작성한 모든 연산자 및 함수가 정상적으로 동작하는지 테스트하는 코드를 작성했다.



설명하기 앞서 테스트하는 도중에 MyIntVector의 상태를 쉽게 볼 수 있도록 만든 printVectorInfo 함수를 정의해 사용했다.



먼저 MyIntVector타입 객체 vector1을 정의해 vector1의 상태를 살펴보았다.



아무것도 들어있지 않고 capacity DEFAULT_CAPACITY(==32) MyIntVector가 생성되었다.




그 후 vector1 0부터 4까지 값을 차례로 가지는 elementpush_back으로 삽입했다. vector1의 상태를 출력한 후 data 0번째 인덱스 값, 1번째 element를 출력하게 했다.



0부터 4까지 차례로 삽입되었고, 0번째 인덱스 값은 0이 맞으므로 push_back []연산자가 정상적으로 동작함을 알 수 있다.




vector2 vector1과 동일하게 생성했다.



vector2 vector1과 완전히 동일하게 생성되었음을 확인했다.




vector2 vector1을 이어 붙이는 += 연산자를 사용하고 vector2의 상태를 확인했다.



0부터 4까지의 원소가 2번 반복되는 것을 보아 정상적으로 이어 붙여졌으므로, +=연산자가 잘 동작함을 확인할 수 있다.




vector3을 생성해, 대입 연산자를 연쇄적으로 사용해보았다. 그 후, vector3 vector1 ==연산자로 비교했다.



대입 연산자를 연쇄적으로 사용해도 정상적으로 동작하며, 비교 연산자인 == 연산자도 정상적으로 동작함을 알 수 있다.




vector327개의 0을 삽입해 size 32로 만들고, 0을 하나 더 삽입하는 코드를 작성해 vector3의 상태를 확인했다.



size 32일 때 0을 하나 더 삽입했더니 capacity 2배인 64로 늘어났다. push_back을 사용했을 때 자동으로 reserve됨을 볼 수 있었다.




vector3의 모든 element 10으로 만들고, vector4 vector3과 똑같게 생성한 후 모든 원소를 1로 바꾸었다. 다음, vector3에서 vector4를 스칼라 뺄셈을 진행하고 vector3의 상태를 확인했다.



10-1 9 33개의 element가 모두 설정되어 있는 것을 보아, ()연산자와 연산자가 잘 동작함을 확인할 수 있다.




마찬가지로 + * 연산자도 테스트 해보았다.



1+1 2이고, 2*9 18이므로 모두 잘 동작함을 확인할 수 있다.




(Unary)- 연산자도 잘 동작하는지 테스트 해보았다.



18에서 -18로 바뀌어서 잘 출력되므로 (Unary)- 연산자가 잘 동작함을 확인했다.




vector3에서 한 elementpop_back으로 제거하고 vector3의 상태를 확인한 후, reserve 함수로 capacity 32로 바꾼 후 vector3의 상태를 확인한다.



Capacity 32로 줄어들었다. pop_back reserve 함수가 잘 동작하는 것을 볼 수 있다.




마지막으로 clear함수를 사용해 데이터를 모두 없애고, isEmpty로 데이터가 없는지 확인한다.



vector3에 있는 element들이 모두 제거되었다. 그러므로 clear 함수와 isEmpty 함수가 잘 동작함을 확인했다.



GitHub: https://github.com/BinZlP/MyIntVector



'Programming > Programming' 카테고리의 다른 글

[C++] 스택을 활용한 수식 계산기  (0) 2018.12.23
[C++] 함수 및 다중 정의 (Overload)  (1) 2018.01.21
[C++] Try 구역과 예외 처리  (0) 2018.01.15
[C++] 문자열 & 벡터  (0) 2018.01.13
[C++] 변수와 기본 타입  (0) 2018.01.11
Posted by BinZIP