Separate string by amount of characters
I am developing a component to read the file sent by DataPrev with the ratio of monthly obituaries. This file is a txt and every 210 characters is a different person.
Documentation can be viewed at this link: SISOBI .
I'm used to separating data like this through a delimiter, using Split()
, but this one in particular has none, and is separated by amount of characters.
I did the Action
to send the file txt to the application, and read the data contained in it.
Ex:
string exemplo = "13032015joao";
From this string , I need to withdraw the data and put in variables, like:
int dia = 13;
int mes = 03;
int ano = 2015;
string nome = joao;
The amount of character is fixed, example:
Day will always be 2 characters, and after it will always come the month with 2 characters, and after the year... And so until you finish the 210 characters.
Using Split()
if you had a delimiter would look something like this:
var exemplo = "13|03|2015|joao";
string[] stringSeparators = new string[] { "|" };
var result = nomeDescrypt.Split(stringSeparators, StringSplitOptions.None);
var dia = Convert.ToInt32(result[0]);
var Mes= Convert.ToInt32(result[1]);
var Ano= Convert.ToInt32(result[2]);
var Nome= Convert.ToInt32(result[3]);
My question is: how to separate a string , delimiting by amount of characters?
My controller to read the file looks like this:
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
//verifica se o arquivo está nulo
if (file == null)
{
TempData["MensagemError"] = "Erro ao realizar o upload do arquivo!";
return View("Index");
}
//Salvar o arquivo txt
string path = Path.Combine(Server.MapPath("~/App_Data/Uploads/" + file.FileName));
file.SaveAs(path);
//Realiza a leitura do arquivo txt
var fileContents = System.IO.File.ReadAllText(path);
//testar se está lendo o arquivo
TempData["Mensagem"] = fileContents;
return RedirectToAction("Index");
}
Layot example:
000028000280000016427201412310000000000MARCIO SEARA RIBEIRO MARIA PETANIA DE OLIVEIRA SEARA 19780306201412319442067052500000000000000000000007657
000028000290000016428201412310000000000MAIRE VALENTIM DA SILVA MAIRE VALENTIM DA SILVA 19281105201412310387867350700000000000000000000007657
1 answers
The method you are looking for is Substring
:
using static System.Convert;
using static System.Console;
public class Program {
public static void Main() {
var exemplo = "13032015joao";
var dia = ToInt32(exemplo.Substring(0, 2));
var mes = ToInt32(exemplo.Substring(2, 2));
var ano = ToInt32(exemplo.Substring(4, 4));
var nome = exemplo.Substring(8);
WriteLine(dia);
WriteLine(mes);
WriteLine(ano);
WriteLine(nome);
}
}
See working on ideone. And no .NET Fiddle. Also I put on GitHub for future reference .
You can do a few things to automate code execution. It may get shorter and generalized, but the logic is a bit more complex. For reference only the most generic form:
using System;
using static System.Console;
using System.Collections.Generic;
public class Program {
public static void Main() {
var exemplo = "13032015joao";
//o último elemento poderia ser 200, por exemplo
//o que se for garantido que ele tenha o tamanho, evitaria o if no método
var partes = SplitFixed(exemplo, new List<int>() {2, 2, 4, 0});
foreach(var parte in partes) {
WriteLine(parte);
}
//poderia fazer as conversões aqui e jogar nas variáveis individuais
//alternativa com tipos, não sei se compensa o esforço
//para fazer certo daria o mesmo trabalho que fazer manualmente
//poucos casos esta forma seria realmente mais vantajosa e o ideal é que a conversão
//fosse feita através de lambdas contendo o código de conversão e não com tipos
var partes2 = SplitFixedTyped(exemplo, new List<Tuple<int, Type>>() {
new Tuple<int, Type>(2, typeof(int)),
new Tuple<int, Type>(2, typeof(int)),
new Tuple<int, Type>(4, typeof(int)),
new Tuple<int, Type>(0, typeof(string))});
foreach(var parte in partes2) {
WriteLine("Dado: {0} - Tipo {1}", parte, parte.GetType());
}
}
public static List<String> SplitFixed(string texto, List<int> tamanhos) {
var partes = new List<String>();
var posicao = 0;
foreach(var tamanho in tamanhos) {
if (tamanho > 0) { //padronizei que 0 significa que deve ir até o fim
partes.Add(texto.Substring(posicao, tamanho));
} else {
// o ideal é que não tenha essa parte e todos os tamanhos sejam definidos
//o 0 só pode ser usado como último parâmetro.
partes.Add(texto.Substring(posicao));
}
posicao += tamanho;
}
return partes;
}
//esta implementação é um pouco ingênua, não funciona em todas as situações mas funciona com o básico
public static List<object> SplitFixedTyped(string texto, List<Tuple<int, Type>> tamanhos) {
var partes = new List<object>();
var posicao = 0;
foreach(var tamanho in tamanhos) {
if (tamanho.Item1 > 0) { //padronizei que 0 significa que deve ir até o fim
partes.Add(Convert.ChangeType(texto.Substring(posicao, tamanho.Item1), tamanho.Item2));
} else {
// o ideal é que não tenha essa parte e todos os tamanhos sejam definidos
//o 0 só pode ser usado como último parâmetro.
partes.Add(texto.Substring(posicao));
}
posicao += tamanho.Item1;
}
return partes;
}
}
See working on Ideon. And no .NET Fiddle. Also I put on GitHub for future reference .
A generic solution can be useful if you have to deal with multiple fixed-size column files with different layout . But when going to do something generic have to think Well of all the possibilities, it is good to ensure that the parameters are in order. I did it quickly without considering everything that can happen.
At the time that I did not exist Span
and tuples by value as they exist today, so this code can be optimized.