Separate string by number of characters

7

I'm developing a component to read the file submitted by DataPrev with the list of monthly obituaries. This file is a TXT and every 210 characters is a different person.

The documentation can be seen at this link : SISOBI .

I'm used to separating data like this through a delimiter, using Split() , but this in particular does not have any, and is separated by amount of characters.

I made Action to send the TXT file to the application, and read the data contained therein.

Ex:

string exemplo = "13032015joao";

From this string , I need to remove the data and put it in variables like:

int dia = 13;
int mes = 03;
int ano = 2015;
string nome = joao;

The amount of character is fixed, eg:

Day will always be 2 characters, and after it will always come the month with 2 characters, and after the year ... And so on until the 210 characters finish.

Using Split() if it had a delimiter, it 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");
        }

Layout example:

000028000280000016427201412310000000000MARCIO SEARA RIBEIRO                                                        MARIA PETANIA DE OLIVEIRA SEARA 19780306201412319442067052500000000000000000000007657          
000028000290000016428201412310000000000MAIRE VALENTIM DA SILVA                                                     MAIRE VALENTIM DA SILVA         19281105201412310387867350700000000000000000000007657  
    
asked by anonymous 13.03.2015 / 16:09

1 answer

7

The method you are looking for is Substring :

using System;
using System.Console;

public class Program {
    public static void Main() {
        var exemplo = "13032015joao";
        var dia = Convert.ToInt32(exemplo.Substring(0, 2)); //posição inicial 0, tamanho 2
        var mes = Convert.ToInt32(exemplo.Substring(2, 2)); //posição inicial 2, tamanho 2
        var ano = Convert.ToInt32(exemplo.Substring(4, 4)); //posição inicial 4, tamanho 4
        var nome = exemplo.Substring(8); //posição inicial 8, até o fim, poderia ser tamanho 200
        WriteLine(dia);
        WriteLine(mes);
        WriteLine(ano);
        WriteLine(nome);
    }
}

See running on dotNetFiddle .

You can do a few things to automate code execution. It can get shorter and more generalized, but the logic is a bit more complex. Just for reference the most generic way:

using System;
using 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 implmentaçã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 dotNetFiddle .

A generic solution can be useful if you have to deal with multiple fixed-size columns with different layout . But when you are going to do something generic you have to think through all the possibilities, it is good to ensure that the parameters are in order. I did it quickly without considering everything that might happen.

    
13.03.2015 / 16:19