다음을 통해 공유


System.Text.Rune 구조체

이 문서는 이 API에 대한 참조 설명서를 보충하는 추가 설명을 제공합니다.

인스턴스는 Rune 서로게이트 범위(U+D800에서 U+DFFF)를 제외한 모든 코드 포인트를 의미하는 유니코드 스칼라 값을 나타냅니다. 형식의 생성자 및 변환 연산자는 입력의 유효성을 검사하므로 소비자는 기본 인스턴스가 잘 형성되어 있다고 가정하여 API를 Rune 호출할 수 있습니다.

유니코드 스칼라 값, 코드 포인트, 서로게이트 범위 및 잘 구성된 용어에 익숙하지 않은 경우 .NET의 문자 인코딩 소개를 참조하세요.

Rune 형식을 사용하는 경우

코드가 다음과 같은 경우 형식을 Rune 사용하는 것이 좋습니다.

  • 유니코드 스칼라 값이 필요한 API 호출
  • 유니코드 대리쌍을 명시적으로 처리합니다.

유니코드 스칼라 값이 필요한 API

코드가 char 또는 string 내의 ReadOnlySpan<char> 인스턴스를 반복하는 경우, 일부 char 메서드는 서로게이트 범위에 있는 char 인스턴스에서 제대로 작동하지 않습니다. 예를 들어 다음 API는 스칼라 값 char 이 올바르게 작동해야 합니다.

다음 예제에서는 각 인스턴스가 서로게이트 코드 포인트일 경우 제대로 작동하지 않는 코드를 보여 줍니다.

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
int CountLettersBadExample(string s)
{
    int letterCount = 0;

    foreach (char ch in s)
    {
        if (char.IsLetter(ch))
        { letterCount++; }
    }

    return letterCount;
}
// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
let countLettersBadExample (s: string) =
    let mutable letterCount = 0

    for ch in s do
        if Char.IsLetter ch then
            letterCount <- letterCount + 1
    
    letterCount

다음은 ReadOnlySpan<char>와 작동하는 동등한 코드입니다.

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.
// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static int CountLettersBadExample(ReadOnlySpan<char> span)
{
    int letterCount = 0;

    foreach (char ch in span)
    {
        if (char.IsLetter(ch))
        { letterCount++; }
    }

    return letterCount;
}

앞의 코드는 영어와 같은 일부 언어에서 올바르게 작동합니다.

CountLettersInString("Hello")
// Returns 5

그러나 Osage와 같은 기본 다국어 평면 외부의 언어에서는 제대로 작동하지 않습니다.

CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0

이 메서드가 Osage 텍스트에 대해 잘못된 결과를 반환하는 이유는 Osage 문자의 char 인스턴스가 서로게이트 코드 포인트이기 때문입니다. 단일 서로게이트 코드 지점에는 문자인지 여부를 확인하기에 충분한 정보가 없습니다.

Rune를 대신 사용하도록 이 코드를 변경하면 메서드는 기본 다국어 평면을 벗어난 코드 포인트에서도 올바르게 작동합니다.

int CountLetters(string s)
{
    int letterCount = 0;

    foreach (Rune rune in s.EnumerateRunes())
    {
        if (Rune.IsLetter(rune))
        { letterCount++; }
    }

    return letterCount;
}
let countLetters (s: string) =
    let mutable letterCount = 0

    for rune in s.EnumerateRunes() do
        if Rune.IsLetter rune then
            letterCount <- letterCount + 1

    letterCount

다음은 ReadOnlySpan<char>와 작동하는 동등한 코드입니다.

static int CountLetters(ReadOnlySpan<char> span)
{
    int letterCount = 0;

    foreach (Rune rune in span.EnumerateRunes())
    {
        if (Rune.IsLetter(rune))
        { letterCount++; }
    }

    return letterCount;
}

앞의 코드는 Osage 문자를 올바르게 계산합니다.

CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8

서로게이트 쌍을 명시적으로 처리하는 코드

코드가 Rune 서로게이트 코드 지점에서 명시적으로 작동하는 API를 호출하는 경우(예: 다음 메서드) 형식을 사용하는 것이 좋습니다.

예를 들어 다음 메서드에는 서로게이트 char 쌍을 처리하는 특수 논리가 있습니다.

static void ProcessStringUseChar(string s)
{
    Console.WriteLine("Using char");

    for (int i = 0; i < s.Length; i++)
    {
        if (!char.IsSurrogate(s[i]))
        {
            Console.WriteLine($"Code point: {(int)(s[i])}");
        }
        else if (i + 1 < s.Length && char.IsSurrogatePair(s[i], s[i + 1]))
        {
            int codePoint = char.ConvertToUtf32(s[i], s[i + 1]);
            Console.WriteLine($"Code point: {codePoint}");
            i++; // so that when the loop iterates it's actually +2
        }
        else
        {
            throw new Exception("String was not well-formed UTF-16.");
        }
    }
}

이러한 코드는 다음 예제와 같이 사용하는 Rune경우 더 간단합니다.

static void ProcessStringUseRune(string s)
{
    Console.WriteLine("Using Rune");

    for (int i = 0; i < s.Length;)
    {
        if (!Rune.TryGetRuneAt(s, i, out Rune rune))
        {
            throw new Exception("String was not well-formed UTF-16.");
        }

        Console.WriteLine($"Code point: {rune.Value}");
        i += rune.Utf16SequenceLength; // increment the iterator by the number of chars in this Rune
    }
}

사용할 필요가 없는 경우 Rune

코드에서 Rune 타입을 사용할 필요가 없습니다.

  • 정확한 char 일치 항목을 찾습니다.
  • 알려진 char 값에 문자열을 분할합니다.

Rune 형식을 사용하면 코드가 다음과 같은 경우 잘못된 결과를 반환할 수 있습니다.

  • 의 표시 문자 수를 계산합니다. string

정확한 char 일치 항목 찾기

다음 코드는 검색된 특정 문자를 반복 string 하여 첫 번째 일치 항목의 인덱스를 반환합니다. 코드가 단일 Rune문자로 표현되는 문자를 찾고 있으므로 이 코드를 사용하도록 char변경할 필요가 없습니다.

int GetIndexOfFirstAToZ(string s)
{
    for (int i = 0; i < s.Length; i++)
    {
        char thisChar = s[i];
        if ('A' <= thisChar && thisChar <= 'Z')
        {
            return i; // found a match
        }
    }

    return -1; // didn't find 'A' - 'Z' in the input string
}

알려진 조건에 따라 문자열을 분할합니다 char

다음 예제와 같이 (공간) 또는 string.Split (쉼표)와 같은 ' ' 구분 기호를 호출 ',' 하고 사용하는 것이 일반적입니다.

string inputString = "🐂, 🐄, 🐆";
string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');

코드에서 단일 Rune문자로 표현되는 문자를 찾고 있으므로 여기서 사용할 char 필요가 없습니다.

디스플레이 문자 수를 계산하기 string

문자열의 Rune 인스턴스 수가 문자열을 표시할 때 표시되는 사용자 인식 가능 문자 수와 일치하지 않을 수 있습니다.

Rune 인스턴스는 유니코드 스칼라 값을 나타내므로 유니코드 텍스트 구분 지침을 따르는 구성 요소는 표시 문자를 계산하기 위한 구성 요소로 사용할 Rune 수 있습니다.

이 형식은 StringInfo 표시 문자 수를 계산하는 데 사용할 수 있지만 .NET 5 이상 이외의 .NET 구현에 대한 모든 시나리오에서 올바르게 계산되지는 않습니다.

자세한 내용은 Grapheme 클러스터를 참조하세요.

Rune를 인스턴스화하는 방법

인스턴스를 가져오는 방법에는 여러 가지가 있습니다 Rune . 생성자를 사용하여 Rune를 직접 만들 수 있습니다.

  • 코드 포인트입니다.

    Rune a = new Rune(0x0061); // LATIN SMALL LETTER A
    Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER
    
  • 단일 char.

    Rune c = new Rune('a');
    
  • 서로게이트 char 쌍입니다.

    Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
    

입력이 유효한 유니코드 스칼라 값을 나타내지 않으면 모든 생성자가 throw ArgumentException 됩니다.

Rune.TryCreate 실패 시 예외를 발생시키지 않으려는 호출자에게 사용할 수 있는 방법이 있습니다.

Rune 기존 입력 시퀀스에서 인스턴스를 읽을 수도 있습니다. 예를 들어 ReadOnlySpan<char> UTF-16 데이터를 나타내는 경우 메서드는 Rune.DecodeFromUtf16 입력 범위의 시작 부분에 첫 번째 Rune 인스턴스를 반환합니다. 메서드도 Rune.DecodeFromUtf8 마찬가지로 작동하여 ReadOnlySpan<byte> UTF-8 데이터를 나타내는 매개 변수를 허용합니다. 범위의 시작이 아니라 범위의 끝에서 읽는 것과 동일한 메서드가 있습니다.

의 쿼리 속성 Rune

인스턴스의 정수 코드 포인트 값을 Rune 얻으려면 속성을 사용합니다 Rune.Value .

Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL
int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)

char 형식에서 사용할 수 있는 많은 정적 API는 Rune 형식에서도 사용할 수 있습니다. 예를 들어, Rune.IsWhiteSpaceRune.GetUnicodeCategoryChar.IsWhiteSpaceChar.GetUnicodeCategory 메서드와 동일합니다. 해당 Rune 메서드는 서로게이트 쌍을 올바르게 처리합니다.

다음 예제 코드는 입력으로 ReadOnlySpan<char> 사용하고 문자나 숫자가 아닌 모든 Rune 범위의 시작과 끝에서 트리밍합니다.

static ReadOnlySpan<char> TrimNonLettersAndNonDigits(ReadOnlySpan<char> span)
{
    // First, trim from the front.
    // If any Rune can't be decoded
    // (return value is anything other than "Done"),
    // or if the Rune is a letter or digit,
    // stop trimming from the front and
    // instead work from the end.
    while (Rune.DecodeFromUtf16(span, out Rune rune, out int charsConsumed) == OperationStatus.Done)
    {
        if (Rune.IsLetterOrDigit(rune))
        { break; }
        span = span[charsConsumed..];
    }

    // Next, trim from the end.
    // If any Rune can't be decoded,
    // or if the Rune is a letter or digit,
    // break from the loop, and we're finished.
    while (Rune.DecodeLastFromUtf16(span, out Rune rune, out int charsConsumed) == OperationStatus.Done)
    {
        if (Rune.IsLetterOrDigit(rune))
        { break; }
        span = span[..^charsConsumed];
    }

    return span;
}

API 간에는 몇 가지 API 차이점이 있습니다 charRune. 다음은 그 예입니다.

Rune UTF-8 또는 UTF-16으로 변환

유니 Rune 코드 스칼라 값이므로 UTF-8, UTF-16 또는 UTF-32 인코딩으로 변환할 수 있습니다. 이 Rune 형식은 UTF-8 및 UTF-16으로의 변환을 기본적으로 지원합니다.

Rune.EncodeToUtf16Rune 인스턴스를 char 인스턴스로 변환합니다. UTF-16으로 char 인스턴스를 변환할 때 생성되는 Rune 인스턴스 수를 쿼리하려면 Rune.Utf16SequenceLength 속성을 사용하십시오. UTF-8 변환에서도 비슷한 메서드가 있습니다.

다음 예제에서는 인스턴스를 Rune 배열로 char 변환합니다. 이 코드는 변수에 인스턴스가 Rune 있다고 가정합니다 rune .

char[] chars = new char[rune.Utf16SequenceLength];
int numCharsWritten = rune.EncodeToUtf16(chars);

A string는 UTF-16 문자 시퀀스이므로, Rune 인스턴스도 같은 예제에서 UTF-16으로 변환됩니다.

string theString = rune.ToString();

다음 예제에서는 인스턴스를 Rune 바이트 배열로 UTF-8 변환합니다.

byte[] bytes = new byte[rune.Utf8SequenceLength];
int numBytesWritten = rune.EncodeToUtf8(bytes);

Rune.EncodeToUtf16 메서드는 Rune.EncodeToUtf8 작성된 실제 요소 수를 반환합니다. 대상 버퍼가 너무 짧아 결과를 포함할 수 없는 경우 예외를 throw합니다. 예외를 피하려는 호출자를 위해 예외를 던지지 않는 TryEncodeToUtf8TryEncodeToUtf16 메서드도 있습니다.

.NET의 다른 언어와의 Rune 비교

"rune"이라는 용어는 유니코드 표준에 정의되어 있지 않습니다. 이 용어는 UTF-8 생성으로 거슬러 올라갑니다. 롭 파이크와 켄 톰슨은 결국 코드 포인트로 알려질 것을 설명하는 용어를 찾고 있었다. 그들은 "rune"이라는 용어에 정착했고, Rob Pike는 나중에 Go 프로그래밍 언어에 대한 영향력을 통해 이 용어를 대중화하는 데 도움을 주었습니다.

그러나 .NET Rune 형식은 Go rune 형식과 동일하지 않습니다. Go에서 rune 형식은 int32에 대한 별칭입니다. Go rune은 유니코드 코드 포인트를 나타내려는 것이지만, 유효하지 않은 유니코드 코드 포인트와 서로게이트 코드 포인트를 포함하여 32비트 값이라면 어떤 값도 될 수 있습니다.

다른 프로그래밍 언어의 유사한 형식은 Rust의 기본 char 형식 또는 Swift의 Unicode.Scalar 형식을 참조하세요. 둘 다 유니코드 스칼라 값을 나타냅니다. 다음과 유사한 기능을 제공합니다. NET의 Rune 형식이며, 합법적인 유니코드 스칼라 값이 아닌 값의 인스턴스화를 허용하지 않습니다.