ID:
PW:

     0 분
     10 분

엔지니어 돌종
  dolljong
프로그래밍 Tips
프로그래밍에 관한 Tip을 공유하는 곳입니다.


[python](엔지니어를 위한) 파이썬 시작하기[5]파일입출력
이석종  2022-08-20 15:54:42, 조회 : 742, 추천 : 74
- Download #1 : engpython05.png (14.6 KB), Download : 15


(엔지니어를 위한) 파이썬 시작하기[5]

 

내용 : 파일 입출력

참조 :  https://wikidocs.net/26

https://docs.python.org/ko/3/tutorial/inputoutput.html#reading-and-writing-files

 

 

0.시작하며

파에서 프로그램은 입력→계산→출력으로 구분할 수 있다고 했는데 파일은 입력으로도 쓰이고 출력으로도 쓰인다. 보통 엔지니어들은 한개의 프로그램만으로 일하지 않는다 어떤 프로그램을 돌려서 나온 결과(파일)을 읽어서 계산을 하고 그 결과를 출력을 한다.

 

그래서 파일을 읽고 쓰는 방법에 대해서 잘 알고 있을 필요가 있다. 여기서 다루는 건 텍스트 파일이다. 파이썬으로 엑셀l파일도 만들고, 읽고, 수정할 수 있다. 아무래도 엔지니어링 실무에서 엑셀파일을 많이 쓰기 때문에 엑셀파일을 다루는 건 나중에 따로 하고 이번에는 텍스트 파일 다루는 것만 진행하겠다.

 

1. 파일 읽기

파일을 읽는 방법은 일단 파일을 열고 그다음 읽기 함수를 사용한다. 파일을 여는 함수는 아래와 같다.

 

f = open("C:/doit/새파일.txt", 'r')

 

open()안에 파일의 full path명(디렉토리+파일명)과 ‘r’을 입력해서 읽기 위해 연다는 걸 알려주면 된다. open함수의 결과는 f에 입력되고 앞으로 f를 이용해서 읽기 관련 기능을 수행한다. 열기만 하고 아직 읽지는 않았다.

 

파일을 열면 당연히 닫아야 한다. 다는 명령은 아래와 같다.

 

f.close()

 

f는 파일을 오픈할 때 정해준 바 있다. 파일의 내용을 읽는 명령들은 open과 close사이에 배치하면 된다.

 

읽는 방법은 몇가지가 있다. 차이점을 알아보고 가자.

1)    f.readline()  한 줄씩 읽는다. → 여러줄을 읽기 위해 for문으로 반복필요. 파일 전체의 내용을 하나의 변수에 넣기 위해서는 리스트에 추가를 해야 한다.

2)    f.readlines()  전체를 읽어서 하나의 리스트로 만든다. 리스트의 요소들은 각 줄들이다.

→  각 줄별로 작업을 하기 위해서 for문으로 반복한다.

3)    f.read() 전체를 읽어서 하나의 문자열로 만든다.

→ 각 줄별로 작업을 하기 위해서는 줄로 나눠야 한다.

 

읽을 파일이 어떤 것이냐에 따라 어느 방법을 쓰는 것이 가장 효율적일지 판단해서 선택해야 하지만, 줄별로 작업을 한다면 2번째 방법이 가장 편할 것이다.

 

예를 들어 구조해석 프로그램의 해석결과 파일이 아래와 같다고 하자. 각 노드의 처짐값이다.

   JOINT          UX          UY          UZ          RX          RY          RZ
    
101  351.754000  351.754000  351.754000    8178.447     .000000     .000000
    
102  259.268000  259.268000  259.268000   16356.890     .000000     .000000
    
103  259.268000  259.268000  259.268000   16356.890     .000000     .000000
    
104  259.268000  259.268000  259.268000   16356.890     .000000     .000000
    
105  243.063000  243.063000  243.063000   14312.280     .000000     .000000

 

파일이름이 result.txt다. 이 파일의 위치는 d:\dev\python\result.txt다. 이 파일을 읽어서 각 노드의 변위값을 구하는 기능을 만들고자 한다.

 

두번째 방법 readlines()을 써서 읽어보자. 텍스트에디터를 열어 아래의 코드를 입력하자.

f=open("d:/dev/python/result.txt", 'r')
result=f.readlines()

f.close()
print(result)

 

프로그램의 내용은 파일을 open()으로 열고 f.readlines()로 읽어서 result라는 변수에 넣고 파일을 닫고, 마지막으로 print로 출력하라는 내용이다.

 

readresult.py라는 이름으로 저장한다음. 실행시키면 아래와 같은 결과를 볼 수 있다.

 붉은 색 원들을 주목하자. 제일 처음에 [로 시작했고 마지막에 ]로 끝났다. [로 시작하고 ]로 끝나는 건 리스트라는 뜻이다. 리스트 안에 ‘~’인 문자열들이 ,로 구분되어 들어가 있다. 즉 한줄이 한 문자열로 들어가 있는 것이다. 그런데 문자열의 마지막을 보면 ‘\n’이 있다. 문자열 안에 있는 \n은 새로운 라인의 즉 줄바꿈의 뜻이다. readlines로 읽으면 문자열의 맨 끝에 줄바꿈문자가 포함된다는 것을 알 수 있다.

 

이번에는 result라는 list에 들어있는 각 문자열을 출력해보자. 앞에서는 result라는 list를 통째로 출력했는데 이번에는 각 줄을 출력하려고 한다. 리스트 안에 있는 요소들에 대해서 어떤 행위를 하려면 for문으로 반복하면 된다.

f=open("d:/dev/python/result.txt", 'r')
result=f.readlines()
f.close()
for iresult in result:
  print(iresult)

 

앞에서 공부한 for문을 썼다. 들여쓰기 주의하고 리스트 안의 각 요소를 iresult라는 변수에 넣고 그것을 print문으로 출력했다. 읽었던 걸 그대로 출력하는 프로그램이다.

결과는 아래와 같다.

 

원본파일과 다르다. 줄 사이에 빈 줄이 하나씩 추가되었다. 왜 그럴까? 앞에서 말한 문자열 마지막의 ‘\n’때문이다. print문 자체가 출력하고 한줄 아래로 내려가는 기능이 있는데 문자열 안에 ‘\n’때문에 한줄 더 내려간 것이다.

 

strip()

그래서 ‘\n’을 제거를 해주는 것이 좋다. 파이썬에는 문자열의 앞 뒤를 정리해주는 기능이 있다. 바로 strip()이다. 사용법은 간단하다. 문자열에 .strip()해주면 된다.

f=open("d:/dev/python/result.txt", 'r')
result=f.readlines()
f.close()
for iresult in result:
  print(iresult.strip())

 

iresult.strip() → readresult1.py에서 이것만 바꿨다. 실행결과는 아래와 같다.

 

줄 사이에 빈 줄이 있던 것이 빈 줄이 없어졌다. ‘\n’이 제거 된 것이다.

그런데 원본과 좀 다르다. 맨앞의 공백들이 없어졌다. 그렇다 strip()기능은 앞뒤 공백을 없애주는 기능이기 때문에 앞의 공백들이 지워졌다.

 

split()

이제 각 줄의 데이터들을 쪼개서 리스트로 만들어보자. 이래야 각 값들을 이용할 수 있다. 이 기능은 말그대로 split()기능을 쓰면 된다.

f=open("d:/dev/python/result.txt", 'r')
result=f.readlines()
f.close()
for iresult in result:
  isplt=iresult.split()
  print(isplt)

 

readresult2.py와 다른 점은 iresult를 split()했다는 것이다. split한 결과를 isplt에 넣었다. split을 하면 리스트가 만들어진다. 따라서 split한 결과가 들어가는 isplt는 리스트다. 이것을 print문으로 출력했다.

 

split기능은 기본적으로 공백을 기준으로 나눈다. 문자열 뒤에 .split()이렇게 입력해주면 공백으로 나누고 split(‘,’) 이렇게 입력하면 ‘,’를 기준으로 나눈다.

 

‘1,2,3’.split(‘,’) → 결과는 [‘1’, ’2’, ’3’]이렇게 된다.

  

기억해야 할 것은 split결과가 [와 ]로 감쌓여진 리스트로 돌려준다는 것이다. 그리고 문자열을 쪼갰기 때문에 당연히 그 결과도 문자열의 리스트라는 것이다.

 

우리는 이 문자열을 숫자로 바꿔야 계산에 써먹을 수 있다. 그래서 형변환을 이용한다. 첫 시간에 형변환에 대해서 공부한 바 있다. int(), float(). 각각 문자열을 정수로 바꾸고 실수로 바꾸는 함수다.

 

첫번째 줄 ['JOINT', 'UX', 'UY', 'UZ', 'RX', 'RY', 'RZ'] 는 형변환이 필요 없다. 두번째 줄부터 형변환을 해보자. 위의 코드에서 for문의 대상을 두번째 줄부터 끝까지로 하기 위해서 for iresult in result[1:]: 이렇게 수정했다.  result가 리스트인데 그중에서 두번째(index=1)부터 마지막까지의 뜻인 [1:]를 붙였다.

f=open("d:/dev/python/result.txt", 'r')
result=f.readlines()
f.close()
for iresult in result[1:]:
  isplt=iresult.split()
  inode=int(isplt[
0])
  idisp=list(map(float,isplt[
1:]))
  inodedisp=[inode]+idisp
  print(inodedisp)

 

위 코드를 readresult4.py로 저장하고 실행하면 아래와 같은 결과를 볼 수 있다.

맨 앞의 node는 정수형 그다음 UX, UY, UZ, RX, RY, RZ는 실수형으로 변환했다.

inode=int(isplt[0])  ← isplt는 split한 결과 즉 list다 그것의 처음요소를 취하고 isplt[0] 그것에 int함수를 적용했다. 정수로 변환한 것. 그것을 inode에 넣었다.

 

idisp=list(map(float,isplt[1:]))  ← 좀 복잡한데 map함수를 알아야 한다.

 

list(map(함수명,list))  : 맵함수 사용법은 왼쪽과 같다. list의 각 요소에 함수를 적용해서 그 결과를 리스트로 돌려준다.

예를 들어보자. python 인터프리터를 켜고 테스트해보자.

 

(base) D:\dev\python>python
Python
3.9.7 (default, Sep 16 2021, 16:59:28) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
Type
"help", "copyright", "credits" or "license" for more information.
>>> list(map(int,['1','2']))
[
1, 2]
>>>

 

[‘1’, ‘2’]라는 리스트의 각 요소에 int함수를 적용해서 그것을 리스트로 만들어 돌려준다. 결과가 [1,2]로 나온 걸 볼 수 있다.

 

idisp=list(map(float,isplt[1:]))  ← 그래서 이 문장은 isplt의 두번째부터 (첫번째는 node라서 따로 정수로 변환했었다) 마지막까지를 실수형으로 변환(float)해서 리스트로 만들어 idisp에 저장했다.

 

자 이제 inode에는 노드번호 정수가 idisp에는 변위 실수리스트가 들어있다.

 

두개를 더해서 하나의 리스트로 만들기 위해서 +연산자를 쓰면 된다.

 

리스트 더하기(연결하기)

inode+idisp 이렇게 하면 에러가 뜬다. inode는 그냥 정수값이고 idisp는 리스트이기 때문이다. 그래서 inode에 [ ]를 씌워서 리스트로 만들어준 후 +연산자를 쓴다.

 

inodedisp=[inode]+idisp

 

이렇게 해서 파일을 읽어서 노드번호와 처짐값 6개를 리스트로 묶는 작업을 했다.

 

그런데 이 프로그램은 아직 미완성이다. 왜냐하면 for문으로 한줄씩 변환한 다음에 출력만 했지 변환된 결과를 저장하지 않았다. 나중에 값들을 불러와서 써먹으려면 각 줄을 읽어서 변환한 결과를 리스트로 만들어서 저장하는 것이 필요하다.

f=open("d:/dev/python/result.txt", 'r')
result=f.readlines()
f.close()
nodedisps=[]
for iresult in result[1:]:
  isplt=iresult.split()
  inode=int(isplt[
0])
  idisp=list(map(float,isplt[
1:]))
  inodedisp=[inode]+idisp
  nodedisps.append(inodedisp) 
print(nodedisps)

 

 readresult4.py와 다른 것은 for문 앞에 nodedisps=[] 과 for문 맨 마지막에 nodedisps.append(inodedisp)가 추가되었다. 그리고 출력은 for문 밖에서 출력했다.

 

append()

nodedisps=[]는 빈 리스트를 정의한 것이다. 그리고 append는 리스트에 리스트를 추가하는 함수다. nodedisps라는 빈 리스트에 inodedisp(리스트다)를 append하는 것이다.

 

append()의 사용법을 python인터프리터로 확인해보자. 코딩을 하다가 테스트를 해볼 때는 자신이 짜고 있는 코드로 테스트해보는 것보다 간단하고 명확하게 테스트해보는 것이 좋다.

 

>>> a=[]
>>> a.append([1,2])
>>> a
[[
1, 2]]

a를 빈 리스트로 설정하고 a에 append([1,2])로 [1,2]리스트를 append했다.

결과는 리스트 안에 [1,2]리스트가 추가되었다.

그런다음 [2,4]리스트를 append해보자. 큰 리스트안에 작은 리스트 두개가 들어가 있는 걸 확인할 수 있다.

>>> a.append([3,4])
>>> a
[[
1, 2], [3, 4]]

 

다시 우리 코드로 돌아와서… for문을 다 돌면 큰 리스트안에 읽은 줄 수 만큼의 리스트들이 추가될 것이다. 그리고 for문 밖에서 전체 리스트를 출력했다. 실행결과는 아래와 같다. 큰 리스트 안에 작은 리스트 5개가 들어있는 구조다.

 

이후에 두번째 노드(102)의 3번째(UZ) 변위값을 알고 싶다면 nodedisps[1][2]를 적용하면 된다. 사실 이와 같이 몇 번째라는 방법으로 값을 호출하는 방법은 파이썬의 기능을 효율적으로 사용하는 방법은 아니다. pandas라는 모듈의 dataframe을 이용하면 훨씬 쉽게 원하는 노드의 원하는 변위성분을 호출할 수 있다. padas는 나중에 다루기로 하겠다.

 

지금까지 파일을 읽어서 써먹을 수 있는 숫자로 바꾸는 작업을 해봤다. 그 과정에서 split(), map(), strip(), append(), 리스트더하기(+) 등을 살펴봤다. 파일을 읽으면 문자열로 읽기 때문에 그것을 값으로 바꾸는 과정을 거쳐야 한다. 이번에 살펴본 코드와 비슷한 패턴으로 진행되므로 기억해둘 필요가 있다.


한개의 게시물에 올릴 수 있는 글의 양이 제한되 있어서 파일 쓰기는 다음 게시물로 옮겼습니다.



이석종
한개 게시물에 올릴 수 있는 글의 양이 제한되 있어서 '파일 쓰기'는 다음 게시물[5-1] 로 옯겼습니다. 2022-08-21
17:00:54

 


  추천하기   목록보기

Copyright 1999-2023 Zeroboard / skin by zero
구조설계의 미래를 준비하는 모임 [구조설계미래포럼]