En C #, j'ai un tableau d'ints contenant uniquement des chiffres. Je veux convertir ce tableau en chaîne.
Exemple de tableau:
int[] arr = {0,1,2,3,0,1};
Comment puis-je convertir cela en une chaîne au format: "012301"
?
at.net 3.5 utilisation:
String.Join("", new List<int>(array).ConvertAll(i => i.ToString()).ToArray());
at.net 4.0 ou supérieur utiliser: (voir la réponse de @Jan Remunda)
string result = string.Join("", array);
Vous pouvez simplement utiliser String.Join function et, en tant que séparateur, utiliser string.Empty
car il utilise StringBuilder en interne.
string result = string.Join(string.Emtpy, new []{0,1,2,3,0,1});
Exemple: si vous utilisez un point-virgule comme séparateur, le résultat serait 0;1;2;3;0;1
. Il fonctionne en fait avec un séparateur nul et le deuxième paramètre peut être énumérable de tous les objets, comme:
string result = string.Join(null, new object[]{0,1,2,3,0,"A",DateTime.Now});
Je me rends compte que mon opinion n’est probablement pas la plus populaire, mais j’ai du mal à sauter sur le wagon du groupe Linq-y. C'est chouette. C'est condensé. Je comprends cela et je ne m'oppose pas à l'utiliser là où c'est approprié. C’est peut-être juste moi, mais j’ai l’impression que les gens ne pensent plus à la création de fonctions utilitaires pour réaliser ce qu’ils veulent, mais préfèrent au contraire joncher leur code avec (parfois) des lignes excessivement longues de code Linq pour créer un 1-liner dense.
Je ne dis pas que les réponses de Linq fournies ici sont mauvaises, mais je pense que ces lignes de code risquent de devenir plus longues et plus obscures, car vous devez gérer diverses situations. . Et si votre tableau est nul? Que faire si vous voulez une chaîne délimitée au lieu de simplement concaténer? Que se passe-t-il si certains des entiers de votre tableau sont à deux chiffres et que vous souhaitez ajouter des zéros au début de chaque valeur, de sorte que la chaîne de chaque élément ait la même longueur que le reste?
En prenant l'une des réponses fournies à titre d'exemple:
result = arr.Aggregate(string.Empty, (s, i) => s + i.ToString());
Si je dois m'inquiéter de la nullité du tableau, maintenant cela devient:
result = (arr == null) ? null : arr.Aggregate(string.Empty, (s, i) => s + i.ToString());
Si je veux une chaîne délimitée par des virgules, maintenant cela devient:
result = (arr == null) ? null : arr.Skip(1).Aggregate(arr[0].ToString(), (s, i) => s + "," + i.ToString());
Ce n'est pas encore trop grave, mais je pense que ce que cette ligne de code fait n'est pas évident en un coup d'œil.
Bien sûr, rien ne vous empêche de jeter cette ligne de code dans votre propre fonction d’utilitaire afin de ne pas mêler ce long gâchis à votre logique d’application, surtout si vous le faites à plusieurs endroits:
public static string ToStringLinqy<T>(this T[] array, string delimiter)
{
// edit: let's replace this with a "better" version using a StringBuilder
//return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(array[0].ToString(), (s, i) => s + "," + i.ToString());
return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(new StringBuilder(array[0].ToString()), (s, i) => s.Append(delimiter).Append(i), s => s.ToString());
}
Mais si vous voulez quand même le placer dans une fonction utilitaire, avez-vous vraiment besoin qu'elle soit condensée en une ligne? Dans ce cas, pourquoi ne pas ajouter quelques lignes supplémentaires pour plus de clarté et tirer parti d'un StringBuilder pour ne pas effectuer d'opérations de concaténation répétées:
public static string ToStringNonLinqy<T>(this T[] array, string delimiter)
{
if (array != null)
{
// edit: replaced my previous implementation to use StringBuilder
if (array.Length > 0)
{
StringBuilder builder = new StringBuilder();
builder.Append(array[0]);
for (int i = 1; i < array.Length; i++)
{
builder.Append(delimiter);
builder.Append(array[i]);
}
return builder.ToString()
}
else
{
return string.Empty;
}
}
else
{
return null;
}
}
Et si les performances vous préoccupent vraiment, vous pouvez même en faire une fonction hybride qui décide de faire string.Join ou d'utiliser un StringBuilder en fonction du nombre d'éléments contenus dans le tableau (il s'agit d'une micro-optimisation, pas la peine à mon avis et peut-être plus néfaste que bénéfique, mais je l’utilise comme exemple pour ce problème):
public static string ToString<T>(this T[] array, string delimiter)
{
if (array != null)
{
// determine if the length of the array is greater than the performance threshold for using a stringbuilder
// 10 is just an arbitrary threshold value I've chosen
if (array.Length < 10)
{
// assumption is that for arrays of less than 10 elements
// this code would be more efficient than a StringBuilder.
// Note: this is a crazy/pointless micro-optimization. Don't do this.
string[] values = new string[array.Length];
for (int i = 0; i < values.Length; i++)
values[i] = array[i].ToString();
return string.Join(delimiter, values);
}
else
{
// for arrays of length 10 or longer, use a StringBuilder
StringBuilder sb = new StringBuilder();
sb.Append(array[0]);
for (int i = 1; i < array.Length; i++)
{
sb.Append(delimiter);
sb.Append(array[i]);
}
return sb.ToString();
}
}
else
{
return null;
}
}
Pour cet exemple, l’impact sur les performances n’est sans doute pas la peine de se soucier de cela, mais le fait est que si vous vous trouvez dans une situation où vous devez réellement vous préoccuper de la performance de vos opérations, quelles qu’elles soient, alors ce sera probablement le cas. plus facile et plus lisible à gérer que dans une fonction d'utilité que d'utiliser une expression complexe Linq.
Cette fonction utilitaire a toujours l’air un peu maladroite. Maintenant abandonnons les choses hybrides et faisons ceci:
// convert an enumeration of one type into an enumeration of another type
public static IEnumerable<TOut> Convert<TIn, TOut>(this IEnumerable<TIn> input, Func<TIn, TOut> conversion)
{
foreach (TIn value in input)
{
yield return conversion(value);
}
}
// concatenate the strings in an enumeration separated by the specified delimiter
public static string Delimit<T>(this IEnumerable<T> input, string delimiter)
{
IEnumerator<T> enumerator = input.GetEnumerator();
if (enumerator.MoveNext())
{
StringBuilder builder = new StringBuilder();
// start off with the first element
builder.Append(enumerator.Current);
// append the remaining elements separated by the delimiter
while (enumerator.MoveNext())
{
builder.Append(delimiter);
builder.Append(enumerator.Current);
}
return builder.ToString();
}
else
{
return string.Empty;
}
}
// concatenate all elements
public static string ToString<T>(this IEnumerable<T> input)
{
return ToString(input, string.Empty);
}
// concatenate all elements separated by a delimiter
public static string ToString<T>(this IEnumerable<T> input, string delimiter)
{
return input.Delimit(delimiter);
}
// concatenate all elements, each one left-padded to a minimum length
public static string ToString<T>(this IEnumerable<T> input, int minLength, char paddingChar)
{
return input.Convert(i => i.ToString().PadLeft(minLength, paddingChar)).Delimit(string.Empty);
}
Nous avons maintenant des fonctions utilitaires séparées et assez compactes, chacune d’elles pouvant être utilement argumentée.
En fin de compte, mon propos n'est pas de ne pas utiliser Linq, mais plutôt de ne pas oublier les avantages de la création de vos propres fonctions utilitaires, même si elles sont petites et ne contiennent peut-être qu'une seule ligne renvoyant le résultat. une ligne de code Linq. Si rien d'autre ne vous empêche de condenser le code de votre application par rapport à une ligne de code Linq, utilisez un utilitaire qui facilite l'ajustement de votre sortie si vous l'utilisez à plusieurs endroits. au cas où vous auriez besoin de le changer plus tard.
Pour ce problème, je préférerais simplement écrire quelque chose comme ceci dans mon code d'application:
int[] arr = { 0, 1, 2, 3, 0, 1 };
// 012301
result = arr.ToString<int>();
// comma-separated values
// 0,1,2,3,0,1
result = arr.ToString(",");
// left-padded to 2 digits
// 000102030001
result = arr.ToString(2, '0');
Pour éviter la création d'un tableau supplémentaire, vous pouvez procéder comme suit.
var builder = new StringBuilder();
Array.ForEach(arr, x => builder.Append(x));
var res = builder.ToString();
string result = arr.Aggregate("", (s, i) => s + i.ToString());
(Avertissement: si vous avez beaucoup de chiffres (au moins des centaines) et que vous vous souciez de la performance, je suggère d'éviter cette méthode et d'utiliser une variable StringBuilder
, comme dans la réponse de JaredPar.)
Tu peux faire:
int[] arr = {0,1,2,3,0,1};
string results = string.Join("",arr.Select(i => i.ToString()).ToArray());
Cela vous donne vos résultats.
J'aime utiliser StringBuilder
avec Aggregate()
. Le "truc" est que Append()
renvoie l'instance StringBuilder
elle-même:
var sb = arr.Aggregate( new StringBuilder(), ( s, i ) => s.Append( i ) );
var result = sb.ToString();
J'ai laissé cela ici pour la postérité, mais je ne recommande pas son utilisation car ce n'est pas très lisible. Cela est particulièrement vrai maintenant que je suis revenu pour voir si, après un certain temps, je me demandais à quoi je pensais quand je l'ai écrit (je pensais probablement 'merde, il faut que ce soit écrit avant que quelqu'un d'autre poste une réponse' .)
string s = string.Concat(arr.Cast<object>().ToArray());
string.Join("", (from i in arr select i.ToString()).ToArray())
Dans le .NET 4.0, le string.Join
peut utiliser un IEnumerable<string>
directement:
string.Join("", from i in arr select i.ToString())
Le moyen le plus efficace n'est pas de convertir chaque int en chaîne, mais de créer une chaîne à partir d'un tableau de caractères. Ensuite, le ramasse-miettes n'a plus qu'un seul objet temporaire à prendre en compte.
int[] arr = {0,1,2,3,0,1};
string result = new string(Array.ConvertAll<int,char>(arr, x => Convert.ToChar(x + '0')));
Si ce tableau est long, vous pouvez utiliser
var sb = arr.Aggregate(new StringBuilder(), ( s, i ) => s.Append( i ), s.ToString());