수행 시간 구하기(clock 함수)

 

여러분이 짠 프로그램의 얼마나 효율적으로 동작하는지 궁금하다거나 회사에서 솔루션을 개발하다가 본인의 함수가 얼마나 빠르게 동작하는 지 확인해볼 필요가 있습니다. 그럴때 프로그램이 얼마나 실행됐는지 궁금하시다면 clock()함수로 구할 수가 있습니다. clock함수는 아래와 같이 정의가 되어있습니다. time.h 헤더파일을 include해야함을 알 수가 있네요.

 

#include <time.h>
clock_t clock(void);

 

이 함수에 대해서 프로그램 수행시간은 아래에서 실제 구현하긴 할 것인데, 우선 이 함수에 대해서 설명부터 하도록 하겠습니다.

clock함수는 clock_t라는 값을 반환하는데 이 값은 CPU가 사용된 값을 나타냅니다. 정확히는 시간이 아닌 clock수를 반환하지요. 초 단위로 구하려면 초당 클록수를 알아야하는데 아래의 매크로를 사용하면 됩니다.

CLOCKS_PER_SEC

이처럼 초 단위로 바꾸고 싶다면 CLOCKS_PER_SEC라는 매크로로 나누어서 초단위로 구할 수 있습니다. clock수를 어떤 이유로 얻어올 수 없다면 (clock_t) -1 이 반환됩니다.

또한 이 함수는 Thread-Safety한 특징을 갖습니다.

이제 정말 프로그램의 수행시간을 알아내는 코드를 보도록 합시다. 아래의 코드는 for루프를 수행한 수행시간을 구합니다. 1초 이내의 끝이 날 것이기 때문에 보다 밀리-세컨 단위의 더 정확한 시간을 측정하기 위해서 clock_t 자료형에서 double로 형변환을 한 것입니다.

 

#include <time.h>
#include <stdio.h>

int main(void)
{
    int  i;
    double start, end;

    //for 루프 시작 시간
    start = (double)clock() / CLOCKS_PER_SEC;    

    //for루프 100000000번 돌아보기
    int sum = 0;
    for (i = 0; i < 100000000; i++) {
        sum++;
    }

    //for 루프 끝난 시간
    end = (((double)clock()) / CLOCKS_PER_SEC);
    printf("프로그램 수행 시간 :%lf\n", (end-start));
}

 

결과 

 

수행 결과

 

제 컴퓨터가 좋은 편이 아니라 0.436초 걸리고 난 후에 프로그램이 종료되었음을 알 수 있네요. 프로그램 수행시간을 측정하는 방법은 이처럼 clock() 함수를 이용해서 구해낼 수 있습니다. 이렇게 우리가 만든 알고리즘이 얼마나 걸리는지도 확인해볼 수 있겠습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,

문자열을 숫자로 변환하는 함수

문자열을 숫자 자료형으로 변환하려면 어떤 방법으로 변환할 수 있을까요? 만약 입력이 숫자의 문자열이라고 가정한다면 아래의 코딩으로 숫자로 변환할 수 있습니다.

#include <stdio.h>

int my_atoi(const char* str) {
	int ret = 0;
	int i;
	for (i = 0; str[i] != '\0'; i++)	//NULL문자만나면 for문 종료
		ret = ret * 10 + (str[i] - '0');	//숫자 문자에 '0'을 빼면 레알 숫자가 구해짐
	return ret;
}
int main() {
	int str = "12345";
	printf("%d\n", my_atoi(str));

}

 

문자 하나에서 '0'을 빼면 이것이 곧 숫자 0이 된다는 점을 이용한 코드이지요. 사실 이렇게 간단하게 짤 수도 있겠지만 문자열에 숫자말고도 알파벳같은 글자가 섞여 들어오면 처리가 필요하게 됩니다. 코드짤때 에러 처리하는 것은 여간 귀찮은 것이 아니지요. 위 처럼 접근하는 것은 좋긴하지만 급할때 삽질하지 말라고 아래의 함수들을 C언어 라이브러리에서 제공해줍니다.

 

지금 소개해드릴 함수들이 그런 함수들입니다. 아래의 함수들을 사용하기 위해서는 stdlib.h 헤더파일을 우선 include시켜줘야한다는 점 기억하시구요.

 

atoi(Ascii To Integer) - int로 변환

#include <stdlib.h>
int atoi(const char* str);

atoi함수는 문자열을 정수형으로 변환시켜주는 함수입니다. 함수명의 앞글자 a는 Ascii를 의미합니다. 이후의 모든 함수의 a는 ascii의 a라는 점은 알아두시면 나중에 배운척 할 수 있습니다. 이 함수가 받는 str 문자열에서 숫자가 아닌 글자들은 모두 무시됩니다.

변환할 수 없는 값이라면 0을 반환합니다.

 

atol(Ascii To Long) - long으로 변환

atoll(Ascii To Long Long) - long long int로 변환

#include <stdlib.h>
long int atol(const char *str);
long long int atoll(const char *str);

long 형태로 입력을 받고 싶다면 atol 함수를 이용하면 됩니다. 이것도 모자라 long int의 범위를 넘어서는 큰 수를 입력받고 싶을때가 있습니다. 보다 큰 범위의 수를 변환하기를 원한다면 atoll 함수를 이용하여 변환할 수 있습니다. 

반환할 수 없으면 0L을 반환합니다.

 

atof(Ascii To Float) - 부동소수점 값으로 변환

#include <stdlib.h>
double atof(const char *str);

정수형말고도 소숫점으로 변환할 수 있는 함수도 있습니다. atof라는 함수이지요. 전달받은 문자열을 부동 소수점 값으로 변환해주는 함수입니다. 여기서 float자료형을 반환하는 게 아닌 double 자료형으로 return 한다는 것을 유의하세요. 

만약 함수를 통해서 반환할 수 없다면 반환값은 0이 됩니다.

 

단순한 설명보다는 무엇보다 어떻게 사용하는지 코드를 보면서 이해를 하는게 좋습니다. 

아래의 코드는 기본적인 함수들의 사용법을 보여주는 예제입니다.

#include <stdio.h>
#include <stdlib.h>
int main() {
	char* ll_str = "987654321123456789";
	char* i_str = "1234";
	char* f_str = "1234.567";
	//long long 자료형 출력
	printf("%lld\n", atoll(ll_str));

	//정수형 10진수로 출력
	printf("%d\n", atoi(i_str));

	//부동 소수점으로 출력
	printf("%.3f\n", atof(f_str));

	i_str = "1234에 문자열 꼽사리";
	printf("%d\n", atoi(i_str));

}

 

결과 화면

 

위 결과에서도 확인할 수 있듯이 "1234"에 다른 문자열 "에 문자열 꼽사리"를 섞어도 숫자만 추출해내는 것을 알 수 있습니다. 

이처럼 C언어에서는 문자열을 숫자로 변환시켜주는 함수를 제공하니 알맞게, 필요할때 사용하시면 되겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

XmlPullParser

Xml을 안드로이드에서 파싱하는 방법은 XmlPullParser라는 녀석으로 파싱할 수 있습니다. 사용법만 알면 누구든 쉽게 파싱할 수 있습니다.

일단 다음과 같은 xml이 있다고 쳐보도록 하겠습니다. 아래는 공공데이터포탈의 버스 위치 정보의 xml파일입니다. 

...
<busLocationList>
  <endBus>0</endBus>
  <lowPlate>0</lowPlate>
  <plateNo>경기70사1109</plateNo>
  <plateType>3</plateType>
  <remainSeatCnt>38</remainSeatCnt>
  <routeId>233000031</routeId>
  <stationId>202000217</stationId>
  <stationSeq>60</stationSeq>
</busLocationList>
<busLocationList>
  <endBus>0</endBus>
  <lowPlate>0</lowPlate>
  <plateNo>경기70사1139</plateNo>
  <plateType>3</plateType>
  <remainSeatCnt>42</remainSeatCnt>
  <routeId>233000031</routeId>
  <stationId>200000321</stationId>
  <stationSeq>8</stationSeq>
</busLocationList>
...

 

위의 xml을 파싱하는 방법은 아래의 코드 예제로 이해가 가능합니다. 우선 두가지 클래스 XmlPullParser와 XmlPullParserFactory가 필요합니다. 그리고 예외를 핸들링하려면 XmlPullParserException가 필요합니다. 이것들을 우선 import하고 아래의 코드를 통해 봅시다. 

import org.xmlpull.v1.XmlPullParser;

import org.xmlpull.v1.XmlPullParserException;

import org.xmlpull.v1.XmlPullParserFactory;

 

우선 웹에서 응답을 가져오는 코드이기 때문에 Thread를 사용하여야합니다. 아래의 코드는 run을 오버라이드한 코드입니다.

 

    @Override
    public void run() {

        try {

            URL url = new URL(requestUrl);
           
            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
            parser = xmlPullParserFactory.newPullParser();
         
            InputStream is = url.openStream();
            parser.setInput(new InputStreamReader(is, "UTF-8"));
            String tagName="";
            
            //event type얻어오기
			int eventType = parser.getEventType();

			//xml문서의 끝까지 읽기
			while (eventType != XmlPullParser.END_DOCUMENT) {
				switch (eventType) {
				//태그가 시작
					case XmlPullParser.START_TAG:
						tagName=parser.getName();
						if (parser.getName().equals("busLocationList")) {
							//객체 생성
							BusLocation location=new BusLocation();
						}
						break;
				//태그의 끝
					case XmlPullParser.END_TAG:
						if (parser.getName().equals("busLocationList")) {
							//객체를 리스트에 추가
							locationList.add(location);
						}
						break;
				//태그 안의 텍스트
					case XmlPullParser.TEXT:
				
						switch(tagName) {
							case "endBus":{
								location.endBus=parser.getText();
								break;
							}
							case "lowPlate":{
								location.lowPlate=parser.getText();
								break;
							}
							case "plateNo":{
								location.plateNo=parser.getText();
								break;
							}
							case "plateType":{
								location.plateType=parser.getText();
								break;
							}
							case "remainSeatCnt":{
								location.remainSeatCnt=parser.getText();
								break;
							}
							case "routeId":{
								location.routeId=parser.getText();
								break;
							}
							case "stationId":{
								location.stationId=parser.getText();
								break;
							}
							case "stationSeq":{
								location.stationSeq=parser.getText();
								break;
							}
						}
						break;
				}
                //다음으로 이동
				eventType = parser.next();
			}

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

 

while을 통해서 xml의 끝까지 돌아야하는데, 이때 XmlPullParser의 END_DOCUMENT를 통해서 알 수 있습니다. 그리고 그 외 시작 태그를 알 수 있는데 XmlPullParser의 START_TAG로 알 수 있습니다. 비슷하게 태그가 닫히는 부분은 END_TAG로 알 수 있습니다.  태그의 이름은 getName으로 알 수 있지요.

태그안의 내용은 XmlPullParser의 TEXT로 알 수 있고, 실제 내용은 getText 메소드로 알 수 있습니다. 위 코드는 응답받은 xml을 busLocationList 항목의 리스트로 저장하는 코드입니다.

사용하는 방법은 그리 어렵지 않죠?

반응형
블로그 이미지

REAKWON

와나진짜

,

GitLab 초기 환경 설정

프로젝트 하나 생성할때마다 항상 깃랩 초기 방법 설정하느라 시간을 잡아먹어서 아예 정리하고 나중에 복붙해서 써먹으려고 합니다.

우선 git을 설치했다고 가정하겠습니다. 깃을 설치안하셨다면 깃먼저 설치해주세요.

git-scm.com/downloads

 

Git - Downloads

Downloads macOS Windows Linux/Unix Older releases are available and the Git source repository is on GitHub. GUI Clients Git comes with built-in GUI tools (git-gui, gitk), but there are several third-party tools for users looking for a platform-specific exp

git-scm.com

 

1. 프로젝트 생성

gitlab에 로그인하고 프로젝트를 생성할 준비를 합니다. 메인 화면의 좌측 상단에 Your projects를 선택하여 이동합니다.

 

좀 내리면 New project가 있는데, 이것을 클릭해 새로운 프로젝트를 생성해줍니다.

 

저는 그냥 기본적인 비어있는 프로젝트를 골랐습니다. 

 

이제 프로젝트를 생성하는데에 기본정보들을 적어줍니다. 여러분이 생각하는 프로젝트 명을 기재하고 설명을 적어보세요. 그리고 마지막에는 ReadMe도 같이 생성해주려고 체크해줍시다.

 

이제 프로젝트를 생성했으면 그 url 주소를 기억해주세요.  저는 아래와 같은 url로 되어있습니다.

https://gitlab.com/reakwon/프로젝트명

 

2. 로컬 PC에 git 설정

1) 우선 프로젝트를 진행할 폴더를 하나 만들어주고 그 안으로 들어갑니다.

2) 그리고 쉘을 열어 명령어를 이용해 순서대로 설정 진행하면 됩니다. 우선 유저의 정보를 설정해줍니다.

git config --global user.name "당신의 이름"
git config --global user.email "당신의 email"

 

3) git init으로 초기화를 해줍니다.

git init

 

4) 이제 git pull을 통해서 원격 저장소에 있는 것과 sync를 해줍니다 .이거 안해주면 나중에 push가 되지 않아요. 아까 만들었던 ReadMe파일을 당겨오게 될거에요.

 git pull https://생성한 프로젝트 url

 

5) remote 설정해줍니다.

git remote add origin https://프로젝트 url

 

6) 이제 기초 설정은 끝이 났으니 이 폴더에 프로젝트를 진행시키고 파일을 업로드해보시면 됩니다.

git add .
git commit -m "커밋 메시지"
git push -e origin master

 

만약 push할때 아래와 같은 오류 메시지를 보게 된다면 원격 저장소와 로컬 PC가 싱크되지 않은 것이기 때문에 git pull 하고 다시 진행해보시기 바랍니다.

hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

 

반응형
블로그 이미지

REAKWON

와나진짜

,

서울시 버스 도착 정보 조회

 

● API 사용 신청하기

 

1. 회원가입 후 로그인 진행

우선 아래의 공공데이터 포털에 접속 한 후 회원가입후 로그인을 합니다.

www.data.go.kr/

 

공공데이터 포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

www.data.go.kr

 

2. 원하는 Open API를 검색

로그인을 하고 난 이후에 맨 위 상단에 데이터찾기 -> 데이터목록 을 찾아 클릭 한 후 검색 조건을 지정하여 API를 검색할 것입니다. 이때 우리가 사용할 API는 서울시 버스 도착 정보 조회입니다.

그냥 검색하면 너무 많은 버스 관련 API들이 검색되기 때문에 제공기관별검색을 눌러 자치행정기관 - 서울특별시 - 서울특별시를 선택해줍시다.

 

 

그 후 검색창에 "버스 조회" 를 검색하면 중간 부분에 오픈 API가 검색이 되는데 버스도착정보조회 서비스가 우리가 사용할 API가 됩니다.

 

눌러서 들어가보면 4개의 API를 사용할 수가 있는 것을 볼 수 있습니다.

 

API에 대한 간단한 설명을 아래의 표로 정리하였습니다.

일련번호 

API명(국문) 

상세기능명(영문) 

상세기능명(국문) 

1 

버스도착정보조회 서비스 

getArrInfoByRouteAllList 

경유노선전체정류소별도착예정정보목록조회 

2 

버스도착정보조회 서비스 

getArrInfoByRouteList 

정류소노선별도착예정정보목록조회 

3 

버스도착정보조회 서비스 

getLowArrInfoByRouteList 

정류소의특정노선교통약자용도착예정정보목록조회 

4 

버스도착정보조회 서비스 

getLowArrInfoByStIdList 

정류소별교통약자용도착예정정보목록조회 

 

3. 활용신청

검색했다고 그냥 쓸 수 있는 것은 아니고 활용 신청하면 API를 사용할 수 있습니다. 오른쪽 활용신청을 클릭해줍니다.

 

여기서 활용목적은 앱개발이며 간단히 어떻게 사용할 것인지만 적어주면 됩니다. 밑에 시스템 유형은 일반으로 지정해주면 됩니다.

 

그리고 더 아래에는 일일 트래픽 1000을 허용하며 추후에 신청하여 늘릴 수 있다고 합니다.

이렇게 신청했다고 바로 API가 사용가능한 상황은 되지 않고 1~2시간 정도가 지나야 API를 사용할 수 있습니다.

 

4. API 테스트 해보기

우선 웹사이트상에서 미리보기를 확인할 수 있습니다. 저는 특정 노선 ID를 통해서 경우노선 전체를 확인해보고자 합니다. 110A 고려대 버스의 노선 정보가 궁금합니다. 이때 버스 노선 ID는 100100016입니다. 버스 노선 정보 역시 공공데이터포털에서 잘 검색하면 엑셀표로 된 것이 나오니까 찾아보시고 확인해보세요. 혹은 아래의 파일을 참고하시기 바랍니다.

서울시 버스노선ID 정보(20190508).csv
0.07MB

 

이것을 샘플데이터에 입력해주고 미리보기를 눌러보면 xml으로 정보를 잘 얻어오는 것을 확인해볼 수 있습니다.

 

 

 

API 사용해보기

 

1. 인터넷 사용권한 

 

AndroidManifest.xml에서 인터넷 사용권한을 지정해줍니다. 

<uses-permission android:name="android.permission.INTERNET" />

 

2. Traffic 허용

그리고 다음과 같이 xml을 지정하여 우리가 사용하는 api 사이트의 traffic을 허용해주어야합니다. 아래와 같이 network_security_config.xml을 파일을 만든 후에 내용을 입력해주세요.

 

network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">ws.bus.go.kr</domain>
    </domain-config>
</network-security-config>

 

이 파일을 다시 AndroidManifest.xml 파일에 지정해주어야 트래픽이 허용됩니다.

<application
        android:allowBackup="true"
        ...
        android:theme="@style/Theme.Chat"
        android:networkSecurityConfig="@xml/network_security_config">

 

3. HttpURLConnection으로 데이터 받기

네트워크를 사용할 것이니 Thread를 사용합니다. 아래는 공공데이터에서 제공하는 getArrInfoByRouteAll를 활용하는 자바 샘플 코드입니다. 


public class NetworkThread extends Thread{

    @Override
    public void run() {
        try {

            StringBuilder urlBuilder = new StringBuilder("http://ws.bus.go.kr/api/rest/arrive/getArrInfoByRouteAll");
            Log.e("MY_TEST","urlBuilder");
            urlBuilder.append("?" + URLEncoder.encode("ServiceKey", "UTF-8") + "=servicekey");
            urlBuilder.append("&" + URLEncoder.encode("busRouteId", "UTF-8") + "=" + URLEncoder.encode("100100016", "UTF-8"));
            URL url = new URL(urlBuilder.toString());
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Content-type", "application/json");
            BufferedReader rd;
            if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
                rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            } else {
                rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = rd.readLine()) != null) {
                sb.append(line);
            }
            Log.e("BUS_API_TEST",sb.toString());
            rd.close();
            conn.disconnect();

        }catch(Exception e){
            e.printStackTrace();
        }

    }

}

 

위에서 보시면 ws.bus.go.kr이 우리가 트래픽을 허용한 주소라는 것을 알 수 있습니다. servicekey는 각자 자신이 받은 servicekey를 적어주시면 됩니다. 활용신청이 완료되었다면 인증키가 보일텐데 인코딩된 키를 이 부분에 사용하시면 됩니다.

 

이후 MainActivity에서는 그 쓰레드를 실행시키면 되지요.

public class MainActivity extends AppCompatActivity {
    private NetworkThread thread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        thread=new NetworkThread();
        thread.start();
    }
}

 

이제 실행 후 확인해보면 정상적으로 값을 로그로 출력함을 확인해볼 수 있습니다.

 

이 후부터는 이제 xml 데이터를 파싱해서 알맞게 사용하시면 됩니다.

 

발생할 수 있는 오류

만약 SERVICE KEY IS NOT REGISTERED ERROR 가 발생했다면 등록한 Service ID가 틀렸거나 아직 서비스 ID가 등록되어있지 않았기 때문에 조금 더 기다렸다가 해보시기 바랍니다.

만약 응답을 아예 받아올 수 없는 경우에는 ws.bus.go.kr 에 트래픽 허용을 안해줘서 그렇습니다. 위 network_security_config.xml의 내용을 확인하여 주세요.

 

반응형
블로그 이미지

REAKWON

와나진짜

,