표현식 기반 탐색 후 변환(Search & Replace) 도구
변수만 다르고 형태는 같은 코드가 여러 개 있을 때 사용하면 손을 덜 귀찮게 해줍니다.
어떤 일을 하는 도구인지 예시로 설명을 시작하겠습니다.
리스트를 세 개 만들었다고 하면,
new_listA = ds_list_create();
new_listB = ds_list_create();
new_listC = ds_list_create();
이 리스트를 지우기 위해서
ds_list_destroy(new_listA);
ds_list_destroy(new_listB);
ds_list_destroy(new_listC);
위와 같이 세 줄의 코드가 추가되었습니다. 여기까지는 쉽습니다.
이제 이 리스트를 21개 더 만들었다고 생각해봅시다. 그러면 리스트 삭제 코드를 21번 더 붙여 넣기를 해야 합니다. 생각만 해도 귀찮은 작업입니다. 하지만 이 도구를 사용한다면 파일 위치를 복사하는 게 더 귀찮은 일이 될 겁니다.
도구를 실행하면, 먼저 읽어낼 파일 경로와 내보낼 파일 경로를 입력해야 합니다.
이후에는 두 개의 표현식을 필요로 합니다.
첫 번째는 변경할 대상을 찾는 표현식 (탐색 표현식)
두 번째는 변환하여 나타내질 표현식입니다. (변환 표현식)
앞서 리스트 생성 코드와 리스트 삭제 코드를 보면,
new_listA = ds_list_create();
ds_list_destroy(new_listA);
수식 부분은 바뀌었지만 변수는 같은 것을 알 수 있습니다.
이에 맞춰 식을 쓴다면 다음처럼 나타낼 수 있습니다.
변수1 = ds_list_create();
ds_list_destroy(변수1);
이 도구에서는 표현식 내의 변수를 <?> 형태로 정의합니다. 즉, 첫 번째 변수(변수1)은 <0> 으로 나타내야 합니다. 이에 맞춰 다시 표현 식을 고치면, 다음과 같이 나타낼 수 있습니다.
<0> = ds_list_create();
ds_list_destroy(<0>);
그러면 리스트 생성 코드만 작성해도 이 도구를 이용하여 바로 리스트 삭제 코드를 만들어 낼 수 있습니다.
//input.txt
aaa = ds_list_create();
bbb = ds_list_create();
//expression
<0> = ds_list_create();
ds_list_destroy(<0>);
//ouput.txt
ds_list_destroy(aaa);
ds_list_destroy(bbb);
주석을 이용한다면 더 많은 것도 할 수 있습니다.
다시 한 번 리스트 세 개를 만들었습니다.
//input
new_listA = ds_list_create(); // 1
new_listB = ds_list_create(); // 2
new_listC = ds_list_create(); // 3
//output
ds_list_add(new_listA, 1);
ds_list_add(new_listB, 2);
ds_list_add(new_listC, 3);
위의 결과가 나오려면 표현식을 어떻게 써야 할까요?
//expression
변수1 = ds_list_create(); // 변수2
ds_list_add(변수1, 변수2);
//expression
<0> = ds_list_create(); // <1>
ds_list_add(<0>, <1>);
조금만 고민한다면 이렇게 일반화된 표현식을 생각할 수 있습니다.
사실 이 도구는 lerp 때문에 화가 나서 만들게 되었습니다.
//fileA.txt
a_value = 0;
a_target = 10;
a_velocity = 10;
a_value += (a_target - a_value)/a_velocity;
//fileB.txt
a_value = 0;
a_target = 10;
a_velocity = 10;
a_value = lerp(a_value, a_target, 1/a_velocity);
두 코드는 a_value가 부드럽게 변경될 수 있도록 하는 코드입니다.
그러나 fileA처럼 쓰기보다는 fileB처럼 쓰면 더 깔끔하고 보기 편하다는 장점이 있습니다. 무엇보다 속력 값이 0 ~ 1로 제한되어 있어 fileA처럼 속력 값이 음수이거나 소수점 아래일 때 값이 튕겨나가는 오류는 발생하지 않습니다.
fileA의 코드를 fileB처럼 바꾸려면 일단 a_value 를 a_target 의 왼쪽으로 옮기고 - 를 지웁니다. 그리고 그 사이에 쉼표를 넣고, a_target 오른쪽에도 쉼표를 넣습니다. 이제 닫는 소괄호를 a_velocity 와 ; 사이로 옮기고, += 를 = 으로 바꿉니다. 그리고 이걸 파일 내의 모든 같은 형태의 코드에 다시 반복해야 합니다.
생각만 해도 화가 나지 않습니까?
그렇다면 제 노가다를 대신 뛰어주는 멋진 도구를 사용해봅시다.
fileA의 수식을 fileB처럼 바꾸려면 어떻게 일반화를 해야 할까요?
//expression
<0> += (<1> - <0>)/<2>;
<0> = lerp(<0>, <1>, 1/<2>);
짜잔, 위와 같은 표현식으로 간단하게 일반화 할 수 있습니다. 복사 붙여 넣기를 하느라 키보드와 마우스를 혹사 시키지 않아도 된다구요.
안타깝게도 버그 테스트조차 하지 않았습니다. 제가 쓰는 코드에는 그렇게 멋진 기능이 필요하진 않았으니까요.
몇 가지 주의 사항이 있습니다.
1. 표현식과 완전히 동일한 항목만 탐색 (띄어쓰기가 다르다면 탐색 되지 않습니다.)
2. 변환할 때 사용된 변수의 개수 <= 탐색할 때 사용된 변수의 개수
3. 내용이 바뀐 줄에 대해 들여쓰기 사라짐 (탭 키 누르는 것이 코드 고치는 것 보다 쉽잖아요.)
4. Python이 있어야 함.
코드 전문 - AutoReplace.py
from io import FileIO
#import sys
#import os
#import time
import re
#import codecs
from typing import Pattern
def string2re(_string) -> str:
_string = _string \
.replace("[", "[[]").replace("]", "[]]").replace("[[[]]","[[]") \
.replace("(", "[(]").replace(")", "[)]") \
.replace("{", "[{]").replace("}", "[}]") \
.replace("|", "[|]").replace("^", "[^]").replace("?", "[?]").replace("!", "[!]").replace("$", "[$]") \
.replace("+", "[+]").replace("-", "[-]").replace("*", "[*]") \
.replace(".", "[.]").replace("\\", r"[\]") \
.replace(" ", "")
return _string
def run() -> None:
file_directory_input: str = input("Input File Directory: \n")
file_directory_output: str = input("Output File Directory: \n")
file_input: FileIO = open(file_directory_input, "r", encoding = "UTF8")
file_output: FileIO = open(file_directory_output, "w", encoding = "UTF8")
file_content: list[str] = file_input.read().splitlines()
file_input.close()
find_index = None
find_value = list()
expression_find_str: str = string2re(input("Input Expression to Find: \n"))
expression_find_re: Pattern = re.compile("<[0-9]?>")
expression_find_str_: str = expression_find_re.sub("<?>", expression_find_str).replace("<?>", r"(.+)")
expression_find_re_: Pattern = re.compile(expression_find_str_)
print("Find with '{}'".format(expression_find_str_))
find_index = expression_find_re.findall(expression_find_str)
for _index in range(0, len(find_index)):
find_index[_index] = int(find_index[_index].replace("<", "").replace(">",""))
for _index in find_index:
find_value.append("")
expression_replace_str: str = input("Input Expression to Replace: \n")
for _index in range(0, len(file_content)):
_content = file_content[_index].replace("\t","").replace("\n","").rstrip().replace(" ","")
_search = expression_find_re_.search(_content)
if _search:
print(_search.group(0), end = " => ")
for __index in range(0, len(find_index)):
find_value[find_index[__index]] = _search.group(__index+1)
file_content[_index] = expression_replace_str
for __index in find_index:
file_content[_index] = file_content[_index].replace("<{}>".format(__index), find_value[__index])
print(file_content[_index])
for _content in file_content:
file_output.write(_content+"\n")
file_output.close()
### Expressions
### <0> = lerp(<0>, <1>, <2>);
### <0> += (<1> - <0>)/<2>;
run()
'작업실 > 도구' 카테고리의 다른 글
SFGenerator.exe: 스프라이트 폰트 생성기 (0) | 2021.12.23 |
---|---|
Merge2CSV.py: CSV 병합 도구 (0) | 2021.09.13 |
Android SAF External File Control: Gamemaker Studio 2 Extension 한국어 문서 (0) | 2021.09.06 |
댓글