닷넷 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도 추가해 봤습니다.