J'ai une chaîne comme celle-ci:
string[] coordinatesVal = coordinateTxt.Trim().Split(new string[] { ",0" }, StringSplitOptions.None);
for (int i = 0; i < coordinatesVal.Length - 1; i++)
{
coordinatesVal[i] = coordinatesVal[i].Trim();
coordinatesVal[i] = coordinatesVal[i].Replace(',', ' ');
numbers.Append(coordinatesVal[i]);
if (i != coordinatesVal.Length - 1)
{
coordinatesVal.Append(", ");
}
}
J'ai besoin d'une sortie comme celle-ci:
-82.9494547 36.2913021, -83.0784938 36.2347521, -82.9537782,36.079235
J'ai essayé ceci après code pour obtenir le résultat souhaité:
-82.9494547,36.2913021,0 -83.0784938,36.2347521,0 -82.9537782,36.079235,0
Mais ce processus ne me semble pas la solution professionnelle. Quelqu'un peut-il suggérer un moyen plus efficace de procéder?
5 Réponses :
Votre code est correct. Vous pouvez ignorer les résultats temporaires et les appels de méthode de chaîne
var numbers = new StringBuilder();
string[] coordinatesVal = coordinateTxt
.Trim()
.Split(new string[] { ",0" }, StringSplitOptions.None);
for (int i = 0; i < coordinatesVal.Length - 1; i++) {
numbers
.Append(coordinatesVal[i].Trim().Replace(',', ' '))
.Append(", ");
}
numbers.Length -= 2;
Notez que la dernière instruction suppose qu'il y a au moins une paire de coordonnées disponible. Si les coordonnées peuvent être vides, vous devrez entourer la boucle et cette dernière instruction dans if (coordonnéesVal.Length> 0) {...} . C'est encore plus efficace que d'avoir un if dans la boucle.
Il y a un bug (dans le code OP aussi). Imaginez cette entrée: -82.9494547,0.2913021,0
@ AntonínLejsek J'ai pensé à ce scénario après avoir publié mon message. toutes les solutions ici, sauf Harun, échoueraient dans ce scénario. après plusieurs tentatives optimales compte tenu de ce scénario, je n'ai pas pu battre le temps de Harun. si ce scénario est possible ou si la troisième valeur n'est peut-être pas 0, la solution harun est la voie à suivre. c'est pourquoi j'ai voté pour sa solution. Cependant, il est possible que les données ne contiendront jamais un zéro non significatif et qu'elles contiendront toujours un 0 final. Dans ce cas, ma deuxième méthode serait la plus rapide à moins que les données ne soient assez volumineuses, puis relancer serait la voie à suivre.
Une approche plus sûre serait de se séparer deux fois. Divisez d'abord en lignes, puis divisez chaque ligne en 3 parties entre les virgules.
Votre solution est bonne. Peut-être que vous pourriez l'écrire un peu plus élégant comme ceci:
string[] coordinatesVal = coordinateTxt.Trim().Split(new string[] { ",0" },
StringSplitOptions.RemoveEmptyEntries);
string result = string.Empty;
foreach (string line in coordinatesVal)
{
string[] numbers = line.Trim().Split(',');
result += numbers[0] + " " + numbers[1] + ", ";
}
result = result.Remove(result.Count()-2, 2);
Notez le paramètre StringSplitOptions.RemoveEmptyEntries de la méthode Split afin de ne pas le faire. t avoir à gérer les lignes vides dans le bloc foreach .
Ou vous pouvez faire une doublure extrêmement courte. Plus difficile à déboguer, mais dans des cas simples fait le travail.
string result =
string.Join(", ",
coordinateTxt.Trim().Split(new string[] { ",0" }, StringSplitOptions.RemoveEmptyEntries).
Select(i => i.Replace(",", " ")));
voici une autre manière sans définir vos propres boucles et remplacer les méthodes, ou utiliser LINQ.
string coordinateTxt = @" -82.9494540,36.2913021,0
-83.0784938,36.2347521,0
-82.9537782,36.079235,0";
string result = coordinateTxt.Replace(Environment.NewLine, "").Replace($",", " ").Replace(" 0", ", ").Trim(new char[]{ ',',' ' });
Console.WriteLine(result);
ou même
string coordinateTxt = @" -82.9494547,36.2913021,0
-83.0784938,36.2347521,0
-82.9537782,36.079235,0";
string[] coordinatesVal = coordinateTxt.Replace(",", "*").Trim().Split(new string[] { "*0", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(",", coordinatesVal).Replace("*", " ");
Console.WriteLine(result);
Êtes-vous sûr que .Replace et .Join fonctionnent sans boucles;)
boucles explicites dans le code. en utilisant les méthodes .net intégrées sans écrire vos propres boucles.
@Fabio i a clarifié le titre pour vous. car ce que je voulais dire n'était pas clair pour vous.
Vous vous interrogez sur l'efficacité, mais vous ne spécifiez pas si vous voulez dire l'efficacité du code (vitesse d'exécution) ou l'efficacité du programmeur (combien de temps vous devez y consacrer). Un élément clé de la programmation professionnelle est de juger lequel de ces éléments est le plus important dans une situation donnée.
Les autres réponses font un bon travail pour couvrir l'efficacité du programmeur, donc je cherche à améliorer l'efficacité du code. Je fais cela à la maison pour le plaisir, mais pour le travail professionnel, il me faudrait une bonne raison avant de faire l'effort de même passer du temps à comparer les vitesses des méthodes données dans les autres réponses, sans parler d'essayer de les améliorer.
Cela dit, attendre que le programme termine la conversion de millions de paires de coordonnées me donnerait une telle raison.
L'un des pièges de la vitesse de la gestion des chaînes C # est le manière String.Replace () et String.Trim () retournent une toute nouvelle copie de la chaîne. Cela implique d'allouer de la mémoire, de copier les caractères et éventuellement de nettoyer les déchets générés. Faites-le plusieurs millions de fois et cela commence à s'additionner. Dans cet esprit, j'ai essayé d'éviter autant d'allocations et de copies que possible.
String Split, Replace each line (see Olivier's answer, though I pre-allocated the space in the StringBuilder):
6542 ms / 13493147 ticks, 130.84ms / 269862.9 ticks per conversion
Replace & Trim entire string (see Heriberto's second version):
3352 ms / 6914604 ticks, 67.04 ms / 138292.1 ticks per conversion
- Note: Original test was done with 900000 coord pairs, but this entire-string version suffered an out of memory exception so I had to rein it in a bit.
Split and Join (see Åukasz's answer):
8780 ms / 18110672 ticks, 175.6 ms / 362213.4 ticks per conversion
Character state machine (see above):
1685 ms / 3475506 ticks, 33.7 ms / 69510.12 ticks per conversion
Cela utilise une machine à états pour traiter les caractères entrants différemment selon qu'ils font partie du premier nombre, deuxième nombre ou le reste de la ligne et affichez les caractères en conséquence. Chaque caractère n'est copié qu'une seule fois dans la sortie, puis je crois encore une fois lorsque la sortie est convertie en chaîne à la fin. Cette seconde conversion pourrait probablement être évitée en utilisant un char [] pour la sortie.
Le goulot d'étranglement dans ce code semble être le nombre d'appels à StringBuilder.Append () . Si plus de vitesse était nécessaire, j'essaierais d'abord de garder une trace du nombre de caractères à copier directement dans la sortie, puis d'utiliser .Append (string value, int startIndex, int count) pour envoyer un nombre entier en un seul appel.
J'ai mis quelques exemples de solutions dans un faisceau de test, et les ai exécutées sur une chaîne contenant 300 000 lignes de paires de coordonnées, en moyenne sur 50 exécutions. Les résultats sur mon PC étaient:
enum CurrentField
{
FirstNum,
SecondNum,
UnwantedZero
};
static string ConvertStateMachine(string input)
{
// Pre-allocate enough space in the string builder.
var numbers = new StringBuilder(input.Length);
var state = CurrentField.FirstNum;
int i = 0;
while (i < input.Length)
{
char c = input[i++];
switch (state)
{
// Copying the first number to the output, next will be another number
case CurrentField.FirstNum:
if (c == ',')
{
// Separate the two numbers by space instead of comma, then move on
numbers.Append(' ');
state = CurrentField.SecondNum;
}
else if (!(c == ' ' || c == '\n'))
{
// Ignore whitespace, output anything else
numbers.Append(c);
}
break;
// Copying the second number to the output, next will be the ,0\n that we don't need
case CurrentField.SecondNum:
if (c == ',')
{
numbers.Append(", ");
state = CurrentField.UnwantedZero;
}
else if (!(c == ' ' || c == '\n'))
{
// Ignore whitespace, output anything else
numbers.Append(c);
}
break;
case CurrentField.UnwantedZero:
// Output nothing, just track when the line is finished and we start all over again.
if (c == '\n')
{
state = CurrentField.FirstNum;
}
break;
}
}
return numbers.ToString();
}
Donc, la question de savoir quelle version est la plus efficace se résume à: quelles sont vos exigences?
J'ai vidé le fichier de code complet à pastebin.com/Tf3nyH1x si quelqu'un veut jouer avec.
J'ai réalisé que la sortie vers char [] supprimerait également la surcharge des appels .Append () beaucoup plus facilement que de regrouper des nombres entiers, alors j'ai essayé. Cela m'a donné: 1121 ms / 2313272 ticks, 22,42ms / 46265,44ticks par conversion
beau travail .. je pense que vous avez manqué que la réponse de Łukasz donne une mauvaise sortie. J'ai joué avec ça un peu plus après ma réponse car il me semblait que cela devrait être plus simple que toutes les réponses données (y compris la mienne) et plus efficace. J'ai effectué une comparaison sur les solutions (avant de voir la vôtre) et ma deuxième méthode était toujours plus rapide. mais j'ai remarqué qu'il y avait 2 hypothèses qui pourraient ne pas toujours être vraies. les données n'auront jamais 0 et le ", 0" sera toujours un 0. ces hypothèses peuvent ou peuvent ne pas être vraies. seul l'OP pourrait le savoir.
Je les ai tous exécutés en 100k itérations (y compris certaines versions sûres que j'ai faites selon les hypothèses que j'ai mentionnées précédemment) et votre méthode est plus rapide que toutes mes méthodes sûres, mais plus lente que ma deuxième "unsafe" (celles fournies comme solutions). ce sont les nombres que j'ai pour 100k itérations (en millisecondes) en utilisant la chaîne fournie: MyReplace2 431.62730000000005 ConvertStateMachine 531.77960000000007 OliverLoop 533.7842 MyReplace1 604.8893 MyOptimizedAttempt2 686.008 ElgasparForeach dépend de l'efficacité de votre travail 1021.5018. je donne +1
Je pense que votre version étendue des données de test devrait probablement utiliser appendline, plutôt que simplement ajouter. semble que c'est le format dans lequel les données sont.
Votre solution est correcte. Vous pouvez enchaîner
TrimetReplace:coordonnéesVal [i] = coordonnéesVal [i] .Trim (). Replace (',', ''); code>. De plus, vous pouvez toujours ajouter","et après la boucle, corriger la longueur duStringBuilderaveccoordonnéesVal.Length - = 2;Merci pour votre réponse. Mais est-il possible de le faire avec moins de lignes de code?
Il semblerait que vous voyiez «la solution professionnelle», «plus efficace» et «moins de lignes de code» comme synonymes. Ils ne le sont pas et sont souvent tout le contraire.
Cela peut être fait avec une ligne de code si vous supprimez les caractères de fin de ligne.
@VidmantasBlazevicius comment faire ça?
La chaîne inclut-elle une nouvelle ligne après ", 0" comme affiché dans un exemple?