ReadOnlySpan<char> Splitํ•˜๊ธฐ

๋‹ท๋„ท 7 ๊ธฐ์ค€ ReadOnlySpan์—๋Š” ์•„์ง Split์ด ์—†์Šต๋‹ˆ๋‹ค. ReadOnlySpan์„ ํ™œ์šฉํ•˜๋ฉด Parse ๋“ฑ ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ์— ๋ถˆํ•„์š”ํ•œ ํž™ ํ• ๋‹น์„ ์ตœ์†Œํ™”ํ•œ ์ฑ„๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ ํ•˜๋Š” ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค.

์‚ฌ์‹ค ReadOnlySpan์—๋Š” ์ด๋ฏธ IndexOf๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ„๋‹จํ•œ ์ˆ˜์ค€์˜ Split์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ค์šด ์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค. IndexOf๋ฅผ ์ด์šฉํ•ด separator์˜ index๋ฅผ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ, ์›๋ณธ ๋ฌธ์ž์—ด์„ Slice ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

public static SpanSplitEnumerator Split(this ReadOnlySpan<char> src, char separator) => new(src, new char[] { separator });

public static SpanSplitEnumerator Split(this ReadOnlySpan<char> src, ReadOnlySpan<char> separator) => new(src, separator);

public ref struct SpanSplitEnumerator
{
	private ReadOnlySpan<char> _string;

	private readonly ReadOnlySpan<char> _separator;

	private int _index;

	private bool _isEnd;

	public SpanSplitEnumerator(ReadOnlySpan<char> source, ReadOnlySpan<char> separator)
	{
		_string = source;
		_separator = separator;
		_index = 0;
		_isEnd = false;
	}

	public SpanSplitEntry Current { get; private set; }

	public SpanSplitEnumerator GetEnumerator() => this;

	public bool MoveNext()
	{
		if (_isEnd)
		{
			return false;
		}

		int index = _string.IndexOf(_separator);
		if (index >= 0)
		{
			Current = new SpanSplitEntry(_string[..index], _index++);
			_string = _string[(index + _separator.Length)..];
		}
		else
		{
			Current = new SpanSplitEntry(_string, _index++);
			_string = ReadOnlySpan<char>.Empty;
			_isEnd = true;
		}

		return true;
	}

	public readonly ref struct SpanSplitEntry
	{
		internal SpanSplitEntry(ReadOnlySpan<char> chars, int index)
		{
			Chars = chars;
			Index = index;
		}

		public ReadOnlySpan<char> Chars { get; }

		public int Index { get; }
	}
}

๋งŒ๋“œ๋Š” ๊น€์— foreach ๋ฌธ์—์„œ index๋ฅผ ์จ์•ผ ํ•  ์ผ์ด ์žˆ์„ ๋•Œ ๋ช‡ ๋ฒˆ์งธ ๋ฌธ์ž์—ด์ธ์ง€ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก index๋„ ์ถ”๊ฐ€ํ•ด ๋ดค์Šต๋‹ˆ๋‹ค.

4๊ฐœ์˜ ์ข‹์•„์š”
string str = "Hello, World!";
var span = str.AsSpan();

Console.WriteLine("========== string.Split ==========");
foreach (var split in str.Split(',').Select((s, index) => (Chars: s, Index: index)))
{
	Console.WriteLine($"index {split.Index}: {split.Chars}");
}

Console.WriteLine();
Console.WriteLine("========== ReadOnlySpan<char>.Split ==========");
foreach (var split in span.Split(','))
{
	Console.WriteLine($"index {split.Index}: {split.Chars}");
}

์ถœ๋ ฅ

========== string.Split ==========
index 0: Hello
index 1:  World!

========== ReadOnlySpan<char>.Split ==========
index 0: Hello
index 1:  World!
4๊ฐœ์˜ ์ข‹์•„์š”

โ€˜ref structโ€™ ์‚ฌ๋ก€๋ฅผ ์ด๊ณณ์—์„œ ๋ณด๋‹ˆ ๋Š๋ฌด ์ข‹์Šต๋‹ˆ๋‹ค!

3๊ฐœ์˜ ์ข‹์•„์š”