← λͺ©λ‘μœΌλ‘œ
How to parse String?

러슀트둜 λ¬Έμžμ—΄ νŒŒμ‹±ν•˜κΈ°

러슀트둜 GraphQL μ„œλ²„ 개발 쀑 λ¬Έμžμ—΄ νŒŒμ‹±μ„ 방법을 μ°Ύμ•„λ³΄λ‹ˆ chars ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ νŒŒμ‹±ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. λ‹€μŒμ€ μ˜ˆμ‹œ μ½”λ“œμž…λ‹ˆλ‹€.

fn main() {
    let text: String = String::from("Hello, μ•ˆλ…•ν•˜μ„Έμš”");

    for (i, c) in text.chars().enumerate() {
        println!("μœ„μΉ˜ {}: {}", i, c);
    }
}

터미널에 λ¬Έμžμ—΄μ΄ ν•˜λ‚˜μ”© 좜λ ₯됨을 확인 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‹€ 문득 chars ν•¨μˆ˜μ˜ λ™μž‘ 원리가 κΆκΈˆν•΄μ‘ŒμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ ν•¨μˆ˜μ˜ μ„€λͺ…을 μ°Ύμ•„λ³΄λ‹ˆ λ‹€μŒκ³Ό 같이 μ„€λͺ…λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

// Returns an iterator over the chars of a string slice.
// As a string slice consists of valid UTF-8, we can iterate through a string slice by char. This method returns such an iterator.
// It's important to remember that char represents a Unicode Scalar Value
// and might not match your idea of what a 'character' is. Iteration over grapheme clusters may be what you actually want.
// This functionality is not provided by Rust's standard library, check crates. io instead.
fn chars() {...}

λ§ˆμ§€λ§‰ λ¬Έμž₯을 μ£Όλͺ©ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ°˜ν™˜λ˜λŠ” λ¬ΈμžλŠ” μœ λ‹ˆμ½”λ“œ 슀칼라 값이며, 일반적인 λ¬Έμžκ°€ 아닐 수 μžˆλ‹€κ³  ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

fn main() {
    let text: String = String::from("α€™α€„α€Ία€Ήα€‚α€œα€¬");

    for (i, c) in text.chars().enumerate() {
        println!("μœ„μΉ˜ {}: {}", i, c);
    }
}
/**
 * μœ„μΉ˜ 0: α€™
 * μœ„μΉ˜ 1: င
 * μœ„μΉ˜ 2: α€Ί
 * μœ„μΉ˜ 3: α€Ή
 * μœ„μΉ˜ 4: ဂ
 * μœ„μΉ˜ 5: α€œ
 * μœ„μΉ˜ 6: ာ
 */

μ‹œκ°μ μœΌλ‘œ λ³΄μ΄λŠ” 문자의 κΈΈμ΄λŠ” 5개, νŒŒμ‹± κ²°κ³ΌλŠ” 7개둜 κ°œμˆ˜κ°€ μƒμ΄ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ ꢁ금증이 μƒκ²ΌμŠ΅λ‹ˆλ‹€. μ™œ μ‹œκ°μ μœΌλ‘œ λ³΄μ΄λŠ” 문자둜 νŒŒμ‹±ν•˜μ§€ λͺ»ν• κΉŒμš”?
κ·Έ 이유λ₯Ό μ•Œμ•„λ³΄κΈ° μœ„ν•΄ chars ν•¨μˆ˜κ°€ λ°˜ν™˜ν•˜λŠ” μœ λ‹ˆμ½”λ“œμ— λŒ€ν•΄ μ•Œμ•„λ΄…μ‹œλ‹€.

μœ λ‹ˆμ½”λ“œλž€?

μ „μ„Έκ³„μ˜ λͺ¨λ“  문자λ₯Ό μΌκ΄€λ˜κ²Œ ν‘œν˜„ ν•  수 μžˆλ„λ‘ μ„€κ³„λœ ꡭ제 ν‘œμ€€ 문자 인코딩 μ²΄κ³„μž…λ‹ˆλ‹€.

논리적 ꡬ쑰

μœ λ‹ˆμ½”λ“œλŠ” 17개 평면(Plane, 0~16번)으둜 λ‚˜λ‰˜λ©° μ΄λŠ” 전체 μ½”λ“œ 곡간을 17개의 λ™μΌν•œ 크기둜 λ‚˜λˆˆ ꡬ역을 μ˜λ―Έν•©λ‹ˆλ‹€. ν˜„μž¬λŠ” 전체 크기 쀑에 μ•½ 15% μ •λ„λ§Œ μ‚¬μš©ν•˜κ³  μžˆμ–΄
μΆ”ν›„ 더 λŠ˜μ–΄λ‚˜λŠ” λ¬Έμžμ— λŒ€ν•΄μ„œ 보좩이 κ°€λŠ₯ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ 각 평면에 ν‘œν˜„ κ°€λŠ₯ν•œ 값을 μœ λ‹ˆμ½”λ“œ μ½”λ“œ 포인트이라 ν•©λ‹ˆλ‹€. 각 평면 λ³„λ‘œμ˜ 역할은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

평면λͺ…μΉ­λ²”μœ„μš©λ„
0κΈ°λ³Έ λ‹€κ΅­μ–΄ 평면<br/>(Basic Multilingual Plane, BMP)U+0000 ~ U+FFFFASCII, ν•œκΈ€, ν•œμž,<br/>λΌν‹΄λ¬Έμž λ“±<br/>κ°€μž₯ 일반적인 λ¬Έμžλ“€
1보좩 λ‹€κ΅­μ–΄ 평면<br/>(Supplementary Multilingual Plane, SMP)U+10000 ~ U+1FFFFκ³ λŒ€ 문자, μŒμ•… 기호,<br/>μˆ˜ν•™ 기호 λ“±
2μƒν˜• 문자 보좩 평면<br/>(Supplementary Ideographic Plane, SIP)U+20000 ~ U+2FFFF희귀 ν•œμž, μΆ”κ°€ ν•œμž
3제3 평면<br/>(Tertiary Ideographic Plane, TIP)U+30000 ~ U+3FFFFκ³ λŒ€ ν•œμž
4-13λ―Έν• λ‹Ή 평면<br/>(Unassigned Planes)U+40000 ~ U+DFFFF미래λ₯Ό μœ„ν•΄ μ˜ˆμ•½
14보좩 특수 μš©λ„ 평면<br/>(Supplementary Special-purpose Plane, SSP)U+E0000 ~ U+EFFFF특수 기호, μ œμ–΄ 문자
15μ‚¬μš©μž μ •μ˜ μ˜μ—­ A<br/>(Supplementary Private Use Area-A)U+F0000 ~ U+FFFFFκ°œμΈμ΄λ‚˜ 쑰직이<br/>자유둭게 μ‚¬μš©
16μ‚¬μš©μž μ •μ˜ μ˜μ—­ B<br/>(Supplementary Private Use Area-B)U+100000 ~ U+10FFFFκ°œμΈμ΄λ‚˜ 쑰직이<br/>자유둭게 μ‚¬μš©

인코딩

μœ λ‹ˆμ½”λ“œλŠ” UTF-8κ³Ό UTF-16의 인코딩 방식을 κ°€μ§‘λ‹ˆλ‹€. 8, 16μ΄λΌλŠ” μˆ«μžλŠ” 문자 μΈμ½”λ”©μ˜ μ΅œμ†Œ λΉ„νŠΈ 수λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. λ‹€μŒμ€ 인코딩 방식에 λ”°λ₯Έ λΉ„νŠΈ μˆ˜μž…λ‹ˆλ‹€.

μœ λ‹ˆμ½”λ“œ λ²”μœ„ν‰λ©΄UTF-8UTF-16
U+0000~U+007F평면 0 (ASCII)1λ°”μ΄νŠΈ2λ°”μ΄νŠΈ
U+0080~U+07FF평면 0 (라틴확μž₯ λ“±)2λ°”μ΄νŠΈ2λ°”μ΄νŠΈ
U+0800~U+FFFF평면 0 (BMP λ‚˜λ¨Έμ§€)3λ°”μ΄νŠΈ2λ°”μ΄νŠΈ
U+10000~U+10FFFF평면 1~15 (보좩 평면)4λ°”μ΄νŠΈ4λ°”μ΄νŠΈ
λŸ¬μŠ€νŠΈμ™€ 일반적인 웹은 UTF-8, μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” UTF-16을 μ‚¬μš©ν•©λ‹ˆλ‹€.
λΈŒλΌμš°μ €μ—μ„œ μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ λ¬Έμžμ—΄μ„ 전달 ν• λ•Œμ—λŠ” μžλ™μœΌλ‘œ UTF-16으둜 λ³€ν™˜ν•©λ‹ˆλ‹€.

μ˜¨μ „ν•œ 문자둜 νŒŒμ‹±

μœ λ‹ˆμ½”λ“œλŠ” 언어에 따라 ν•œ κΈ€μžμ˜ 크기가 가변적이며 νŠΉμˆ˜λ¬Έμžκ°€ 경우 λ³„λ„μ˜ 처리λ₯Ό ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ— 각 언어에 λ§žλŠ” μ™„μ „ν•œ κΈ€μžλ‘œ νŒŒμ‹±ν•˜λŠ” 것은 λ³΅μž‘ν•œ κΈ°λŠ₯μž…λ‹ˆλ‹€.
μ•„λž˜λŠ” unicode-segmentation 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ λ¬Έμžμ—΄μ„ νŒŒμ‹±ν•œ μ˜ˆμ‹œμž…λ‹ˆλ‹€. 라이브러리λ₯Ό μ‚¬μš©ν•˜λ”λΌλ„ λ°œμŒκΈ°ν˜Έκ°€ λΆ„λ¦¬λ˜μ„œ 좜λ ₯λ©λ‹ˆλ‹€.

use unicode_segmentation::UnicodeSegmentation;

fn main() {
    let text = "α€™α€„α€Ία€Ήα€‚α€œα€¬";
    
    for grapheme in text.graphemes(true) {
        println!("{}", grapheme);
    }
}
/**
 * α€™
 * င်္
 * ဂ
 * α€œ
 *  ာ
 */

κ·Έλ ‡λ‹€λ©΄ λ‹€λ₯Έ μ–Έμ–΄λ₯Ό νŒŒμ‹±ν•΄λ΄…μ‹œλ‹€. μ•„λž˜λŠ” ν”„λž‘μŠ€μ–΄μ˜ café을 νŒŒμ‹±ν•œ κ²°κ³Όμž…λ‹ˆλ‹€. ν”„λž‘μŠ€μ–΄μ—μ„œ μ•…μ„ΌνŠΈκ°€ λ¬Έμžμ™€ κ²°ν•©λ˜μ–΄ 좜λ ₯되고 μžˆμŠ΅λ‹ˆλ‹€.

use unicode_segmentation::UnicodeSegmentation;

fn main() {
    let text = "cafΓ©";
    
    for grapheme in text.graphemes(true) {
        println!("{}", grapheme);
    }
}
/**
 * c
 * a
 * f
 * Γ©
 */

μ™œ λ―Έμ–€λ§ˆμ–΄μ™€ ν”„λž‘μŠ€μ–΄μ˜ νŒŒμ‹± κ²°κ³Όκ°€ λ‹€λ₯Έμ§€ μ‚΄νŽ΄λ΄…μ‹œλ‹€.

νŒŒμ‹± 방법

unicode-segmentation λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μœ λ‹ˆμ½”λ“œμ˜ UAX #29 (Unicode Text Segmentation) κ·œμΉ™μ„ 따라 λ¬Έμžμ—΄μ„ νŒŒμ‹±ν•©λ‹ˆλ‹€. 이 κ·œμΉ™μ€ λ¬Έμžμ†Œ ν΄λŸ¬μŠ€ν„°, 단어, λ¬Έμž₯ λ‹¨μœ„λ‘œ λΆ„ν•  λ‹¨μœ„λ₯Ό μ •ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

λ¬Έμžμ†Œ ν΄λŸ¬μŠ€ν„°

λ¬Έμžμ†Œ ν΄λŸ¬μŠ€ν„°λž€ μœ μ €κ°€ μΈμ‹ν•˜λŠ” 문자의 μ΅œμ†Œ λ‹¨μœ„λ‘œ, ν•˜λ‚˜μ˜ 문자λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. ν•œκΈ€μ˜ 경우 "κ°€", 라틴 문자의 경우 "Γ©" 등을 μ˜λ―Έν•©λ‹ˆλ‹€.

단어

λ‹¨μ–΄λŠ” λ¬Έμžμ†Œ ν΄λŸ¬μŠ€ν„°μ˜ μ§‘ν•©μœΌλ‘œ, κ³΅λ°±μ΄λ‚˜ κ΅¬λ‘μ μœΌλ‘œ κ΅¬λΆ„λ©λ‹ˆλ‹€. λ‹¨μ–΄μ˜ κ²½κ³„λŠ” κ³΅λ°±μ΄λ‚˜ κ΅¬λ‘μ μœΌλ‘œ κ΅¬λΆ„λ©λ‹ˆλ‹€.

λ¬Έμž₯

λ¬Έμž₯은 λ‹¨μ–΄μ˜ μ§‘ν•©μœΌλ‘œ, λ§ˆμΉ¨ν‘œ, λŠλ‚Œν‘œ, λ¬ΌμŒν‘œλ‘œ κ΅¬λΆ„λ©λ‹ˆλ‹€. λ¬Έμž₯의 κ²½κ³„λŠ” λ§ˆμΉ¨ν‘œ, λŠλ‚Œν‘œ, λ¬ΌμŒν‘œλ‘œ κ΅¬λΆ„λ©λ‹ˆλ‹€.

문자 속성

λ¬Έμžμ†Œ ν΄λŸ¬μŠ€ν„°λŠ” μ—¬λŸ¬κ°€μ§€ 문자 속성을 κ°€μ§„ 문자둜 κ΅¬μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. μ£Όμš” 속성듀을 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • Extend: μ•ž λ¬Έμžμ™€ κ²°ν•©ν•˜λŠ” 문자 (예: ν”„λž‘μŠ€μ–΄ μ•…μ„ΌνŠΈ)
  • SpacingMark: μžμ‹ μ˜ 곡간을 κ°€μ§€λŠ” ν‘œμ‹œ (예: λ―Έμ–€λ§ˆμ–΄ 일뢀 발음기호)
  • Prepend: λ’€ λ¬Έμžμ™€ κ²°ν•©ν•˜λŠ” 문자
  • L/V/T: ν•œκΈ€ 자λͺ¨
  • Control: μ œμ–΄ 문자

이 κΈ°μ€€μœΌλ‘œ μ–Έμ–΄λ³„λ‘œ μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ΄…μ‹œλ‹€.

  • κ°€ = α„€(L) + α…‘(V)
  • Γ© = e + ́ ́(Extend)
  • င်္ဂ = င(Base) + α€Ί(Extend) + α€Ή(Extend) + ဂ(Base)

μ˜ˆμ‹œλ₯Ό 보면 ν”„λž‘μŠ€μ–΄μ˜ μ•…μ„ΌνŠΈμΈ 경우 Extend둜 λΆ„λ₯˜λ˜μ–΄ 있고, λ―Έμ–€λ§ˆμ–΄μ˜ λ°œμŒκΈ°ν˜ΈλŠ” SpacingMark둜 λΆ„λ₯˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. SpacingMarkλŠ” μžμ‹ μ˜ 곡간을 κ°€μ§€λŠ” ν‘œμ‹œμ΄λ―€λ‘œ, λ‹€λ₯Έ λ¬Έμžμ™€ λΆ„λ¦¬λ˜μ–΄ 좜λ ₯λ©λ‹ˆλ‹€.
κ²°κ΅­ 문자의 속성에 따라 νŒŒμ‹± κ²°κ³Όκ°€ λ‹¬λΌμ§€κ²Œ λ©λ‹ˆλ‹€. λ§Œμ•½ λ―Έμ•ˆλ§ˆμ–΄μ˜ 발음기호λ₯Ό λΆ„λ¦¬ν•˜μ§€ μ•Šκ³  좜λ ₯ν•˜κ³  μ‹Άλ‹€λ©΄ 각 μœ λ‹ˆμ½”λ“œ μ½”λ“œ 포인트λ₯Ό λΆ„μ„ν•˜μ—¬ SpacingMarkλ₯Ό κ²°ν•©ν•˜λŠ” λ‘œμ§μ„ μΆ”κ°€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

정리

  • λ¬Έμžμ—΄μ€ μœ λ‹ˆμ½”λ“œλ‘œ 이루어져 있으며 각 λ¬ΈμžλŠ” μœ λ‹ˆμ½”λ“œ μ½”λ“œ 포인트둜 ν‘œν˜„λ©λ‹ˆλ‹€.
  • μœ λ‹ˆμ½”λ“œ μ½”λ“œ 포인트의 ν¬κΈ°λŠ” 가변적이며, μ½”λ“œ 포인트 λ³„λ‘œ 사전에 μ •μ˜λœ 문자 속성을 κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • μœ λ‹ˆμ½”λ“œ μ½”λ“œ 포인트의 문자 속성에 따라 νŒŒμ‹± κ²°κ³Όκ°€ 달라지며, 문자 속성을 λΆ„μ„ν•˜μ—¬ μ›ν•˜λŠ” κ²°κ³Όλ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • SpacingMark 속성을 κ°€μ§€λŠ” μœ λ‹ˆμ½”λ“œ μ½”λ“œ ν¬μΈνŠΈκ°€ μ‘΄μž¬ν•˜λŠ” 언어인 경우 λ¬Έμžμ—΄μ„ λ‹€λ£° λ•Œ μ£Όμ˜κ°€ ν•„μš”ν•©λ‹ˆλ‹€.
  • κΈ€λ‘œλ²Œ μ„œλΉ„μŠ€μ—μ„œ μ–Έμ–΄λ₯Ό λ‹€λ£° λ•Œμ—λŠ” μ–Έμ–΄μ˜ μœ λ‹ˆμ½”λ“œμ  νŠΉμ„±μ„ μ΄ν•΄ν•˜κ³  λ§Œμ•½ λ³„λ„μ˜ μ˜ˆμ™Έμ²˜λ¦¬κ°€ ν•„μš”ν•˜λ‹€λ©΄ ν•΄λ‹Ή 언어에 λŒ€ν•œ λ³„λ„μ˜ νŒŒμ‹± 라이브러리λ₯Ό ꡬ좕이 ν•„μš”ν•©λ‹ˆλ‹€.