perl 정규표현칙 치환 - 텍스트에서 주석문 제거하기

raymundo의 이미지

안녕하세요,

텍스트 파일을 읽고 작업을 하는 CGI코드를 수정하여, 그 텍스트 파일 안에 "#"으로 시작하는 주석을 달 수 있도록 하고 싶습니다. 텍스트를 읽는 루틴과 작업을 하는 루틴 사이에 적절하게 "주석문 제거 루틴"을 넣어서, 주석이 전혀 없는 것과 동일하게 만들어 준다면 뒷부분에 작업을 하는 루틴을 고칠 필요가 없겠죠.

원래의 텍스트 파일은 아래와 같이 한 쌍의 스트링 조합의 나열입니다

aaaaa        AAAAAAAAAAAAAAAAAAAAAAA
bbbb          BBBBBBBBBBBBBBBBBB
ccccccc    CCCCCCCCCCCCCCC

주석은 다음과 같은 형태로 붙일 수 있게 하고자 합니다.

# 줄의 처음부터 #으로 시작하는 주석
aaaaa        AAAAAAAAAAAAAAAAAAAAAAA
        # 앞에 공백이 있어도 허용
bbbb          BBBBBBBBBBBBBBBBBB
ccccccc    CCCCCCCCCCCCCCC   # 이렇게 내용이 있는 줄 뒷부분에 달린 주석

일단 파일을 읽는 루틴은, $data 라는 변수에 전체 텍스트가 다 들어갑니다. (한 줄씩 읽는 게 아니라)

아래는 제가 만든 치환 루틴입니다.

            $data =~ s/^\s+//gm;          # 각 라인의 앞에 공백 제거
            $data =~ s/\s+$//gm;          # 각 라인의 뒤에 공백 제거
            $data =~ s/^#.*$//gm;          # 샵으로 시작하는 라인 제거
            $data =~ s/\s+#.*$//gm;     # 공백 이후 샵으로 시작하면 거기서부터 라인 끝까지 제거

이렇게 했더니만, 치환 후의 결과가 아래와 같이 됩니다.

- 빈 줄!!
aaaaa        AAAAAAAAAAAAAAAAAAAAAAA
- 빈 줄!!
bbbb          BBBBBBBBBBBBBBBBBB
ccccccc    CCCCCCCCCCCCCCC

즉 주석만 있던 라인들이 빈 줄로 존재하게 됩니다. 그래서 결과적으로 이후 작업을 하는 루틴에서 문제가 됩니다. 완전히 저 빈 줄까지 없애줘야 합니다.

그래서 치환 두 번을 더 해야 했습니다.

            $data =~ s/^\s+//gm;
            $data =~ s/\s+$//gm;
            $data =~ s/^#.*$//gm;
            $data =~ s/\s+#.*$//gm;
            $data =~ s/(\r?\n)+/\n/gs;  # 줄바꿈이 하나 이상 있으면 그걸 하나로 치환. 빈 줄제거.
            $data =~ s/^\r?\n//gs;          # 텍스트 제일 앞에 빈 줄 제거

제가 테스트하기에는 일단 원하는 대로 빈 줄도 다 없애고 제일 처음의 텍스트 형식으로 되돌려 주는 것 같습니다만... 스트링을 여섯번이나 스캔하면서 치환을 한다는 것이 매우 기분이 나쁩니다! 텍스트가 그다지 긴 편이 아니라서 지금은 괜찮습니다만, 효율성 면에서도 안 좋을 것 같고요.

좀 더 깔끔하게, 적은 횟수의 치환으로 해결할 수 없을까요?

윤봉환의 이미지

굳이 perl을 쓰지 않아도 된다면
sed 는 어떨까요?

$ sed -e "/^#\|^\s\+#/d; s/#.*$//" TARGET_FILE

깔끔하지 않나요?
ed 도 쓸만하구요..

放下着-----
내려놓으려는 마음도 내려놓기

raymundo의 이미지

제 홈에 쓰고 있는 CGI 프로그램을 수정하는 거여서요... perl로 해야 합니다 ^^; 답변 감사드려요~

좋은 하루 되세요!

lifthrasiir의 이미지

$data =~ s/(?:\r?\n|\r)?[ \t]*#.*?(?=\r?\n|\r)//g;

저는 펄은 못 하지만 펄 정규 표현식은 할 수 있습니다 -,.- 펄 5.8.7에서 잘 돌아 가는 듯 합니다.

- 토끼군

raymundo의 이미지

정규표현식을 좀 공부했는데, 저 (?: (?= 등의 기호만 나오면 여전히 못 알아 듣겠더군요. ^^; 일단 그대로 가져다 테스트를 했습니다.

제가 원문에 언급했던 코멘트들은 정확히 제거해 주는 것 같습니다.

그런데 (제가 원문에서 빼먹은 탓입니다만), 중간 중간 가독성을 위해 빈 줄을 일부러 넣은 경우도 지원해 주길 원했는데 그 경우는 그냥 빈 줄이 나오더군요.
그래서 다른 건 그대로 쓰고 저 #.*? 부분을 다시 괄호로 묶었습니다.

$data =~ s/(?:\r?\n|\r)?[ \t]*(#.*?)?(?=\r?\n|\r)//g;
#.*? 을 (#.*?)?로 변경

혹시 제가 수정한 것 때문에 엉뚱한 부작용이 일어날 수 있을까요? 원문에 언급한 경우 이외의 것을 지울 수 있다던가... (아, 이 텍스트 파일에는 #이 전혀 들어가지 않습니다. 즉 #은 항상 주석을 위해 따로 넣은 경우만 있으니 주석이 아니면서 # 때문에 지워질 염려는 없음)

부작용이 없다면 실제로 홈에 적용할 텐데 아직 제가 고친 것을 제가 믿지를 못하겠군요 ^^; 한 번 살펴봐 주시면 감사하겠습니다. 번거롭게 해드려 정말 죄송합니다.

좋은 하루 되세요!

ai의 이미지

파일을 스칼라 변수에 읽어오도록 하는 특별한 이유가 있습니까? 배열에다 읽어와서 라인 단위로 처리하는 것이 일반적이라고 생각합니다만.. (perldoc -f grep 하시면 바로 나오는 예문처럼요.)

War doesnt determine whos right, just whos left.

raymundo의 이미지

제가 고치려는 프로그램이 UseModWiki 입니다. 그걸 제 홈에 설치한 다음, 제가 적당히 고쳐가면서 부족한 기능을 추가해서 사용하는 건데요.

파일 내용을 통채로 스칼라 변수로 담는 이유는,

첫째로 그 위키 소스가 원래 그렇게 짜여져 있기 때문입니다. ^^; 그러니 기존 코드의 수정을 최소한으로 하기 위해서 어떻게든 스칼라 변수에 담긴 상태에서 주석을 제거하고 다시 기존 코드에 넘기고 싶은 거고,

둘째로 그럼 왜 기존 소스는 그리 되었냐 하면... 그 스칼라를 다시 split 해서 한번에 해쉬변수 쌍으로 바꿔 버리거든요.

%InterSite = split(/\s+/, $data);

즉 파일을 읽어서,
$InterSite{"aaaaa"} = "AAAAAAAAAAAAAAAAAAAAAAA";
$InterSite{"bbbb"} = "BBBBBBBBBBBBBBBBBB";
$InterSite{"ccccccc"} = "CCCCCCCCCCCCCCC";
의 형태로 해쉬변수를 채워넣는 건데, 제가 테스트 해보니 중간에 빈줄이 들어가면 거기서부터 어긋나는지 제대로 채워지지 않더라고요. (제가 펄을 잘 아는게 아니라서 더 자세히는...) 그래서 아예 빈 줄을 없애버리는 과정을 여쭈는 거고요 ^_^

좋은 하루 되세요!

ai의 이미지

그런 사정이라면 위에 토끼군님께서 제시하신 정규표현식을 사용하시면 되겠습니다. 다만 길이가 매우 긴 스칼라 변수에 대해 global match 치환하는 것은 비용이 매우 많이 들어가므로, 아래와 같이 쓰시면 속도상의 이점을 얻을 수 있습니다.

%InterSite = map { s/\s*#.*//; split /\s+/; } grep { /^[^ #]/ } split /\n/, $data;

# 또는 공백으로 시작하지 않는 라인에 대해서 # 문자 이후는 무시하도록 처리합니다.

War doesnt determine whos right, just whos left.

raymundo의 이미지

허걱... 암호문 같습니다 ^^;;;

해석하는 순서가,

$data 를 \n을 구분자로 쪼개어 배열을 만들고
grep 을 서서 "공백 또는 #"이 아닌 글자로 시작하는 원소만 남기고,
남은 배열의 각 원소에 대하여,
공백(의 0번 이상반복)과 #으로 시작하는 경우 그 이하를 없애버리고,
공백을 구분자로 쪼개어 두 개로 가른 후에
앞의 것을 키로 뒤의 것을 값으로 해서 mapping 한다....

는 것이 되는 것으로 보이네요. (맞나요?^^)

도대체 어떤 경우 소괄호를 쓰고 어떤 경우 중괄호를 쓰는지는 예나 지금이나 영 이해가 안 됩니다만 ^^; 확실히 라인단위로 처리를 하는 게 비용은 훨씬 절감할 거라는 것은 알겠습니다~ 감사합니다~

좋은 하루 되세요!

좋은 하루 되세요!

raymundo의 이미지

"(공백)(공백)(공백)aaa(공백)(공백)AAAAA" 와 같이, 줄 앞에 공백이 있더라도 유효한 라인으로 인식을 시키고 싶습니다. 그러나 "[^ #]"을 "[^#]"으로 바꿔주기만 해서는 빈 줄이 남는 문제가 또 생겨서, 일단 "줄 처음에 있는 공백을 다 제거"한 후에 나머지 처리를 하기로 했습니다.

그래서
%InterSite = map { s/\s*#.*//; split /\s+/; } grep { s/^\s*//; /^[^#]/ } split /\n/, $data;

위와 같이 수정했습니다. grep 뒤 중괄호 안에, 공백 제거를 먼저 하고, 그 다음 #으로 시작하지 않는 라인만 잡아내라는 의미로 쓴 건데, 저렇게 쓴게 맞는지요? (제 테스트로는 잘 되는 것 같긴 한데 확신이 없어서...) 저 중괄호 안에 세미콜론을 썼을 때와 컴마를 썼을 때 어떤 차이가 있는지요?

좋은 하루 되세요!

ai의 이미지

수정하신 그대로 사용하시면 원하시는 동작이 이루어 집니다. 중괄호 안에 들어가는 표현에 대해서는 매뉴얼대로

$ perldoc -f grep
       grep BLOCK LIST
       grep EXPR,LIST
               Evaluates the BLOCK or EXPR for each element of LIST (locally setting $_ to each element) and returns the list value consisting of those elements for which the expression evaluated to true.  In scalar context, returns the number of times the expression was true.

block 에 해당하는 것이므로 statement 를 나열한 것 뿐입니다. 최종적인 evaluation 결과가 참인 원소만 배열에서 추려내는 것이구요.

grep(1) 과는 다르게 EXPR, BLOCK 은 정규표현식이 아니라도 관계가 없습니다. 이를테면

my @a = (1,2,3,4,5,6);
@a = grep { $_>4 } @a;

한편 grep 과 다르게 map 은 주어진 LIST 의 원소를 직접 수정하기 때문에 주의하지 않으면 가끔 예상치 못한 결과를 가져오는 경우가 있습니다. map 은 foreach 와 같다고 생각하시면 편합니다.

참고로 perl 5.6 이전에서는 foreach 를 사용해 hash 의 value 를 변경하고자 할 때 perl 5.6 이후의 동작과 다른 부분이 있습니다. 자세한 내용은 perlfaq4 의 How do I process/modify each element of an array? 를 참고하세요.

War doesnt determine whos right, just whos left.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.