自分の関数を書く直前に、.NETライブラリにstring.split(string input, params int[] indexes)
のような関数が存在するかどうかを確認したかっただけですか?この関数は、渡したインデックスの文字列を分割する必要があります。
編集:string.join文を追加するべきではありませんでした-混乱していました。
String
インスタンスメソッド Substring を使用できます。
string a = input.Substring(0, 10);
string b = input.Substring(10, 5);
string c = input.Substring(15, 3);
他のすべての答えは複雑すぎるように見えたので、私は刺しました。
using System.Linq;
public static class StringExtensions
{
/// <summary>
/// Returns a string array that contains the substrings in this instance that are delimited by specified indexes.
/// </summary>
/// <param name="source">The original string.</param>
/// <param name="index">An index that delimits the substrings in this string.</param>
/// <returns>An array whose elements contain the substrings in this instance that are delimited by one or more indexes.</returns>
/// <exception cref="ArgumentNullException"><paramref name="index" /> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">An <paramref name="index" /> is less than zero or greater than the length of this instance.</exception>
public static string[] SplitAt(this string source, params int[] index)
{
index = index.Distinct().OrderBy(x => x).ToArray();
string[] output = new string[index.Length + 1];
int pos = 0;
for (int i = 0; i < index.Length; pos = index[i++])
output[i] = source.Substring(pos, index[i] - pos);
output[index.Length] = source.Substring(pos);
return output;
}
}
Splitメソッドは、認識パターンに基づいて文字列を分割します。カンマ区切りのリストなどを分解するのに最適です。
しかし、あなたは正しいです、あなたが望むものを達成するための組み込みの文字列メソッドはありません。
public static IEnumerable<string> SplitAt(this string source, params int[] index)
{
var indices = new[] { 0 }.Union(index).Union(new[] { source.Length });
return indices
.Zip(indices.Skip(1), (a, b) => (a, b))
.Select(_ => source.Substring(_.a, _.b - _.a));
}
var s = "abcd";
s.SplitAt(); // "abcd"
s.SplitAt(0); // "abcd"
s.SplitAt(1); // "a", "bcd"
s.SplitAt(2); // "ab", "cd"
s.SplitAt(1, 2) // "a", "b", "cd"
s.SplitAt(3); // "abc", "d"
1つの可能な解決策:
public static class StringExtension
{
public static string[] Split(this string source, params int[] sizes)
{
var length = sizes.Sum();
if (length > source.Length) return null;
var resultSize = sizes.Length;
if (length < source.Length) resultSize++;
var result = new string[resultSize];
var start = 0;
for (var i = 0; i < resultSize; i++)
{
if (i + 1 == resultSize)
{
result[i] = source.Substring(start);
break;
}
result[i] = source.Substring(start, sizes[i]);
start += sizes[i];
}
return result;
}
}
これはyourの一般的な質問に直接答えることはありませんが、最も一般的なケース(または少なくとも私が探していたケース)ではこれに出くわしたときの答えSO質問)ここで、indexes
は単一のint
ですが、この拡張メソッドは_string[]
_を返すよりも少しクリーンです。配列、特にC#7で。
その価値については、2つの_char[]
_配列を作成し、string.Substring()
を呼び出し、text.CopyTo()
を呼び出して2つの文字列を返すことに対して、new string(charArray)
を使用してベンチマークを行いました。 string.Substring()
の使用は約2倍高速でした。
_public static class StringExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (string left, string right) SplitAt(this string text, int index) =>
(text.Substring(0, index), text.Substring(index));
}
public static class Program
{
public static void Main()
{
var (left, right) = "leftright".SplitAt(4);
Console.WriteLine(left);
Console.WriteLine(right);
}
}
_
注:C#7より前のバージョンで_Tuple<string, string>
_を使用しても、冗長性はそれほど節約されません。実際には、_string[2]
_配列を返すだけの方がクリーンな場合があります。
_public static class StringExtensions
{
// I'd use one or the other of these methods, and whichever one you choose,
// rename it to SplitAt()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Tuple<string, string> TupleSplitAt(this string text, int index) =>
Tuple.Create<string, string>(text.Substring(0, index), text.Substring(index));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string[] ArraySplitAt(this string text, int index) =>
new string[] { text.Substring(0, index), text.Substring(index) };
}
public static class Program
{
public static void Main()
{
Tuple<string, string> stringsTuple = "leftright".TupleSplitAt(4);
Console.WriteLine("Tuple method");
Console.WriteLine(stringsTuple.Item1);
Console.WriteLine(stringsTuple.Item2);
Console.WriteLine();
Console.WriteLine("Array method");
string[] stringsArray = "leftright".ArraySplitAt(4);
Console.WriteLine(stringsArray[0]);
Console.WriteLine(stringsArray[1]);
}
}
_
正規表現は常にあります。
これは、拡張できる例です。
string text = "0123456789ABCDEF";
Match m = new Regex("(.{7})(.{4})(.{5})").Match(text);
if (m.Success)
{
var result = new string[m.Groups.Count - 1];
for (var i = 1; i < m.Groups.Count; i++)
result[i - 1] = m.Groups[i].Value;
}
上記のロジックをカプセル化する関数は次のとおりです。
public static string[] SplitAt(this string text, params int[] indexes)
{
var pattern = new StringBuilder();
var lastIndex = 0;
foreach (var index in indexes)
{
pattern.AppendFormat("(.{{{0}}})", index - lastIndex);
lastIndex = index;
}
pattern.Append("(.+)");
var match = new Regex(pattern.ToString()).Match(text);
if (! match.Success)
{
throw new ArgumentException("text cannot be split by given indexes");
}
var result = new string[match.Groups.Count - 1];
for (var i = 1; i < match.Groups.Count; i++)
result[i - 1] = match.Groups[i].Value;
return result;
}
これはかなり早く書かれましたが、それは私のポイントを説明し、コメントの著者であるマイケルへの私のポイントを強調していると思います。
戻り値として「List <string>」を含むバージョン。
発信者
string iTextLine = "02121AAAARobert Louis StevensonXXXX"
int[] tempListIndex = new int[] {
// 0 - // 0number (exclude first)
5, // 1user
9, // 2name
31 // role
};
// GET - words from indexes
List<string> tempWords = getListWordsFromLine(iTextLine, tempListIndex);
方法
/// <summary>
/// GET - split line in parts using index cuts
/// </summary>
/// <param name="iListIndex">Input List of indexes</param>
/// <param name="iTextLine">Input line to split</param>
public static List<string> getListWordsFromLine(string iTextLine, int[] iListIndex)
{
// INIT
List<string> retObj = new List<string>();
int currStartPos = 0;
// GET - clear index list from dupl. and sort it
int[] tempListIndex = iListIndex.Distinct()
.OrderBy(o => o)
.ToArray();
// CTRL
if (tempListIndex.Length != iListIndex.Length)
{
// ERR
throw new Exception("Input iListIndex contains duplicate indexes");
}
for (int jj = 0; jj < tempListIndex.Length; ++jj)
{
try
{
// SET - line chunk
retObj.Add(iTextLine.Substring(currStartPos,
tempListIndex[jj] - currStartPos));
}
catch (Exception)
{
// SET - line is shorter than expected
retObj.Add(string.Empty);
}
// GET - update start position
currStartPos = tempListIndex[jj];
}
// SET
retObj.Add(iTextLine.Substring(currStartPos));
// RET
return retObj;
}