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

와나진짜

,

회의실 배정 - BOJ 1931

www.acmicpc.net/problem/1931

 

1931번: 회의실 배정

(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

www.acmicpc.net

 

회의실의 시작 시간과 끝 시간이 주어졌을때 가장 많은 회의를 할 수 있는 회의수를 구하는 문제입니다. 아래와 같은 입력에 대해서 4개의 회의를 진행할 수 있고 최대로 회의를 할 수 있는 수입니다.

회의할 수 있는 시간은 (1 4), (5 7), (8 11), (12 14)가 될 수 있겠습니다.

입력

11
1 4
3 5
0 6
5 7
3 8
5 9
6 10
8 11
8 12
2 13
12 14

 

출력

4

 

소스 코드

회의실 문제는 탐욕 알고리즘을 적용하는 유명한 문제 중 하나인데요.

여기서 가장 먼저 시작하는 회의를 선택해서는 이 답을 찾을 수 없습니다. 예를 들어 회의가 (0 5), (3 4), (4 5), (6 8), (5 10) 이렇게 5개가 예정이 되어있다고 가정하겠습니다.  가장 먼저 시작하는 회의를 고른다면 만약 (0 5), (5 10)이 됩니다.하지만 (3 4), (4 5), (6 8)로 3개로 더 많은 회의를 진행할 수 있지요.

혹은 가장 짧게 회의를 진행하는 순서대로 회의를 진행하면 답을 찾을 수 있을 것 같긴하지만 다음과 같은 상황((1 7), (5 9), (8 13)이 있는 회의)이 발생한다면 오답이 발생하게 됩니다. 

greedy

(5 9)가 가장 짧은 회의로 그것을 먼저 선택했을때는 1개밖에 진행할 수가 없고, 나머지를 선택하면 2개를 진행할 수 있게 됩니다. 이것도 역시 해답은 아니군요.

결론은 항상 가장 먼저 끝나는 회의 먼저 배정해주면 됩니다. 그리고 겹치는 회의를 제거하면 되지요. 즉, 아래와 같은 순서대로 회의를 지정해주면 됩니다.

1. 현재 남아있는 회의들 중 가장 먼저 회의가 끝나는 것부터 출력하고 가장 마지막에 끝난 회의라고 하여 lastFinishedTime에 끝난 시간을 기록합니다.

2. 그리고 이 회의와 겹치지 않는 가장 먼저 끝나는 회의를 선택하여 출력합니다. 그러려면 lastFinishedTime보다 그 다음 진행할 회의의 시작 시간보다 작거나 같아야합니다.

3. 이 과정을 배열이 끝날때까지 반복합니다.

입력은 가장 먼저 끝나는 순서대로 주어지지 않을 수 있으니, 끝나는 순서로 정렬하는 과정이 필요합니다. 

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int N;
vector<pair<int, int> > arr;
int main() {
	scanf("%d", &N);
	for (int i = 0; i < N; i++) {
		int start, end;
		scanf("%d %d", &start, &end);

		//정렬은 첫번째 기준으로 정렬이 되기 때문에 end, start 로 pair
		arr.push_back({ end,start });
	}


	//끝나는 순서대로 정렬
	sort(arr.begin(),arr.end());

	int ret = 1;
	int lastFinishedTime = arr[0].first;
	for (int i = 1; i < arr.size(); i++) {
		int nextStartTime = arr[i].second;
		if (lastFinishedTime > nextStartTime) continue;
		lastFinishedTime = arr[i].first;
		ret++;
	}

	printf("%d\n", ret);
}

 

BOJ 1931 회의실 배정 문제를 풀어보았습니다. 이 문제는 탐욕 알고리즘과 정렬 알고리즘이 섞인 문제였습니다. 

반응형
블로그 이미지

REAKWON

와나진짜

,

BOJ 괄호의 값 - 2504

www.acmicpc.net/problem/2504

 

2504번: 괄호의 값

4개의 기호 ‘(’, ‘)’, ‘[’, ‘]’를 이용해서 만들어지는 괄호열 중에서 올바른 괄호열이란 다음과 같이 정의된다. 한 쌍의 괄호로만 이루어진 ‘()’와 ‘[]’는 올바른 괄호열이다.  만일

www.acmicpc.net

 

괄호와 관련해서 원래 스택의 기본 문제가 짝이 지어지느냐 마느냐인데, 이 문제는 연산까지 더해집니다. 괄호는 (, ), [, ]를 사용하며 A와 B는 이미 연산된 수를 의미합니다. 연산의 규칙은 다음과 같습니다. 

1. () = 2

2. [] = 3

3. (A) = 2*A

4. [A] = 3*A

5 AB = A+B

 

스택을 이용하면서 연산까지 같이 하면서 집어넣어줘야합니다. 어떻게 할 수 있을까요? 입력 예를 보면서 어떻게 풀어나갈지 생각해봅시다.

(()[[]])([])

 

큰 틀은 아래와 같은데요. 

1. 우리가 여는 괄호를 만나면 우선 스택에 집어넣습니다.

2. 닫는 괄호가 나오면 매칭되는 괄호가 나올때까지 이전에 계산한 수를 모두 더합니다. 그리고 여는 괄호가 '('이냐, '[' 이냐에 따라서 2를 곱하거나 3을 곱해서 다시 스택에 집어넣습니다.

3. 모든 괄호를 전부 만났다면 스택에 있는 모든 수를 더합니다.

 

이제 그림을 통해서 보도록 하겠습니다. 우선 처음 ( ( 는 여는 괄호이므로 전부 스택이 집어 넣습니다. 이 후 최초로 닫는 괄호 )가 나오니 연산해서 다음 데이터를 집어넣을 차례입니다.

 

여는 괄호 (를 만날때까지 스택에 있는 모든 수를 더하지만 아직 더할 수는 없습니다. 그래서 ()에 해당하는 2를 집어넣습니다. 하지만 여기서는 음수로 집어넣지요. 다음 '[', '[' 역시 여는 괄호이기 때문에 스택에 집어넣습니다.

 

그 후 닫는 괄호 ']'가 나오니까 여는 괄호가 나올때까지 있는 수를 모두 더하고 3을 곱한 수를 넣어야하지만 없으므로 []에 해당하는 -3을 집어넣습니다. 그리고 다시 닫는 괄호 ]이 나오네요. 이때 이제 그 전에 더할 수 -3이 있지요. 

 

그래서 -3을 더하고 난 후 [X]에 해당하는 3을 곱해줍니다. -9가 되겠네요. 이 값을 다시 스택에 집어넣습니다. 다시 닫는 괄호 )가 나오면 앞에 있던 방법으로 모두 더한후 (X)에 해당하는 연산을 하면 -22가 스택에 들어가게 됩니다.

 

 

남아있는 ([])는 아래와 같이 연산됩니다. 

 

이제 괄호의 식이 전부 끝났죠? 그러면 스택에 남아있는 모든 수를 더하고 -1을 곱하면 그게 답입니다. 아래는 위의 풀이를 코드로 구현한 것입니다. 큰 틀은 이렇습니다. N은 문자열의 길이입니다. 여는 괄호가 나오면 모두 스택에 집어넣습니다. 닫는 괄호가 나오면 getNextSum()을 통해서 스택에 있는 모든 수를 더하고 난 이후 맞는 괄호에 해당하는 수를 곱한 값을 받아옵니다. 이후 이 값을 스택에 다시 집어넣죠.

stack<int> st;

int main() {
	char str[31];
	scanf(" %s", str);

	int N = strlen(str);
	for (int i = 0; i < N; i++) {
		char next = str[i];
		switch (next) {
			case '(':
			case '[':
				st.push(next);
				break;

			case ')':
			case ']':
			{
				int s = getNextSum(next);
				if (s == IMPOSSIBLE) {
					printf("0\n");
					return 0;
				}
				st.push(s);
				break;
			}
		}
	}
    
    //...//
 }

 

getNextSum() 함수는 아래와 같이 구현이 되었습니다. 만약 next에 해당하는 데이터가 문자(양수)라면 여는 괄호를 찾고 음수이면 그 수를 더합니다. 여기서 왜 숫자를 음수로 집어넣는지 이해가 가나요? 문자 자체도 ascii 코드로 숫자로 표시될 수 있기 때문에 문자와 숫자를 구분하기 위해서 음수로 집어넣었던 것입니다.

 

만약 아무런 값도 들어있지 않다면 (), []이기 때문에 -2나 -3을 출력해주면 되고 값이 있다면 모두 음수로 더해서 2, 3을 곱한 값을 출력해주면 됩니다. 이때 식은 (X) 이거나 [X] 에 해당합니다.

 

이때 매칭이 되지 않는 괄호는 impossibleOpen이라고 하며 스택을 돌다가 이 괄호가 걸리게 되면 IMPOSSIBLE이라는 -1을 반환합니다. 

#define IMPOSSIBLE -1
int getNextSum(char next) {
	int sum = 0;
	char open = next == ')' ? '(' : '[';
	char impossibleOpen = next == ')' ? '[' : '(';

	while (!st.empty()) {
		int prev = st.top();
		st.pop();
		if (prev == open) 
			return sum == 0 ? (next == ')' ? -2 : -3) : (next == ')' ? sum * (-2) : sum * (-3));
		
		if (prev == impossibleOpen)  return IMPOSSIBLE;
		
		sum -= prev;
	}
	
	return IMPOSSIBLE;
}

 

마지막 과정에는 스택에 괄호가 있는지 없는 지 검사한 후 괄호가 있으면 이 괄호식은 매칭이 되지 않는 식이므로 0을 출력하고 종료하면 되고, 그렇지 않고 숫자만 있는 상태면 모두 더해서 출력해주면 됩니다.

	int ans = 0;
	while (!st.empty()) {
		if (st.top() == '[' || st.top() == ']' || st.top() == '(' || st.top() == ')') {
			printf("0\n");
			return 0;
		}
		ans -= st.top();
		st.pop();
	}

	printf("%d\n", ans);

 

전체코드입니다.

#include <cstdio>
#include <stack>
#include <string.h>
using namespace std;

stack<int> st;
#define IMPOSSIBLE -1
int getNextSum(char next) {
	int sum = 0;
	char open = next == ')' ? '(' : '[';
	char impossibleOpen = next == ')' ? '[' : '(';

	while (!st.empty()) {
		int prev = st.top();
		st.pop();
		if (prev == open) 
			return sum == 0 ? (next == ')' ? -2 : -3) : (next == ')' ? sum * (-2) : sum * (-3));
		
		if (prev == impossibleOpen)  return IMPOSSIBLE;
		
		sum -= prev;
	}
	
	return IMPOSSIBLE;
}

int main() {
	char str[31];
	scanf(" %s", str);

	int N = strlen(str);
	for (int i = 0; i < N; i++) {
		char next = str[i];
		switch (next) {
			case '(':
			case '[':
				st.push(next);
				break;

			case ')':
			case ']':
			{
				int s = getNextSum(next);
				if (s == IMPOSSIBLE) {
					printf("0\n");
					return 0;
				}
				st.push(s);
				break;
			}
		}
	}

	int ans = 0;
	while (!st.empty()) {
		if (st.top() == '[' || st.top() == ']' || st.top() == '(' || st.top() == ')') {
			printf("0\n");
			return 0;
		}
		ans -= st.top();
		st.pop();
	}

	printf("%d\n", ans);

}

 

반응형
블로그 이미지

REAKWON

와나진짜

,

파이썬 파일 다루기(File Handling)

모든 언어에서 파일을 다루는 것은 매우 중요한 일이고 필수적으로 알아두어야합니다. 프로그래밍에서 파일을 읽고 분석하는 작업, 그리고 결과를 기록하여 보관하는 작업 등 매우 필수적이기 때문이죠. 여러분이 어떤 실행파일을 설치했는데 그 설정된 이력을 어떻게 보관할 수 있을까요? 눈치 채셨겠지만 파일의 형태로 어딘가에 저장하고 읽고 수정합니다. 텍스트 파일로든 이진 파일로든 말이죠. 이제 파이썬에서 파일을 어떻게 다룰수 있는지 여러 예를 보면서 알아보도록 하겠습니다.

1. 파일 열기 - open

파일을 사용하기 위해선 가장 먼저 해야하는 일이 파일을 여는 것입니다. 파일을 열때 사용하는 함수가 open함수입니다. 이름이 너무 직관적이라서 기억하기도 쉽군요. open은 두가지 인자를 받을 수 있습니다. 하나는 파일 이름, 다른 하나는 file open mode입니다. mode는 아래에 표를 참고해주세요.

mode desc
"r" Read를 뜻하며 파일을 수정하는 용도가 아니라 읽기 전용으로 엽니다. 파일이 없으면 에러가 발생합니다.
"w" Write를 뜻하며 파일을 수정할때 사용하지만, 이미 파일에 내용이 있다면 새로 다시 씁니다. 파일이 존재하지 않으면 새로 생성합니다.
"a" Append를 뜻하고 파일에 내용을 덧붙일때 사용하는 mode입니다. "w" 모드는 새롭게 덮어쓰는 것이고, "a" 모드는 뒤에 추가한다는 점이 다릅니다. 역시 파일이 존재하지 않으면 새롭게 생성합니다.
"x" Create를 의미하며 파일을 생성합니다. 파일이 존재하면 에러를 반환합니다.

 

여기에 추가적으로 파일이 이진 파일이냐, 사람이 읽을 수 있는 텍스트 파일이냐에 따른 mode도 존재합니다.

mode desc
"t" Text를 뜻하며 텍스트 모드로 파일을 엽니다. open에서 mode를 지정하지 않으면 테스트 모드로 읽습니다.
"b" Binary를 뜻하며 이진 파일을 읽습니다. 예를 들면 이미지같은 파일을 의미하는 것이죠.

 

open은 호출이 완료되면 파일 객체를 반환해줍니다. 그리고 이 객체를 통해서 읽기, 쓰기 작업이 이루어질 수 있죠. 파일에 대한 작업을 완료하면 파일 객체의 close 메소드로 받드시 닫아주어야합니다.

※close() 를 반드시 해야하는 이유

보통 close()를 안하시고 중요하게 생각하지 않는 사람들이 많은데 그것은 우리들의 프로그램이 금방 끝나기 때문입니다. 아주 빈번하게 발생하는 문제점은 이런 상황입니다. 한글 파일을 연 상태에서 그 파일을 삭제시켜 보세요. 어디에서 열려있다고 하면서 삭제 동작을 하지 않습니다. 마찬가지로 프로그램 내에서 파일을 다 사용했는데 열어놓으면 다른 쓰레드나 프로세스가 쓸 수 없는 상황이 발생합니다. 이해하시겠죠? 아래 영화폴더는 저의 보물창고입니다.

 

아래의 코드는 exam.txt라는 파일을 읽기 전용으로 텍스트 모드로 열고 닫는 예를 보여줍니다.

f = open("exam.txt","rt")
f.close()

 

2. 파일 쓰기 - write, writelines

write를 통해서 파일에 내용을 쓸 수 있습니다. 이때 여러 줄을 쓸때는 List 자료형이나 Tuple을 사용할 수 있는 writelines 메소드도 존재합니다. 아래는 파일에 문자열을 쓰는 코드의 사용방법을 보여줍니다.

f = open("exam.txt","wt")
#일반적으로 쓰는 write
f.write("============write test===============\n")

# 리스트로 한번에 넣어버리기
lines = ["write\n","list\n","lines\n"]
f.writelines(lines)

# 튜플로 한번에 넣어버리기
lines = ("write\n","tuple\n","lines\n")
f.writelines(lines)

f.close()

 

위 코드를 작성하고 실행하면 프로젝트와 같은 디렉토리에 파일이 생겨납니다. 우리가 썼던 그 텍스트 내용인것을 확인할 수 있습니다.

write

 

3. read, readline

이제 이 파일을 읽어보도록 할까요? 읽기 위해서는 파일 객체의 read류의 메소드들을 사용하여 읽을 수 있습니다. read는 기본적으로 한글자씩 읽어오는 메소드입니다. 활용하는 방식은 아래와 같이 파일의 내용이 없을때까지 출력합니다. read()는 더 이상 읽을 내용이 없으면 빈 문자열을 반환합니다. 그리고 컴퓨터 공학에서는 이것을 파일의 끝(EOF: End Of File)이라고 합니다. C언어와 같은 언어에서는 EOF는 -1이라는 것은 그냥 참고만 하세요.

f=open("exam.txt","rt")
while True:
    c = f.read()
    if c == '':
        break
    print(c, end='')

f.close()
============write test===============
write
list
lines
write
tuple
lines

 

혹은 그냥 줄 단위로 가져오고 싶지는 않으신가요? 그럴땐 readline을 사용하여 가져올 수 있습니다. readline 역시 더 이상 읽을 데이터가 없다면 빈 문자열을 반환합니다.

f=open("exam.txt","rt")

while True:
    line = f.readline()
    if line == '' :
        break
    print(line, end='')

f.close()

 

writelines와 마찬가지로 리스트 형태로 여러 줄들을 읽어올 수도 있습니다. 그래서 for문으로 그 줄들을 순회할 수 있습니다.

f=open("exam.txt","rt")

lines = f.readlines()
for line in lines:
    print (line, end='')

f.close()

 

4. 파일 삭제 - remove

파일을 생성하고 쓰는 방법은 알았는데, 파일을 삭제하려면 어떤 방법으로 삭제를 할까요? os 모듈을 통해서 파일이 존재하는지 확인할 수 있고 삭제할 수 있습니다. 아래 코드는 해당 파일이 존재한다면 삭제하고, 아니라면 존재하지 않는 다는 출력을 해주지요. 

import os
if os.path.exists("exam.txt"):
  os.remove("exam.txt")
else:
  print("not exist")

 

해당 코드를 실행하면 우리가 지금까지 썼던 exam.txt 파일은 삭제되었음을 알 수 있습니다.

 

여기까지 파이썬으로 파일을 다루는 아주 기초적인 활용 예제들을 보았습니다. 파이썬이 다른 언어에 비해서 파일을 다루는 게 단순한 편입니다. 잘 숙지하시고 연습많이 하세요.

반응형
블로그 이미지

REAKWON

와나진짜

,

BOJ 2805 나무 자르기

www.acmicpc.net/problem/2805

 

2805번: 나무 자르기

첫째 줄에 나무의 수 N과 상근이가 집으로 가져가려고 하는 나무의 길이 M이 주어진다. (1 ≤ N ≤ 1,000,000, 1 ≤ M ≤ 2,000,000,000) 둘째 줄에는 나무의 높이가 주어진다. 나무의 높이의 합은 항상 M보

www.acmicpc.net

 

설명

1박 2일의 상근이가 나무가 필요한데, 벌목하고 잘린 나무를 사용한다는 내용입니다. 이때 벌목할때 쓰는 목재절단기는 한번에 H만큼 높이까지 모든 나무를 한번에 자를 수 있다고 합니다. 환경을 생각하는 상근이는 나무가 최대한 잘려나가지 않고 필요한 나무를 얻어내는것이 목표입니다.

 

만약 나무 4개가 20m, 15m, 10m, 17m가 있고 상근이가 필요한 나무는 7m일때 목재절단기의 높이가 15m가 되면 첫번째 나무에서 5m, 네번째 나무에서 2m를 얻어낼 수있으므로 잘리는 높이를 15m로 정해주어야하죠. 필요한 나무를 다 채우면서 나무를 최대한 높게 자르려면 절단기의 높이를 어떻게 정해야할까요?

 

풀이

나무의 수 N과 구하고자 하는 상근이의 나무 M이 주어지고 범위가 1<= N <= 1,000,000, 1<= M <= 2,000,000,000입니다. 보통 이런식으로 말도 안되는 큰 값을 범위로 주면 log가 섞여있는 알고리즘으로 풀어야합니다. 떠올려볼 수 있는 알고리즘이 빠른 정렬 알고리즘, 이진탐색 등이 있겠네요.

우선 중간 정도의 높이 정해보고 만약 상근이가 가져갈 수 있는 양이면 점차적으로 높이를 높이는 방식으로 접근하면 됩니다. 

어떤 높이 height가 주어졌을때 이 height로 잘린 트리의 남은 부분을 전부 합쳐 M 이상이면 이 height는 H의 조건을 만족하게 됩니다. 이 함수가 아래의 isPossible이지요.

bool isPossible(unsigned int height) {
	unsigned int taken = 0;
	for (int i = 0; i < N; i++) {
		if (trees[i] >= height)
			taken += (trees[i] - height);
		if (taken >= M) return true;
	}
	return false;
}

 

그래서 만약 그 높이가 가능하다면 높이를 올립니다. 그리고 다시 가능한지 확인하죠. 아래의 코드는 이진탐색의 코드입니다.

isPossible에서 true가 반환되었다면 일단 답이 될 후보이며 ret에 그 값을 저장해놓습니다. 그리고 left의 값을 mid + 1로 증가시켜 자를 높이를 높게만들죠. isPossible이 false가 반환되었다는 것은 이 값은 답 후보가 아니라는 겁니다. 그래서 자를 높이를 아래로 조정합니다.

right는 입력에서 들어올 수 있는 가장 큰 값으로 지정을 했는데, 정석대로 풀려면 나무들을 순회하면서 가장 큰 나무의 높이가 right가 되어야겠죠? 하지만 이진탐색은 빠르니까 가장 큰 값을 지정해도 문제는 없습니다.

int solve() {
	unsigned int left = 0, right = 1000000000;
	unsigned int mid,ret;
	while (left <= right) {
		mid = (left + right) / 2;
		if (isPossible(mid)) {
			ret = mid;
			left = mid + 1;
		}
		else {
			right = mid - 1;
		}
	}
	return ret;
}

 

그리고 이진 탐색에서 나무들은 모두 정렬된 상태여야합니다. 그래서 문제를 풀기 전에 sort로 정렬합니다. 아래는 전체 코드입니다.

#include <iostream>

using namespace std;
int N;
long long M;
long long trees[1000001];
bool isPossible(unsigned int height) {
	unsigned int taken = 0;
	for (int i = 0; i < N; i++) {
		if (trees[i] >= height)
			taken += (trees[i] - height);
		if (taken >= M) return true;
	}
	return false;
}
int solve() {
	unsigned int left = 0, right = 1000000000;
	unsigned int mid,ret;
	while (left <= right) {
		mid = (left + right) / 2;
		if (isPossible(mid)) {
			ret = mid;
			left = mid + 1;
		}
		else {
			right = mid - 1;
		}
	}
	return ret;
}
int main() {
	cin >> N >> M;
	for (int i = 0; i < N; i++)
		cin >> trees[i];

	cout << solve() << endl;

}

 

위 풀이의 시간은 O(N log(M)) 입니다. isPossible은 모든 나무를 돌아가며 연산하기 때문에 N, 그리고 이진 탐색의 뼈대인 solve함수가 log M이 되죠.

반응형
블로그 이미지

REAKWON

와나진짜

,

datetime 모듈

파이썬에서 날짜와 관련된 데이터를 다룰때에는 datetime이라는 모듈을 import 하여 사용해야합니다. datetime은 날짜와 관련된 여러가지 메소드들을 가지고 있습니다.

 

현재 시간 구하기(now)

현재 시간을 구하려면 아래와 같이 간단한 한줄만 사용하여 나타낼 수 있습니다. now()는 datetime 객체를 반환하여 줍니다.

import datetime

d = datetime.datetime.now()
print (d)
2021-04-18 16:50:43.895283

 

datetime 객체는 년(year), 월(month), 일(day), 시(hour), 분(minute), 초(second)의 필드를 가지고 있습니다. 그래서 필요한 시간의 부분만 사용할 수도 있습니다.

d = datetime.datetime.now()
print (d.year,'년 ', d.month,'월 ', d.day,' 일')
print (d.hour,'시 ',d.minute,'분 ',d.second,'초')
2021 년  4 월  18  일
16 시  58 분  38 초

 

datetime 객체 구하기

위의 현재 시간을 now라는 메소드로 구할 수 있지만 우리가 직접 지정한 날짜를 datetime 객체로 가져오기를 원한다면 datetime 클래스의 생성자를 이용하여 가져올 수 있습니다.'

wuhan_covid19 = datetime.datetime(2019,12,12)
print (wuhan_covid19)
2019-12-12 00:00:00

 

위에서는 날짜만 지정했지만 시간도 같이 지정해줄 수 있습니다.

wuhan_covid19 = datetime.datetime(2019, 12, 12, 3, 3, 3)
print (wuhan_covid19)
2019-12-12 03:03:03

 

strftime() 메소드로 원하는 날짜 형식대로 출력

datetime은 날짜를 형식화해주는 메소드가 존재하는데 메소드명은 strftime(String Format Time)입니다. 메소드에 포맷 문자를 전달하여 원하는 날짜의 형태를 문자열로 가져올 수 있습니다.

포맷 코드는 아래의 표에 정리하였으니 맞는 format code를 조합하여 사용하면 됩니다.

Format Code

포맷 코드 설명
%a 요일을 짧게 표시합니다. Sun
%A 요일을 길게 표시합니다. Sunday
%w 요일을 숫자로 표시합니다. 일요일을 0부터 시작하여 토요일은 6입니다. 0
%d 날(day)을 출력합니다. 1부터 31까지가 있겠죠 18
%b 월을 영어로 짧게 출력해줍니다. Apr
%B 월을 영어로 길게 출력해줍니다. April
%m 월을 숫자로 표현합니다. 04
%y 년을 짧게 숫자로 표시합니다 21
%Y 년을 길게 숫자로 표시합니다. 2021
%H 시간을 24시간의 표현 방식(00-23)으로 숫자로 표시합니다. 18
%I 시간을 0-12시 표시 방법으로 표시합니다 6
%p 오전(AM), 오후(PM)을 표시합니다. PM
%M 분(0 - 59)을 표시합니다. 38
%S 초(0 - 59)를 표시합니다. 55
%f microsecond단위를 표시합니다. 545433
%j 일년중 몇번째일인지 나타냅니다. 108
%U 일년 중 몇번째 주 인지 나타내니다. 이 포맷에서 일요일은 일주일의 시작입니다. 일년은 52주, 53주입니다. 108
%W 일년 중 몇번째 주 인지 나타내는 것은 %U와 같지만 일주일의 시작을 월요일로 정합니다. 108
%c Local version의 날짜와 시간을 나타냅니다. Sun Apr 18 17:26:26 2021
%x Local version의 날짜만 나타냅니다. 04/18/21
%X Local version의 시간만 나타냅니다. 17:26:26

 

아래는 strftime을 통해서 시간을 출력해주는 예제 코드입니다.

now = datetime.datetime.now()

print (now.strftime("%Y/%m(%B)/%d %A %p %I:%m:%S, 일년 중 %U 번째주, 일년 중 %j번째 날 "))
print (now.strftime("%c"))
print (now.strftime("%x"))
print (now.strftime("%X"))
2021/04(April)/18 Sunday PM 05:04:51, 일년 중 16 번째주, 일년 중 108번째 날 
Sun Apr 18 17:30:51 2021
04/18/21
17:30:51

 

datetime 연산

datetime은 연산도 가능한데요. datetime끼리 더하기, 빼기 연산이 가능합니다. 이때 결과는 timedelta의 객체 형태로 반환됩니다.

import datetime

birthday = datetime.datetime(1988,12,11)
now = datetime.datetime.now()
elapsed = now - birthday
print (elapsed)
11816 days, 17:39:16.585322

 

여기까지 파이썬에서 날짜를 다루는 방법을 알아보았습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

파이썬은 문자열을 다룰때 다양한 형태로 문자열을 다룰 수 있습니다. C나 Java같은 언어에서는 문자열을 쌍따옴표로 둘러싸서 문자열을 표현하지만 파이썬은 ', ", ''' 로 둘러싸서 문자열을 표현할 수 있죠. 이렇게하는 이유는 ', " 가 문자열에 글자로 표현될 때 문자열의 종료나 시작으로 인식하지 않게 하기 위함입니다.

str1 = 'python'
str2 = "python"
str3 = '''python'''

print (str1, str2, str3)

str1 = '"python"'
str2 = "'python'"
str3 = '''"python"'''

print ( str1, str2, str3)
python python python
"python" 'python' "python"

 

이제부터 파이썬의 문자열에 대해서 알아보도록 하겠습니다. 

문자열 포맷

- 포맷 문자를 통한 문자열 포맷

C와 같이 문자열에 다른 데이터를 포함시키려면 format 문자를 사용해서 정수든, 글자든 입력받을 수가 있죠.

포맷 문자 설명
%d 10진수 정수 (Decimal)
%c 문자 (Character)
%f 부동 소수 (Floating Point)
%o 8진수 정수 (Octal)
%x 16진수 정수 (Hexadecimal)
%s 문자열 (String)
%% % 문자

 

C에서 지원하는 무자열 형태의 format은 거의다 지원한다고 보시면됩니다. 그렇기 때문에 아래의 링크를 통해서 더 많은 문자열 포맷을 활용하는 방법을 알아보시기 바랍니다.

reakwon.tistory.com/169

 

[C언어] 출력 형식(format) 총정리 (Feat. sprintf, fprintf) - 일정한 간격으로 문자열 출력 예제 까지

C언어의 다양한 출력 문자들 C언어에서 다양한 출력 형식을 지원합니다. 우리가 너무나 잘 알고 있는 부호있는 정수형은 %d, 문자열 출력은 %s 등이 그 출력형식인데요. 오늘은 자세하게 한번 총

reakwon.tistory.com

 

형식을 지정할 데이터는 문자열 끝 %를 이용해서 나열해줍니다.

print ('구구단 2단')
for i in range(10):
    print ('%d * %d = %d' % (2, i, 2*i) )
구구단 2단
2 * 0 = 0
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
2 * 5 = 10
2 * 6 = 12
2 * 7 = 14
2 * 8 = 16
2 * 9 = 18

 

- format함수를 통한 문자열 포맷

이와 같은 방식은 우선 자료형에 따른 포맷 문자를 알고있어야하는 단점이 있습니다. 파이썬 3부터는 포맷을 알고 있지 않아도 사용할 수 있는 방법은 format함수를 사용할 수 있습니다. 여기서 중괄호를 이용합니다. 숫자를 입력하여 차례대로 입력받을 수도 있고, 명시적으로 이름을 지정해서 사용할 수도 있습니다.

str = 'str.{} example'.format('format')
print(str)
                                                #  {0}      {1}        {2}          {3}
str = 'SELECT {0} FROM {1} WHERE {2} = {3}'.format('*', 'accounts', 'email', 'reakwon@gmail.com')
print (str)

str = 'name : {name}, age : {age}'.format(name='kim',age=22)
print (str)
str.format example
SELECT * FROM accounts WHERE email = reakwon@gmail.com
name : kim, age : 22

 

- f String을 사용한 문자열 포맷

문자열을 저장할때 가장 맨 앞에 f를 준 후 변수명을 그대로 문자열에 중괄호로 입력하면 그 변수명의 데이터가 그대로 문자열에 입력됩니다. f는 format의 약자라는 점은 참고해주세요. 이 f string은 파이썬 3.6부터 지원합니다.

name = 'shin'
age = 22
score = 80

result = f'name : {name}, age : {age}, score : {score}'
print (result)
name : shin, age : 22, score : 80

 

산술 연산도 할 수 있습니다.

a = 10
b = 20

fstr = f'{a} * {b} = {a*b}, {a} + {b} = {a+b}'
print (fstr)
10 * 20 = 200, 10 + 20 = 30

 

함수의 반환값도 쓸수 있죠.

def mult(a, b):
    return a*b

def add(a, b):
    return a+b

a = 5
b = 9
fstr = f'{a} * {b} = {mult(a,b)}, {a} + {b} = {add(a,b)}'
print (fstr)
5 * 9 = 45, 5 + 9 = 14

 

문자열 메소드

문자열 메소드는 엄청 많은데, 그 중에서 몇가지 문자열 메소드를 알아보도록 하겠습니다. 문자열은 아래의 문자열을 사용해보지요.

paul_rand = 'Do not to be original, just-try-to-be-good.'

 

- 문자열 길이 : len

문자열 내장 메소드는 아니지만 길이를 알고자 하는 경우 len 내장 함수를 쓰면 됩니다.

print(len(paul_rand))
43

 

- 문자수 세기 : count

문자나 문자열의 수를 새려면 count 메소드를 사용하여 확인할 수 있습니다.

print (paul_rand.count('t'))        #t문자 세기
print (paul_rand.count('to'))      #to 문자열 세기
5
2

 

- 대소문자로 변경 : upper, lower, casefold

알파벳을 모두 소문자로 변경하려면 lower 메소드 사용하면 되는데 소문자로 변경하는 메소드는 casefold라는 메소드도 있습니다. 반대로 모두 대문자로 변경하려면 upper를 사용하면 됩니다.

print (paul_rand.upper())   #모두 대문자로 변경
print (paul_rand.lower())   #모두 소문자로 변경
print (paul_rand.casefold())
DO NOT TO BE ORIGINAL, JUST-TRY-TO-BE-GOOD.
do not to be original, just-try-to-be-good.
do not to be original, just-try-to-be-good.

 

- 문자열 분리 : split

문자열을 공백, 또는 지정된 나누려고 구분된 구분자에 따라서 문자열을 쪼개고 싶다면 split 메소드를 사용할 수 있습니다. 인자를 넣어주지 않는다면 공백을 기준으로 나누고, 지정한 문자열을 넘겨주면 그 문자열을 기준으로 문자열을 나눕니다. 쪼개어진 문자열들은 리스트 형태로 넘겨줍니다.

tokens = paul_rand.split()
print (tokens)

tokens = paul_rand.split('-')
print (tokens)
['Do', 'not', 'to', 'be', 'original,', 'just-try-to-be-good.']
['Do not to be original, just', 'try', 'to', 'be', 'good.']

 

- 문자열 공백 지우기 : strip, lstrip, rstrip

문자열에 공백을 제거하려면 strip 메소드를 사용하면 됩니다. 특별히 왼쪽 공백은 lstrip, 오른쪽 공백은 rstrip을 사용하면 됩니다.

str = '  __name__  __main__    '

print (str.lstrip())    # 왼쪽 공백 제거
print (str.rstrip())    # 오른쪽 공백 제거
print (str.strip())     # 양쪽 공백 제거
__name__  __main__    
  __name__  __main__
__name__  __main__

 

- 문자열 위치 : find, index, rfind, rindex

문자열에서 특정 문자열이 어느 위치에 있는지 확인하려면 find와 index를 사용하면 됩니다. 이때 가장 첫번째로 등장한 위치를 반환합니다. find와 index의 차이점은 문자를 찾지 못할때는 에러를 발생시키느냐 마냐입니다. find는 못찾으면 -1을 반환하고 index는 에러를 발생시킵니다.

print ('o : ', paul_rand.find('o'))
print ('. : ', paul_rand.index('.'))
print ('original : ', paul_rand.find('original'))
print ('just : ' ,paul_rand.index('just'))

print ('6 :', paul_rand.find('6'))      #없는 문자열의 경우 -1 반환
print ('6 :', paul_rand.index('6'))     #없는 문자열의 경우 에러

o :  1
. :  42
original :  13
just :  23
6 : -1
Traceback (most recent call last):
  File "C:\Users\grjwu\PycharmProjects\pythonProject1\main.py", line 9, in <module>
    print ('6 :', paul_rand.index('6'))
ValueError: substring not found

 

또는 start와 end 인덱스를 지정하게 되면 그 부분에 대해서만 찾아오게 됩니다.

print (paul_rand.find('to',4,9))    # 글자위치 4부터 9 전까지 탐색
print (paul_rand.index('or',10,19)) # 글자위치 10부터 19 전까지 탐색
7
13

 

왼쪽이 아니라 오른쪽에서 찾아보고 싶다면 rfind와 rindex를 사용하면 됩니다. 이때 결과는 위의 index와 find와 동일하며 오류내는 것도 동일합니다.

good = 'good, good, good~'

print ('o : ', good.rfind('o'))
print ('g : ', good.rindex(','))
o :  14
g :  10

 

 

- 문자열 변경 : replace

특정 문자열을 변경하고 싶다면 replace메소드를 사용하여 바꿀 수 있습니다.

print (paul_rand.replace('to','TO'))
Do not TO be original, just-try-TO-be-good.

 

- 특정 문자열로 시작하느냐 끝나느냐 - startswith, endswith

우리가 지정한 문자열로 시작하느냐를 알아보고 싶다면 startswith, 끝이 나는가를 알아보려면 endswith 메소드를 사용하면 됩니다. 지정된 문자열로 시작, 끝이 나면 True를, 아니면 False를 반환합니다.

print (paul_rand.startswith('Do'))
print (paul_rand.endswith('.'))

print (paul_rand.startswith('The'))
print (paul_rand.endswith('!'))
True
True
False
False

 

- 문자열 삽입 : join

특정 문자열을 문자마다 삽입하고 싶다면 join을 사용하면 됩니다. 글자마다 우리가 지정한 문자열이 삽입되고 만약 단어마다 문자열 삽입을 원한다면 리스트 형태의 문자열 리스트를 전달해주면 됩니다.

 

str = 'ABCDE'
print ("=".join(str))

str = ['Apple','Banana','Cherry']
print (', '.join(str))
A=B=C=D=E
Apple, Banana, Cherry

 

- 탭 간격 조정 : expandtabs

탭의 간격을 조정하는 메소드는 expandtabs입니다. 

str = "h\te\tl\tl"
print (str)
print (str.expandtabs(2))
print (str.expandtabs(4))
print (str.expandtabs(10))
h	e	l	l
h e l l
h   e   l   l
h         e         l         l

 

여기까지 파이썬 문자열의 활용방법과 메소드 들에 대해서 알아보았습니다. 여기서 소개하지 않은 메소드도 많이 있으므로 그때 그때 구글링하여 사용하시기 바랍니다.

반응형
블로그 이미지

REAKWON

와나진짜

,

리스트(List) 자료형

파이썬에는 다른 언어와 비슷하게 자료를 배열과 같이 사용하는 방법이 있습니다. 다른 언어와 같이 배열과 비슷한 특징이 있으나 여기서 추가 기능이 많이 추가 되어 있죠. 어떤 것들이 있는지 이 포스팅에서 정리하도록 하겠습니다.

1. 리스트 초기화와 접근(Indexing)

기본 초기화

리스트는 대괄호([ ]) 안에 리스트의 요소들이 저장됩니다. 대괄호하면 뭔가 배열이 생각나지 않으신가요? 대괄호에 저장하고싶은 원소를 쉼표로 구분하여 넣어주면 끝입니다.

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]
print (intList)
print (strList)
[1, 2, 3, 4, 5]
['reakwon', 'tistory', 'com']

 

원소 접근

만약 리스트의 원소에 접근하여 읽거나 값을 변경하고 싶다고 하면 인덱스를 사용하여 접근할 수 있습니다. 맨 앞 원소는 0번, 맨 뒤의 원소는 리스트의 길이 - 1이 되죠. 인덱스에는 부호 -가 붙을 수 있는데, 이때는 뒤의 인덱스를 의미하지만 -1부터 시작합니다. -1은 맨 마지막 원소의 인덱스입니다. -2는 맨 마지막 2번째 원소를 의미하게 되죠.

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]
print (intList[0], intList[1], intList[-1], intList[-2])
print (strList[0], strList[-1])

#strList 마지막의 원소 값 변경
strList[2] = 'COM'
print (strList)
1 2 5 4
reakwon com
['reakwon', 'tistory', 'COM']

 

슬라이싱 - i인덱스부터 j인덱스까지 접근 : [i : j ] 

그렇다면 만약 전체 원소에 접근하지말고 특정 인덱스(begin)부터 특정 인덱스 -1 (end) 까지 원소에 접근하고 싶다면 콜론(:)을 이용할수 있습니다. 마지막 처음 인덱스를 지정하지 않으면 0번부터 시작, 마지막 인덱스를 지정하지 않는다면 리스트의 끝까지를 의미합니다. 이 같은 법칙에 따라서 [:]은 리스트의 전체를 의미합니다. 

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]

print (intList[1:2])    #1번 원소부터 2번 원소 전까지
print (strList[1:])     #1번 원소부터 끝까지

# [1, 2, 3, 4, 5] 의 1번 인덱스부터 3-1=2번 index까지 값을 변경
intList[1:3] = [12, 13]
print(intList)
[2]
['tistory', 'com']
[1, 12, 13, 4, 5]

 

2. 리스트 원소 삭제

읽기와 변경뿐만 아니라 원소를 아예 리스트에서 삭제하고 싶다면 del 이라는 함수를 사용해서 삭제할 수 있습니다. 마찬가지로 슬라이싱으로 한꺼번에 리스트 원소를 삭제할 수도 있습니다.

intList = [1, 2, 3, 4, 5]
strList = ["reakwon", "tistory", "com"]

del intList[:3]	# 3번 인덱스 이전까지 원소 모두 삭제
del strList[2]	# 2번 인덱스 원소 삭제

print(intList)
print(strList)
[4, 5]
['reakwon', 'tistory']

 

 

 

3. 다중 리스트

리스트안에 리스트가 포함이 될 수 있습니다. 다중 리스트에 접근할때는 인덱스를 추가한 리스트만큼 인덱스를 반복하면 됩니다. 만약 아래와 같이 mergedList에 intList에 포함이 된다면 intList에 존재하는 원소에 접근하려할때는 인덱스를 어떻게 지정하면 좋을까요? 모든 것을 포함한 index와 그 다음 리스트가 포함한 원소의 index , ... 마지막 리스트의 원소 index 이런식으로 접근할 수 있습니다.

intList = [1, 2, 3, 4, 5]
        # [0][1][2][3][4]
mergedList = ["reakwon", "tistory", "com",intList]
            #   [0]         [1]      [2]    [3]

print(mergedList[0], mergedList[1], mergedList[2], mergedList[3][0], mergedList[3][1], mergedList[-1][-1])
reakwon tistory com 1 2 5

 

4. 리스트의 반복

리스트를 반복하고 싶으면 * 연산자를 사용해서 반복할 횟수를 적어주면 됩니다.

ls = ['-','=']
repeat = ls * 5
print (repeat)
['-', '=', '-', '=', '-', '=', '-', '=', '-', '=']

 

5. 리스트의 내장 함수

리스트는 인덱스 접근, 변경, 삭제가 가능할뿐만 아니라 때에 따라 맞게 사용할 수 있는 함수를 내장하고 있습니다. 여기서 어떤 함수들이 있나 살펴보도록 하겠습니다.

 

리스트의 길이 - len

리스트의 내장함수는 아닌데, 리스트의 길이를 구하고 싶을때는 len 함수를 사용하여 구할 수 있습니다.

numbers = [5, 2, 3, 5, 6, 7, 1, 1, 1]   #9개 원소

print ("numbers 길이 : ", len(numbers))
numbers 길이 :  9

 

정렬 - sort

리스트는 sort 함수 한번으로 원소를 정렬할 수 있습니다. 이때 주의할 점은 리스트 내의 자료형은 같은 자료형이어야합니다. 정수형, 문자열을 섞어서 사용할 수는 없습니다. 숫자일때 기본 정렬은 오름차순, 문자열일때 정렬은 기본 사전순이 됩니다. 이때 대소문자를 구분하여 대문자가 먼저 나옵니다. 이유는 소문자의 ASCII코드보다 대문자가 더 작은 코드이기 때문이죠.

numbers = [5, 2, 3, 5, 6, 7, 1, 1, 1]
strs = ['apple', 'computer', 'python', 'list', 'C++','Java', 'banana']

numbers.sort()  #정수 정렬
strs.sort()     #문자열 정렬

print(numbers)
print(strs)

 

[1, 1, 1, 2, 3, 5, 5, 6, 7]
['C++', 'Java', 'apple', 'banana', 'computer', 'list', 'python']

 

리스트의 순서를 거꾸로 저장 - reverse

리스트 순서를 거꾸로 만들고 싶다면 reverse를 이용해서 순서를 거꾸로 만들 수 있습니다. 이를 응용해서 위의 정렬된 리스트를 내림차순으로 만들고 싶다면 sort 이후 reverse를 사용하면 되겠죠?

numbers = [5, 2, 3, 5, 6, 7, 1, 1, 1]

numbers.sort()  #정수 정렬
numbers.reverse()   #거꾸로 뒤집은 리스트

print(numbers)
[7, 6, 5, 5, 3, 2, 1, 1, 1]

 

리트스에 포함된 원소의 갯수 - count

혹시 리스트에 우리가 찾는 값이 있는지, 있다면 몇개나 존재하는지 확인하고 싶다고 한다면 count 함수를 사용하면 됩니다.

numbers = [30, 19, 100, 34, 123, 51, 0, 0, -1]
strings = ['java', 'python', 'programming', 'algorithm', 'null', 'null']

print ('0은 몇개? :', numbers.count(0))
print ('null은 몇개? :', strings.count('null'))

0은 몇개? : 2
null은 몇개? : 2

 

마지막에 요소 추가 - append

append는 덧붙이기라는 뜻이 있어 보통 파일에 덧붙이기로 열때 옵션으로 'a'를 사용하는 것도 파일에 추가 기록하기 위함입니다. 기존의 원소를 삭제하지 않고 마지막에 추가합니다

ls = ['hello','world']

ls.append('!!')
ls.append('This')
ls.append('is')
ls.append('python')
print (ls)
['hello', 'world', '!!', 'This', 'is', 'python']

 

리스트를 끝에 추가하는 것도 가능합니다.

animals = ['dog', 'cat', 'orca']

animals.append(['monkey','elephant'])

print(animals)
['dog', 'cat', 'orca', ['monkey', 'elephant']]

 

리스트의 확장 - extend

append에서 끝에 리스트를 추가할 수 있었는데 이를 접근하려면 중첩 인덱싱을 사용해야합니다. 이렇게 말고 아예 원소들 자체를 리스트에 포함시키고 싶을때는 extend를 활용하여 달성할 수가 있습니다. extend는 반드시 리스트 자료형을 전달받습니다.

 

animals = ['dog', 'cat', 'orca']

animals.extend(['monkey','elephant'])

print(animals)
print(animals)

 

중간에 요소 삽입 - insert

마지막이 아니라 중간 어느 지점에 원소를 추가하려면 insert 함수를 사용하면 됩니다. 어느 위치에 삽입 어떤 데이터를 삽입할 것이냐를 알아야하기 때문에 인자를 2개받습니다.

www = ['world','web']

www.insert(1,'wide')    # 1번째에 'wide' 원소 추가
print(www)

 

append와 같이 중간에 리스트 자체를 추가할 수도 있습니다.

PS4Titles = ['WWZ', 'Tekken','GTA','Red Dead Redemption', 'FIFA']
PS4Titles.insert(2,['The Last Of Us Part 1', 'The Last Of Us Part Golf'])

print(PS4Titles)
['WWZ', 'Tekken', ['The Last Of Us Part 1', 'The Last Of Us Part Golf'], 'GTA', 'Red Dead Redemption', 'FIFA']

 

원소 삭제 - remove

원소를 삭제하려면 remove를 사용해서 삭제할 수 있습니다. 이때 인덱스가 아닌 원소 자체를 넘겨줘야합니다.

countries = ['korea','japan','china','US','UK','france','vietnam']

countries.remove('japan')
countries.remove('china')

print(countries)
['korea', 'US', 'UK', 'france', 'vietnam']

 

혹은 리스트 자체를 삭제할 수도 있습니다. 

countries = ['korea',['japan','china'],'US','UK','france','vietnam']

countries.remove(['japan','china'])

print(countries)
['korea', 'US', 'UK', 'france', 'vietnam']

 

원소 전부 삭제 - clear

리스트의 내용을 전부 비워내고 싶다면 clear 함수를 사용할 수 있습니다. 허나 del로도 같은 기능을 할 수 있습니다.

www = ['world','web']

www.clear() #del www[:]와도 같은 동작

print (www)
[]

 

원소를 가져온 후 삭제 - pop

pop은 원소를 가져오고 난 이후에 리스트에 삭제하는 기능을 합니다. 만약 아무런 인자를 넘겨주지 않는 경우 마지막 원소를 가져오고 리스트에서 삭제하고, 그렇지 않고 인덱스를 넘겨주면 그 인덱스의 원소를 가져온 후 삭제합니다.

countries = ['korea','japan','US','UK','france','vietnam', 'china']

countries.pop()     #마지막 원소 삭제
countries.pop(1)    #1번 원소 삭제

print(countries)
['korea', 'US', 'UK', 'france', 'vietnam']

 

원소의 인덱스 찾기 - index

값이 어느 인덱스에 저장되어있는지 확인하려면 index를 사용하여 확인할 수 있습니다. 가장 1번째는 찾을 값이 주어지집니다. 이후 다른 인자가 없다면 전체 리스트를 검색하여 찾아냅니다. 그렇지 않고 시작 인덱스와 끝 인덱스를 지정하여 찾아낼 수도 있습니다. 

languages = ['C++', 'Java', 'Python', 'C', 'C#', 'Kotlin']

print (languages.index('C++'))
print (languages.index('Kotlin',0,4))

0
Traceback (most recent call last):
  File "C:\Users\grjwu\PycharmProjects\pythonProject1\main.py", line 5, in <module>
    print (languages.index('Kotlin',0,4))
ValueError: 'Kotlin' is not in list

 

만약 인덱스를 찾아올 수 없다면 위의 에러를 만나게 되죠.

간단하게 리스트를 파이썬에서 어떻게 활용하는지 확인해보았습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,