MySQL에서 정규 표현식을 바꾸는 방법은 무엇입니까?


515

~ 500k 행의 테이블이 있습니다. varchar (255) UTF8 열에 filename는 파일 이름이 있습니다.

파일 이름에서 다양한 이상한 문자를 제거하려고합니다. 문자 클래스를 사용한다고 생각했습니다. [^a-zA-Z0-9()_ .\-]

이제 MySQL에 정규식을 통해 바꿀 수있는 함수가 있습니까? REPLACE () 함수와 유사한 기능을 찾고 있습니다. 간단한 예는 다음과 같습니다.

SELECT REPLACE('stackowerflow', 'ower', 'over');

Output: "stackoverflow"

/* does something like this exist? */
SELECT X_REG_REPLACE('Stackoverflow','/[A-Zf]/','-'); 

Output: "-tackover-low"

내가 알고 정규 표현식 / RLIKE , 그러나 그 만 확인 하면 일치가 아니라 어떤 경기입니다.

(나는 할 수 는 "할 SELECT pkey_id,filename FROM foo WHERE filename RLIKE '[^a-zA-Z0-9()_ .\-]'"PHP 스크립트에서하는을 preg_replace다음과 "을 UPDATE foo ... WHERE pkey_id=..."하지만 같은 그 모습 느린 & 추한 해킹을 마지막으로 리조트)


8
: 그것은 2007 기능 요청입니다 bugs.mysql.com/bug.php?id=27389 . 이 기능을 정말로 원한다면 로그인하여 "나에게 영향"버튼을 클릭하십시오. 바라건대 충분한 투표를 할 수 있기를 바랍니다.
— TMS

4
@Tomas : 나는 2009 년에 그것을 둘러 보았을 때 그렇게했습니다. 진전이 없었으므로 분명히 중요한 기능은 아닙니다. (btw Postgres에 있음 : stackoverflow.com/questions/11722995/… )
— Piskvor는 건물을

1
이 질문의 관련 있고 간단한 버전 : stackoverflow.com/questions/6942973/…
— Kzqai

2
연산자 로 구현 된 regexp_split(function + procedure) &을 만들었습니다 . 간단한 조회의 경우 트릭을 수행합니다. 여기에서 찾을 수 있습니다 -UDF가 아닌 MySQL 저장 코드를 사용하는 방법입니다. 알려진 제한 사항에 포함되지 않은 버그를 발견 한 경우 언제든지 문제를여십시오. regexp_replaceREGEXP
— 앨마도

1
다른 SO 스레드 에서이 라이브러리를 찾았습니다 : github.com/mysqludf/lib_mysqludf_preg 가 완벽하게 작동합니다.
— Kyle

답변:


77

로 MySQL을 8.0 이상 당신은 기본적으로 사용할 수있는 REGEXP_REPLACE기능.

12.5.2 정규식 :

REGEXP_REPLACE(expr, pat, repl[, pos[, occurrence[, match_type]]])

패턴 pat에 의해 지정된 정규 표현식과 일치 하는 문자열 expr 에서 발생하는 문자열을 대체 문자열 repl로 바꾸고 결과 문자열을 리턴합니다. 경우 EXPR , 팻 , 또는 REPL이 있다 , 반환 값입니다 .NULLNULL

그리고 정규 표현식 지원 :

이전에는 MySQL 이 Henry Spencer 정규식 라이브러리를 사용하여 정규식 연산자 ( REGEXP, RLIKE) 를 지원했습니다 .

ICU (International Components for Unicode)를 사용하여 정규식 지원이 다시 구현되어 완전한 유니 코드 지원을 제공하며 멀티 바이트 안전합니다. 이 REGEXP_LIKE()함수는 REGEXPand RLIKE연산자 의 방식으로 정규 표현식 일치를 수행 하며 이제는 해당 함수의 동의어입니다. 또한 REGEXP_INSTR(), REGEXP_REPLACE()한 REGEXP_SUBSTR() 기능 매치 위치를 찾아 사용할 수 있으며, 각각 교체 추출 서브 스트링을 수행한다.

SELECT REGEXP_REPLACE('Stackoverflow','[A-Zf]','-',1,0,'c'); 
-- Output:
-tackover-low

DBFiddle 데모


146

MySQL 8.0 이상 :

기본 REGEXP_REPLACE기능을 사용할 수 있습니다 .

이전 버전 :

mysql-udf-regexp 와 같은 UDF (사용자 정의 함수)를 사용할 수 있습니다 .


3
사용자 정의 함수로서 REGEXP_REPLACE? 유망 해 보이며 그것을 조사 할 것입니다. 감사!
— Piskvor는

15
불행히도 mysql-udf-regexp는 멀티 바이트 문자를 지원하지 않는 것 같습니다. regexp_replace ( 'äöõü', 'ä', '')는 실제 텍스트 대신 긴 숫자 문자열을 반환합니다.
— lkraav

3
MySQL 자체는 RegEx 기능을 가진 멀티 바이트 문자를 지원하지 않습니다.
— Brad

4
Windows 사용자 : 여기에 링크 된 UDF 라이브러리가 Windows를 제대로 지원하지 않는 것 같습니다. 설명 된 Windows 설치 방법이 나에게 적합하지 않았습니다.
— Jonathan

2
@lkraav 아래의 lib_mysqludf_preg 라이브러리를 사용해보십시오. 이 장황한 버전은 기본적으로 얼룩을 반환하므로 기본값으로 멀티 바이트 문자 집합이 있는지 모르겠습니다. select cast (TR as char) COLLATE utf8_unicode_ci from (select preg_replace ( '/ ä /', '', 'öõüä') R) T
— gillyspy

124

대신 MariaDB를 사용하십시오. 기능이 있습니다

REGEXP_REPLACE(col, regexp, replace)

참조 MariaDB의 문서 및 PCRE 정규 표현식 향상

정규식 그룹화도 사용할 수 있습니다 (매우 유용하다는 것을 알았습니다).

SELECT REGEXP_REPLACE("stackoverflow", "(stack)(over)(flow)", '\\2 - \\1 - \\3')

보고

over - stack - flow

12
이것은 mariadb 10에서 온 것입니다
— Nick

6
다음에 필요한 경우 전체 열을 변경하는 구문은 다음과 같습니다. UPDATE table SET Name = REGEXP_REPLACE(Name, "-2$", "\\1")이렇게하면 abcxyz-2에서 -2가 전체 열에서 한 번에 제거됩니다.
— Josiah

27
전체 플랫폼을 변경하는 것은 현실적인 해결책이 아닙니다.
— David Baucum

3
@DavidBaucum MariaDB는 MySQL의 드롭 인 대체품입니다. 따라서 "플랫폼 변경"이 아니라 같은 여행을 위해 다른 항공사를 선택하는 것과 비슷합니다
— Benvorth


113

이것을 작동시키는 내 무차별 대입 방법은 다음과 같습니다.

  1. 테이블을 버리고- mysqldump -u user -p database table > dump.sql
  2. 몇 가지 패턴 찾기 및 바꾸기- find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \;파일에서 수행 할 수있는 다른 perl regeular 표현식이 있습니다.
  3. 테이블 가져 오기- mysqlimport -u user -p database table < dump.sql

문자열이 데이터 세트의 다른 곳에 있지 않도록하려면 몇 가지 정규식을 실행하여 모두 비슷한 환경에서 발생하는지 확인하십시오. 또한 심도있는 정보를 잃어버린 것을 실수로 파괴 할 경우를 대비하여 교체를 실행하기 전에 백업을 만드는 것이 어렵지 않습니다.


33
좋아, 그것도 효과가있다. 오프라인 교체는 고려하지 않았습니다. 거기에 좋은 즉시 생각!
— Piskvor는

10
당신이 그런 식으로 찾기를 사용하는 것이 이상하다고 생각됩니다-sed -i 's / old_string / new_string / g'/path/to/dump.sql
— speshak

36
빅 데이터 세트 또는 참조 무결성이있는 경우 매우 위험하고 실용적이지 않습니다. 데이터를 제거한 다음 다시 삽입하려면 참조 무결성을 해제해야합니다. 실제로 데이터베이스도 그대로 둡니다.
— Raul Luna

5
과거 에이 방법을 사용했지만 Raul과 동의합니다. 이는 매우 위험합니다. 문자열이 데이터 세트의 다른 곳에 있지 않다는 것을 확실히 확신해야합니다.
— eggmatters

1
대답 @speshak에 늦게 늦었지만이 파일에 액세스하기로 선택한 이유는 원래 위에서 언급 한 것과 같은 이유로 매우 긴장했기 때문입니다. 당시 "파일 찾기"부분을 "바꾸기"부분에서 분리하면 코드를 제출하기 전에 코드를보다 쉽게 ​​읽을 수있을 것입니다.
— Ryan Ward

42

우리는 정규 표현식을 사용하지 않고이 문제를 해결합니다.이 쿼리는 정확히 일치하는 문자열 만 바꿉니다.

update employee set
employee_firstname = 
trim(REPLACE(concat(" ",employee_firstname," "),' jay ',' abc '))

예:

emp_id employee_firstname

제이 1

2 어치 제이

3 어치

쿼리 결과를 실행 한 후 :

emp_id employee_firstname

1 ABC

2 abc ajay

3 ABC


@yellowmelon 두 쌍의 큰 따옴표는 무엇입니까?
— codecowboy

5
직원 이름 앞뒤에 공백을 채 웁니다. 이를 통해 (space) 직원 이름 (space)을 검색 대치 할 수 있으므로 더 큰 문자열 "ajay"의 일부인 경우 "jay"직원 이름을 포착하지 않아도됩니다. 그런 다음 그는 완료되면 공간을 잘라냅니다.
— Slam

42

최근에 정규 표현식을 사용하여 문자열을 바꾸는 MySQL 함수를 작성했습니다. 다음 위치에서 내 게시물을 찾을 수 있습니다.

http://techras.wordpress.com/2011/06/02/regex-replace-for-mysql/

함수 코드는 다음과 같습니다.

DELIMITER $$

CREATE FUNCTION  `regex_replace`(pattern VARCHAR(1000),replacement VARCHAR(1000),original VARCHAR(1000))
RETURNS VARCHAR(1000)
DETERMINISTIC
BEGIN 
 DECLARE temp VARCHAR(1000); 
 DECLARE ch VARCHAR(1); 
 DECLARE i INT;
 SET i = 1;
 SET temp = '';
 IF original REGEXP pattern THEN 
  loop_label: LOOP 
   IF i>CHAR_LENGTH(original) THEN
    LEAVE loop_label;  
   END IF;
   SET ch = SUBSTRING(original,i,1);
   IF NOT ch REGEXP pattern THEN
    SET temp = CONCAT(temp,ch);
   ELSE
    SET temp = CONCAT(temp,replacement);
   END IF;
   SET i=i+1;
  END LOOP;
 ELSE
  SET temp = original;
 END IF;
 RETURN temp;
END$$

DELIMITER ;

실행 예 :

mysql> select regex_replace('[^a-zA-Z0-9\-]','','2my test3_text-to. check \\ my- sql (regular) ,expressions ._,');

25
위의 요점을 강화하겠습니다.이 함수는 단일 문자 표현식과 일치하는 문자 를 대체합니다 . 그것은 "정규 표현식을 사용하여 문자열을 리 팔링하는 데 사용된다"고 말하며 약간 오도 할 수 있습니다. 그것은 그 일을하지만, 요구되는 일이 아닙니다. (불만은 아닙니다-그것은 단지 잘못된 길을 선도하는 사람들을 구하는 것입니다)
— Jason

2
알몸 링크를 게시하는 대신 답변에 실제로 코드를 포함시키는 것이 더 도움이됩니다.
— phobie

2
멋지지만 불행히도 select regex_replace('.*(abc).*','\1','noabcde')( 'abc'가 아닌 'noabcde'를 반환) 와 같은 참조를 처리하지 않습니다 .
— Izzy

@ pobie 누군가 가이 답변 에서 링크를 죽일 경우를 대비하여 참조로 다른 사람이 그렇게 했습니다 .)
— Izzy

위에서 언급 한 몇 가지 제한 사항을 해결하기 위해이 방법을 수정했습니다. 이 답변을 참조하십시오 .
— Steve Chambers


13

업데이트 2 : REGEXP_REPLACE를 포함한 유용한 정규 표현식 함수 가 MySQL 8.0에서 제공되었습니다. 이전 버전을 사용하도록 제한되어 있지 않으면 불필요하게 읽을 수 있습니다.


업데이트 1 : 이제 이것을 블로그 게시물로 만들었습니다 : http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html


다음 은 Rasika Godawatte가 제공 하는 기능을 확장 하지만 단일 문자를 테스트하는 대신 필요한 모든 하위 문자열을 탐색 합니다.

-- ------------------------------------------------------------------------------------
-- USAGE
-- ------------------------------------------------------------------------------------
-- SELECT reg_replace(<subject>,
--                    <pattern>,
--                    <replacement>,
--                    <greedy>,
--                    <minMatchLen>,
--                    <maxMatchLen>);
-- where:
-- <subject> is the string to look in for doing the replacements
-- <pattern> is the regular expression to match against
-- <replacement> is the replacement string
-- <greedy> is TRUE for greedy matching or FALSE for non-greedy matching
-- <minMatchLen> specifies the minimum match length
-- <maxMatchLen> specifies the maximum match length
-- (minMatchLen and maxMatchLen are used to improve efficiency but are
--  optional and can be set to 0 or NULL if not known/required)
-- Example:
-- SELECT reg_replace(txt, '^[Tt][^ ]* ', 'a', TRUE, 2, 0) FROM tbl;
DROP FUNCTION IF EXISTS reg_replace;
DELIMITER //
CREATE FUNCTION reg_replace(subject VARCHAR(21845), pattern VARCHAR(21845),
  replacement VARCHAR(21845), greedy BOOLEAN, minMatchLen INT, maxMatchLen INT)
RETURNS VARCHAR(21845) DETERMINISTIC BEGIN 
  DECLARE result, subStr, usePattern VARCHAR(21845); 
  DECLARE startPos, prevStartPos, startInc, len, lenInc INT;
  IF subject REGEXP pattern THEN
    SET result = '';
    -- Sanitize input parameter values
    SET minMatchLen = IF(minMatchLen < 1, 1, minMatchLen);
    SET maxMatchLen = IF(maxMatchLen < 1 OR maxMatchLen > CHAR_LENGTH(subject),
                         CHAR_LENGTH(subject), maxMatchLen);
    -- Set the pattern to use to match an entire string rather than part of a string
    SET usePattern = IF (LEFT(pattern, 1) = '^', pattern, CONCAT('^', pattern));
    SET usePattern = IF (RIGHT(pattern, 1) = '$', usePattern, CONCAT(usePattern, '$'));
    -- Set start position to 1 if pattern starts with ^ or doesn't end with $.
    IF LEFT(pattern, 1) = '^' OR RIGHT(pattern, 1) <> '$' THEN
      SET startPos = 1, startInc = 1;
    -- Otherwise (i.e. pattern ends with $ but doesn't start with ^): Set start pos
    -- to the min or max match length from the end (depending on "greedy" flag).
    ELSEIF greedy THEN
      SET startPos = CHAR_LENGTH(subject) - maxMatchLen + 1, startInc = 1;
    ELSE
      SET startPos = CHAR_LENGTH(subject) - minMatchLen + 1, startInc = -1;
    END IF;
    WHILE startPos >= 1 AND startPos <= CHAR_LENGTH(subject)
      AND startPos + minMatchLen - 1 <= CHAR_LENGTH(subject)
      AND !(LEFT(pattern, 1) = '^' AND startPos <> 1)
      AND !(RIGHT(pattern, 1) = '$'
            AND startPos + maxMatchLen - 1 < CHAR_LENGTH(subject)) DO
      -- Set start length to maximum if matching greedily or pattern ends with $.
      -- Otherwise set starting length to the minimum match length.
      IF greedy OR RIGHT(pattern, 1) = '$' THEN
        SET len = LEAST(CHAR_LENGTH(subject) - startPos + 1, maxMatchLen), lenInc = -1;
      ELSE
        SET len = minMatchLen, lenInc = 1;
      END IF;
      SET prevStartPos = startPos;
      lenLoop: WHILE len >= 1 AND len <= maxMatchLen
                 AND startPos + len - 1 <= CHAR_LENGTH(subject)
                 AND !(RIGHT(pattern, 1) = '$' 
                       AND startPos + len - 1 <> CHAR_LENGTH(subject)) DO
        SET subStr = SUBSTRING(subject, startPos, len);
        IF subStr REGEXP usePattern THEN
          SET result = IF(startInc = 1,
                          CONCAT(result, replacement), CONCAT(replacement, result));
          SET startPos = startPos + startInc * len;
          LEAVE lenLoop;
        END IF;
        SET len = len + lenInc;
      END WHILE;
      IF (startPos = prevStartPos) THEN
        SET result = IF(startInc = 1, CONCAT(result, SUBSTRING(subject, startPos, 1)),
                        CONCAT(SUBSTRING(subject, startPos, 1), result));
        SET startPos = startPos + startInc;
      END IF;
    END WHILE;
    IF startInc = 1 AND startPos <= CHAR_LENGTH(subject) THEN
      SET result = CONCAT(result, RIGHT(subject, CHAR_LENGTH(subject) + 1 - startPos));
    ELSEIF startInc = -1 AND startPos >= 1 THEN
      SET result = CONCAT(LEFT(subject, startPos), result);
    END IF;
  ELSE
    SET result = subject;
  END IF;
  RETURN result;
END//
DELIMITER ;

데모

Rextester 데모

한계

  1. 물론이 방법은 제목 문자열이 클 때 시간이 걸립니다. 업데이트 : 최소 및 최대 일치 길이 매개 변수를 추가하여 효율성을 높이도록했습니다 (제로 = 알 수 없음 / 제한 없음).
  2. 이 되지 역 참조 (예 치환 수 \1, \2 촬상기를 대체 등). 이 기능이 필요한 경우, 참조하십시오 이 답변 에서 보조 찾기 및 바꾸기를 허용하도록 기능을 업데이트하여 (복잡성이 증가함에 따라) 해결 방법을 제공하는 을 .
  3. ^및 / 또는 $패턴에서 사용되는 경우 , 각각 시작과 끝에 있어야합니다 (예 : (^start|end$)지원되지 않는 패턴) .
  4. 전체 일치가 욕심인지 아닌지 욕설인지를 지정하는 "greedy"플래그가 있습니다. 단일 정규 표현식 (예 :) 내에서 욕심과 게으른 일치를 결합하는 a.*?b.*것은 지원되지 않습니다.

사용 예

이 함수는 다음 StackOverflow 질문에 답변하는 데 사용되었습니다.


7

당신은 그것을 할 수 있습니다 ...하지만 현명하지는 않습니다 ... 이것은 내가 시도 할만 큼 대담합니다 ... 정규 RegEx가 perl 등을 사용하여 훨씬 더 나은 것을 지원하는 한.

UPDATE db.tbl
SET column = 
CASE 
WHEN column REGEXP '[[:<:]]WORD_TO_REPLACE[[:>:]]' 
THEN REPLACE(column,'WORD_TO_REPLACE','REPLACEMENT')
END 
WHERE column REGEXP '[[:<:]]WORD_TO_REPLACE[[:>:]]'

1
아니요, 작동하지 않습니다. 열에 'asdfWORD_TO_REPLACE WORD_TO_REPLACE'가 포함되어 있다고 가정하면 메서드에 'asdfREPLACEMENT REPLACEMENT'가 표시되고 정답은 "asdfWORD_TO_REPLACE REPLACEMENT"가됩니다.
— Ryan Shillington

1
@Ryan ... 그것이 정확히 현명하지 않다고 말한 이유입니다 ... 유스 케이스에서 이것이 가장 확실하게 실패 할 것입니다. 간단히 말해서 'regex-like'구조를 사용하는 것은 좋지 않습니다. 더 나쁜 ... where 절을 삭제하면 모든 값이 NULL이됩니다 ...
— Eddie B

1
실제로이 경우 Ryan은 길이가 0 인 단어 'boundaries'와 일치하는 단어 만 찾을 수 있으므로 단어의 앞뒤에 경계가있는 단어 만 일치하므로 잘못되었습니다. 그래도 좋지 않은 생각입니다 ...
— Eddie B

6

다음과 같이 SELECT 쿼리에서 IF 조건을 사용할 수 있습니다.

"ABC", "ABC1", "ABC2", "ABC3"등이있는 모든 항목에 대해 "ABC"로 바꾸고 SELECT 쿼리에서 REGEXP 및 IF () 조건을 사용하려는 경우이를 달성 할 수 있습니다. .

통사론:

SELECT IF(column_name REGEXP 'ABC[0-9]$','ABC',column_name)
FROM table1 
WHERE column_name LIKE 'ABC%';

예:

SELECT IF('ABC1' REGEXP 'ABC[0-9]$','ABC','ABC1');

안녕하세요, 제안 해 주셔서 감사합니다. 비슷한 것을 시도했지만 데이터 세트의 성능이 만족스럽지 않았습니다. 작은 세트의 경우 이것이 가능할 수 있습니다.
— Piskvor는

3

아래의 것은 기본적으로 왼쪽에서 첫 번째 일치를 찾은 다음 모든 일치를 대체합니다 ( ).

용법:

SELECT REGEX_REPLACE('dis ambiguity', 'dis[[:space:]]*ambiguity', 'disambiguity');

이행:

DELIMITER $$
CREATE FUNCTION REGEX_REPLACE(
  var_original VARCHAR(1000),
  var_pattern VARCHAR(1000),
  var_replacement VARCHAR(1000)
  ) RETURNS
    VARCHAR(1000)
  COMMENT 'Based on https://techras.wordpress.com/2011/06/02/regex-replace-for-mysql/'
BEGIN
  DECLARE var_replaced VARCHAR(1000) DEFAULT var_original;
  DECLARE var_leftmost_match VARCHAR(1000) DEFAULT
    REGEX_CAPTURE_LEFTMOST(var_original, var_pattern);
    WHILE var_leftmost_match IS NOT NULL DO
      IF var_replacement <> var_leftmost_match THEN
        SET var_replaced = REPLACE(var_replaced, var_leftmost_match, var_replacement);
        SET var_leftmost_match = REGEX_CAPTURE_LEFTMOST(var_replaced, var_pattern);
        ELSE
          SET var_leftmost_match = NULL;
        END IF;
      END WHILE;
  RETURN var_replaced;
END $$
DELIMITER ;

DELIMITER $$
CREATE FUNCTION REGEX_CAPTURE_LEFTMOST(
  var_original VARCHAR(1000),
  var_pattern VARCHAR(1000)
  ) RETURNS
    VARCHAR(1000)
  COMMENT '
  Captures the leftmost substring that matches the [var_pattern]
  IN [var_original], OR NULL if no match.
  '
BEGIN
  DECLARE var_temp_l VARCHAR(1000);
  DECLARE var_temp_r VARCHAR(1000);
  DECLARE var_left_trim_index INT;
  DECLARE var_right_trim_index INT;
  SET var_left_trim_index = 1;
  SET var_right_trim_index = 1;
  SET var_temp_l = '';
  SET var_temp_r = '';
  WHILE (CHAR_LENGTH(var_original) >= var_left_trim_index) DO
    SET var_temp_l = LEFT(var_original, var_left_trim_index);
    IF var_temp_l REGEXP var_pattern THEN
      WHILE (CHAR_LENGTH(var_temp_l) >= var_right_trim_index) DO
        SET var_temp_r = RIGHT(var_temp_l, var_right_trim_index);
        IF var_temp_r REGEXP var_pattern THEN
          RETURN var_temp_r;
          END IF;
        SET var_right_trim_index = var_right_trim_index + 1;
        END WHILE;
      END IF;
    SET var_left_trim_index = var_left_trim_index + 1;
    END WHILE;
  RETURN NULL;
END $$
DELIMITER ;

3

나는 이것을 달성하는 쉬운 방법이 있다고 생각하며 그것은 나에게 잘 작동한다.

REGEX를 사용하여 행을 선택하려면

SELECT * FROM `table_name` WHERE `column_name_to_find` REGEXP 'string-to-find'

REGEX를 사용하여 행을 업데이트하려면

UPDATE `table_name` SET column_name_to_find=REGEXP_REPLACE(column_name_to_find, 'string-to-find', 'string-to-replace') WHERE column_name_to_find REGEXP 'string-to-find'

REGEXP 참조 : https://www.geeksforgeeks.org/mysql-regular-expressions-regexp/


주셔서 감사합니다 :) 버전 8부터 쉽게 할 수 있습니다
— Piskvor 건물 왼쪽
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.