[ruby] Ruby HEREDOC에서 선행 공백 문자를 제거하려면 어떻게해야합니까?
내가 만들려고하는 Ruby heredoc에 문제가 있습니다. 모든 선행 공백 문자를 억제해야하는-연산자를 포함하더라도 각 줄에서 선행 공백을 반환합니다. 내 방법은 다음과 같습니다.
def distinct_count
<<-EOF
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
end
내 출력은 다음과 같습니다.
=> " \tSELECT\n \t CAST('SRC_ACCT_NUM' AS VARCHAR(30)) as
COLUMN_NAME\n \t,COUNT(DISTINCT SRC_ACCT_NUM) AS DISTINCT_COUNT\n
\tFROM UD461.MGMT_REPORT_HNB\n"
물론 이것은 첫 번째 “와 \ t 사이의 모든 공백을 제외하고는이 특정 인스턴스에서 옳습니다. 내가 여기서 뭘 잘못하고 있는지 아는 사람이 있습니까?
답변
<<-
heredoc 의 형식은 끝 구분 기호의 선행 공백 만 무시합니다.
Ruby 2.3 이상에서는 구불 구불 한 heredoc ( <<~
)을 사용하여 콘텐츠 행의 선행 공백을 억제 할 수 있습니다 .
def test
<<~END
First content line.
Two spaces here.
No space here.
END
end
test
# => "First content line.\n Two spaces here.\nNo space here.\n"
Ruby 리터럴 문서에서 :
가장 적게 들여 쓰기 된 줄의 들여 쓰기가 콘텐츠의 각 줄에서 제거됩니다. 리터럴 탭과 공백으로 만 구성된 빈 줄과 줄은 들여 쓰기를 결정하기 위해 무시되지만 이스케이프 된 탭과 공백은 들여 쓰기가 아닌 문자로 간주됩니다.
답변
Rails 3.0 이상을 사용하는 경우 #strip_heredoc
. 문서의이 예제는 들여 쓰기없이 처음 세 줄을 인쇄하고 마지막 두 줄의 두 칸 들여 쓰기를 유지합니다.
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
문서는 또한 “기술적으로는 전체 문자열에서 들여 쓰기가 가장 적은 줄을 찾고 선행 공백을 제거합니다.”라고 설명합니다.
다음은 active_support / core_ext / string / strip.rb 의 구현입니다 .
class String
def strip_heredoc
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
gsub(/^[ \t]{#{indent}}/, '')
end
end
그리고 test / core_ext / string_ext_test.rb 에서 테스트를 찾을 수 있습니다 .
답변
할 일이 많지 않아 두려워요. 나는 보통 :
def distinct_count
<<-EOF.gsub /^\s+/, ""
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
end
작동하지만 약간의 해킹입니다.
편집 : 아래의 Rene Saarsoo에서 영감을 받아 대신 다음과 같은 것을 제안합니다.
class String
def unindent
gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
end
end
def distinct_count
<<-EOF.unindent
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
end
이 버전은 첫 번째 줄이 가장 왼쪽에있는 줄이 아닐 때 처리해야합니다.
답변
내가 사용하는 unindent 스크립트의 훨씬 간단한 버전은 다음과 같습니다.
class String
# Strip leading whitespace from each line that is the same as the
# amount of whitespace on the first line of the string.
# Leaves _additional_ indentation on later lines intact.
def unindent
gsub /^#{self[/\A[ \t]*/]}/, ''
end
end
다음과 같이 사용하십시오.
foo = {
bar: <<-ENDBAR.unindent
My multiline
and indented
content here
Yay!
ENDBAR
}
#=> {:bar=>"My multiline\n and indented\n content here\nYay!"}
첫 번째 줄이 다른 줄보다 더 많이 들여 쓰기 될 수 있고 (Rails와 같이) 가장 적게 들여 쓰기 된 줄을 기준으로 들여 쓰기를 취소하려면 다음을 대신 사용할 수 있습니다.
class String
# Strip leading whitespace from each line that is the same as the
# amount of whitespace on the least-indented line of the string.
def strip_indent
if mindent=scan(/^[ \t]+/).min_by(&:length)
gsub /^#{mindent}/, ''
end
end
end
\s+
대신 스캔 [ \t]+
하면 선행 공백 대신 heredoc에서 줄 바꿈이 제거 될 수 있습니다. 바람직하지 않습니다!
답변
<<-
Ruby에서는 끝 구분 기호의 선행 공백 만 무시하므로 적절하게 들여 쓰기 할 수 있습니다. 일부 온라인 문서에서 설명하는 내용에도 불구하고 문자열 내부 행의 선행 공백을 제거하지 않습니다.
다음을 사용하여 선행 공백을 직접 제거 할 수 있습니다 gsub
.
<<-EOF.gsub /^\s*/, ''
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
또는 공백 만 제거하려면 탭을 그대로 둡니다.
<<-EOF.gsub /^ */, ''
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
답변
다른 답변은 들여 쓰기가 가장 적은 줄 의 들여 쓰기 수준을 찾아 모든 줄에서 삭제하지만 프로그래밍의 들여 쓰기 특성 (첫 번째 줄이 가장 적게 들여 씁니다)을 고려할 때 들여 쓰기 수준을 찾아야한다고 생각합니다 . 첫 번째 줄 .
class String
def unindent; gsub(/^#{match(/^\s+/)}/, "") end
end
답변
원본 포스터처럼 저도 <<-HEREDOC
구문을 발견했고 제가 생각했던대로 동작하지 않는다는 사실에 상당히 실망했습니다.
그러나 gsub-s로 코드를 버리는 대신 String 클래스를 확장했습니다.
class String
# Removes beginning-whitespace from each line of a string.
# But only as many whitespace as the first line has.
#
# Ment to be used with heredoc strings like so:
#
# text = <<-EOS.unindent
# This line has no indentation
# This line has 2 spaces of indentation
# This line is also not indented
# EOS
#
def unindent
lines = []
each_line {|ln| lines << ln }
first_line_ws = lines[0].match(/^\s+/)[0]
re = Regexp.new('^\s{0,' + first_line_ws.length.to_s + '}')
lines.collect {|line| line.sub(re, "") }.join
end
end