diff --git a/BoletoNetCore/Arquivo/ArquivoRetorno.cs b/BoletoNetCore/Arquivo/ArquivoRetorno.cs index 7c31ef4f..e1607935 100644 --- a/BoletoNetCore/Arquivo/ArquivoRetorno.cs +++ b/BoletoNetCore/Arquivo/ArquivoRetorno.cs @@ -119,43 +119,46 @@ private void LerLinhaDoArquivoRetornoCNAB240(string registro) IBancoCNAB240 b = (IBancoCNAB240)Banco; if (b == null) throw new Exception("Leitura CNAB240 não implementada para este banco."); - var tipoRegistro = registro.Substring(7, 1); - var tipoSegmento = registro.Substring(13, 1); + char tipoRegistro = registro[7]; + char tipoSegmento = registro[13]; - if (tipoRegistro == "0") + if (tipoRegistro == '0') { //REGISTRO HEADER DO ARQUIVO RETORNO b.LerHeaderRetornoCNAB240(this, registro); return; } - if (tipoRegistro == "3" & tipoSegmento == "T") + if (tipoRegistro == '3') { - // Segmento T - Indica um novo boleto - var boleto = new Boleto(this.Banco, _ignorarCarteiraBoleto); - b.LerDetalheRetornoCNAB240SegmentoT(ref boleto, registro); - Boletos.Add(boleto); - return; - } + if (tipoSegmento == 'T') + { + // Segmento T - Indica um novo boleto + var boleto = new Boleto(this.Banco, _ignorarCarteiraBoleto); + b.LerDetalheRetornoCNAB240SegmentoT(ref boleto, registro); + Boletos.Add(boleto); + return; + } - if (tipoRegistro == "3" & tipoSegmento == "U") - { - // Segmento U - Continuação do segmento T anterior (localiza o último boleto da lista) - Boleto boleto = Boletos.LastOrDefault(); - // Se não encontrou um boleto válido, ocorreu algum problema, pois deveria ter criado um novo objeto no registro que foi analisado anteriormente. - if (boleto == null) - throw new Exception("Objeto boleto não identificado"); - b.LerDetalheRetornoCNAB240SegmentoU(ref boleto, registro); - return; - } + if (tipoSegmento == 'U') + { + // Segmento U - Continuação do segmento T anterior (localiza o último boleto da lista) + Boleto boleto = Boletos.LastOrDefault(); + // Se não encontrou um boleto válido, ocorreu algum problema, pois deveria ter criado um novo objeto no registro que foi analisado anteriormente. + if (boleto == null) + throw new Exception("Objeto boleto não identificado"); + b.LerDetalheRetornoCNAB240SegmentoU(ref boleto, registro); + return; + } - if (tipoRegistro == "3" & tipoSegmento == "A") - { - // Segmento A - Indica um novo boleto - var boleto = new Boleto(this.Banco, _ignorarCarteiraBoleto); - b.LerDetalheRetornoCNAB240SegmentoA(ref boleto, registro); - Boletos.Add(boleto); - return; + if (tipoSegmento == 'A') + { + // Segmento A - Indica um novo boleto + var boleto = new Boleto(this.Banco, _ignorarCarteiraBoleto); + b.LerDetalheRetornoCNAB240SegmentoA(ref boleto, registro); + Boletos.Add(boleto); + return; + } } } @@ -165,17 +168,18 @@ private void LerLinhaDoArquivoRetornoCNAB400(string registro) if (b == null) throw new Exception("Leitura CNAB400 não implementada para este banco."); // Identifica o tipo do registro (primeira posição da linha) - var tipoRegistro = registro.Substring(0, 1); + char tipoRegistroChar = registro[0]; + string tipoRegistro = tipoRegistroChar.ToString(); // Registro HEADER - if (tipoRegistro == "0") + if (tipoRegistroChar == '0') { b.LerHeaderRetornoCNAB400(registro); return; } // Registro TRAILER - if (tipoRegistro == "9") + if (tipoRegistroChar == '9') { b.LerTrailerRetornoCNAB400(registro); return; @@ -213,18 +217,18 @@ private void LerLinhaDoArquivoRetornoCNAB400(string registro) // Identifica o tipo de registro que deve ser analisado pelo Banco. - switch (tipoRegistro) + switch (tipoRegistroChar) { - case "1": + case '1': b.LerDetalheRetornoCNAB400Segmento1(ref boleto, registro); break; - case "2": + case '2': b.LerDetalheRetornoCNAB400Segmento2(ref boleto, registro); break; - case "4": + case '4': b.LerDetalheRetornoCNAB400Segmento4(ref boleto, registro); break; - case "7": + case '7': b.LerDetalheRetornoCNAB400Segmento7(ref boleto, registro); break; default: diff --git a/BoletoNetCore/Extensions/StringExtensions.cs b/BoletoNetCore/Extensions/StringExtensions.cs index c6c81cfb..71665a69 100644 --- a/BoletoNetCore/Extensions/StringExtensions.cs +++ b/BoletoNetCore/Extensions/StringExtensions.cs @@ -8,13 +8,27 @@ public static string Right(this string value, int length) { if (String.IsNullOrEmpty(value)) return string.Empty; - return value.Length <= length ? value : value.Substring(value.Length - length); + return value.Length <= length ? value : value.AsSpan(value.Length - length).ToString(); + } + + internal static ReadOnlySpan RightSpan(this string value, int length) + { + if (string.IsNullOrEmpty(value)) + return ReadOnlySpan.Empty; + return value.Length <= length ? value.AsSpan() : value.AsSpan(value.Length - length); } public static string Left(this string value, int length) { if (String.IsNullOrEmpty(value)) return string.Empty; - return value.Length <= length ? value : value.Substring(0, length); + return value.Length <= length ? value : value.AsSpan(0, length).ToString(); + } + + internal static ReadOnlySpan LeftSpan(this string value, int length) + { + if (string.IsNullOrEmpty(value)) + return ReadOnlySpan.Empty; + return value.Length <= length ? value.AsSpan() : value.AsSpan(0, length); } public static string MidVB(this string str, int start, int length) @@ -29,203 +43,221 @@ public static string Mid(this string str, int startIndex, int length) { length = str.Length - startIndex; } - return str.Substring(startIndex, length); + return str.AsSpan(startIndex, length).ToString(); + } + + internal static ReadOnlySpan MidSpan(this string str, int startIndex, int length) + { + if (str.Length <= 0 || startIndex >= str.Length) return ReadOnlySpan.Empty; + if (startIndex + length > str.Length) + { + length = str.Length - startIndex; + } + return str.AsSpan(startIndex, length); } public static string CalcularDVCaixa(this string texto) { - string digito; + return CalcularDVCaixa(texto.AsSpan()); + } + + public static string CalcularDVCaixa(this ReadOnlySpan texto) + { int pesoMaximo = 9, soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - soma = soma + Convert.ToInt32(texto.Substring(i, 1)) * peso; - if (peso == pesoMaximo) - peso = 2; - else - peso = peso + 1; + int digito = texto[i] - '0'; + soma += digito * peso; + peso = peso == pesoMaximo ? 2 : peso + 1; } var resto = soma % 11; - if (resto <= 1) - digito = "0"; - else - digito = (11 - resto).ToString(); - return digito; + return resto <= 1 ? "0" : (11 - resto).ToString(); } public static string CalcularDVSantander(this string texto) { - string digito; + return CalcularDVSantander(texto.AsSpan()); + } + + public static string CalcularDVSantander(this ReadOnlySpan texto) + { int pesoMaximo = 9, soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - soma = soma + Convert.ToInt32(texto.Substring(i, 1)) * peso; - if (peso == pesoMaximo) - peso = 2; - else - peso = peso + 1; + int digito = texto[i] - '0'; + soma += digito * peso; + peso = peso == pesoMaximo ? 2 : peso + 1; } var resto = soma % 11; - if (resto <= 1) - digito = "0"; - else - digito = (11 - resto).ToString(); - return digito; + return resto <= 1 ? "0" : (11 - resto).ToString(); } + private static ReadOnlySpan FatorMultiplicacaoSicoob => new byte[] + { 3, 1, 9, 7, 3, 1, 9, 7, 3, 1, 9, 7, 3, 1, 9, 7, 3, 1, 9, 7, 3 }; + public static string CalcularDVSicoob(this string texto) { - string digito, fatorMultiplicacao = "319731973197319731973"; + return CalcularDVSicoob(texto.AsSpan()); + } + + public static string CalcularDVSicoob(this ReadOnlySpan texto) + { + if (texto.Length != 21) + throw new ArgumentException("Texto deve ter 21 caracteres", nameof(texto)); + + ReadOnlySpan fatores = FatorMultiplicacaoSicoob; int soma = 0; + for (int i = 0; i < 21; i++) { - soma += Convert.ToInt16(texto.Substring(i, 1)) * Convert.ToInt16(fatorMultiplicacao.Substring(i, 1)); + soma += (texto[i] - '0') * fatores[i]; } - int resto = (soma % 11); - if (resto <= 1) - digito = "0"; - else - digito = (11 - resto).ToString(); - return digito; + + int resto = soma % 11; + return resto <= 1 ? "0" : (11 - resto).ToString(); } public static string CalcularDVBradesco(this string texto) { - string digito; + return CalcularDVBradesco(texto.AsSpan()); + } + + public static string CalcularDVBradesco(this ReadOnlySpan texto) + { int pesoMaximo = 7, soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - soma = soma + (int)char.GetNumericValue(texto[i]) * peso; - if (peso == pesoMaximo) - peso = 2; - else - peso = peso + 1; + int digito = texto[i] - '0'; + soma += digito * peso; + peso = peso == pesoMaximo ? 2 : peso + 1; } var resto = soma % 11; switch (resto) { case 0: - digito = "0"; - break; + return "0"; case 1: - digito = "P"; - break; + return "P"; default: - digito = (11 - resto).ToString(); - break; + return (11 - resto).ToString(); } - return digito; } public static string CalcularDVItau(this string texto) { - string digito; - int soma = 0, peso = 2, digTmp = 0; + return CalcularDVItau(texto.AsSpan()); + } + + public static string CalcularDVItau(this ReadOnlySpan texto) + { + int soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - digTmp = (int)char.GetNumericValue(texto[i]) * peso; + int digito = texto[i] - '0'; + int digTmp = digito * peso; if (digTmp > 9) digTmp = (digTmp / 10) + (digTmp % 10); - soma = soma + digTmp; - - if (peso == 2) - peso = 1; - else - peso = peso + 1; + soma += digTmp; + peso = peso == 2 ? 1 : peso + 1; } - var resto = (soma % 10); - if (resto == 0) - digito = "0"; - else - digito = (10 - resto).ToString(); - return digito; + var resto = soma % 10; + return resto == 0 ? "0" : (10 - resto).ToString(); } public static string CalcularDVSafra(this string texto) { - string digito; + return CalcularDVSafra(texto.AsSpan()); + } + + public static string CalcularDVSafra(this ReadOnlySpan texto) + { int pesoMaximo = 9, soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - soma = soma + (int)char.GetNumericValue(texto[i]) * peso; - if (peso == pesoMaximo) - peso = 2; - else - peso = peso + 1; + int digito = texto[i] - '0'; + soma += digito * peso; + peso = peso == pesoMaximo ? 2 : peso + 1; } var resto = soma % 11; switch (resto) { case 0: - digito = "1"; - break; + return "1"; case 1: - digito = "0"; - break; + return "0"; default: - digito = (11 - resto).ToString(); - break; + return (11 - resto).ToString(); } - return digito; } public static string CalcularDVUniprimeNortePR(this string texto) + { + return CalcularDVUniprimeNortePR(texto.AsSpan()); + } + + public static string CalcularDVUniprimeNortePR(this ReadOnlySpan texto) { int pesoMaximo = 7, soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - soma = soma + (int)char.GetNumericValue(texto[i]) * peso; - if (peso == pesoMaximo) - peso = 2; - else - peso = peso + 1; + int digito = texto[i] - '0'; + soma += digito * peso; + peso = peso == pesoMaximo ? 2 : peso + 1; } var moduloFinal = soma % 11; - int digitoFinal; - if (moduloFinal < 2) - digitoFinal = 0; - else - digitoFinal = 11 - moduloFinal; - + if (moduloFinal == 1) return "P"; + int digitoFinal = moduloFinal < 2 ? 0 : 11 - moduloFinal; return digitoFinal.ToString(); } public static string CalcularDVBancoInter(this string texto) { - int soma = 0, peso = 2; + return CalcularDVBancoInter(texto.AsSpan()); + } + + public static string CalcularDVBancoInter(this ReadOnlySpan texto) + { + int soma = 0; for (var i = 0; i < texto.Length; i++) { - var numero = (int)char.GetNumericValue(texto[i]); - peso = i % 2 == 0 ? 2 : 1; - var parcial = numero * peso; + int numero = texto[i] - '0'; + int peso = i % 2 == 0 ? 2 : 1; + int parcial = numero * peso; + if (parcial < 10) + { soma += parcial; + } else { - soma += (int)char.GetNumericValue(parcial.ToString()[0]) + (int)char.GetNumericValue(parcial.ToString()[1]); + // Soma os dígitos do resultado (ex: 14 -> 1 + 4) + soma += (parcial / 10) + (parcial % 10); } } - var moduloFinal = soma % 10; - int digitoFinal = 0; - if (moduloFinal > 0) - digitoFinal = 10 - moduloFinal; - + int moduloFinal = soma % 10; + int digitoFinal = moduloFinal > 0 ? 10 - moduloFinal : 0; return digitoFinal.ToString(); } public static string CalcularDVBancoBTGPactual(this string nossoNumero) { - var soma = 0; - var multiplicador = 2; + return CalcularDVBancoBTGPactual(nossoNumero.AsSpan()); + } + + public static string CalcularDVBancoBTGPactual(this ReadOnlySpan nossoNumero) + { + int soma = 0; + int multiplicador = 2; // Percorrer o número de trás para frente for (int i = nossoNumero.Length - 1; i >= 0; i--) { // Multiplicar cada dígito pela sequência crescente de 2 a 9 - int numero = int.Parse(nossoNumero[i].ToString()); + int numero = nossoNumero[i] - '0'; soma += numero * multiplicador; // Atualizar o multiplicador (2 a 9 e depois volta para 2) @@ -238,46 +270,30 @@ public static string CalcularDVBancoBTGPactual(this string nossoNumero) // Aplicar o Módulo 11 int resto = soma % 11; - int digitoVerificador; - - if (resto == 0 || resto == 1) - { - digitoVerificador = 1; // Regra para evitar dígito zero - } - else if (resto == 10) - { - digitoVerificador = 1; // Também pode ser 1, dependendo da regra do banco - } - else - { - digitoVerificador = 11 - resto; - } + int digitoVerificador = (resto == 0 || resto == 1 || resto == 10) ? 1 : 11 - resto; return digitoVerificador.ToString(); } public static string CalcularDVDaycoval(this string texto) { - string digito; - int soma = 0, peso = 2, digTmp = 0; + return CalcularDVDaycoval(texto.AsSpan()); + } + + public static string CalcularDVDaycoval(this ReadOnlySpan texto) + { + int soma = 0, peso = 2; for (var i = texto.Length - 1; i >= 0; i--) { - digTmp = (int)char.GetNumericValue(texto[i]) * peso; + int digito = texto[i] - '0'; + int digTmp = digito * peso; if (digTmp > 9) digTmp = (digTmp / 10) + (digTmp % 10); - soma = soma + digTmp; - - if (peso == 2) - peso = 1; - else - peso = peso + 1; + soma += digTmp; + peso = peso == 2 ? 1 : peso + 1; } - var resto = (soma % 10); - if (resto == 0) - digito = "0"; - else - digito = (10 - resto).ToString(); - return digito; + var resto = soma % 10; + return resto == 0 ? "0" : (10 - resto).ToString(); } } diff --git a/BoletoNetCore/Util/TCampoRegistroEDI.cs b/BoletoNetCore/Util/TCampoRegistroEDI.cs index 2ade3a96..3caa31f9 100644 --- a/BoletoNetCore/Util/TCampoRegistroEDI.cs +++ b/BoletoNetCore/Util/TCampoRegistroEDI.cs @@ -4,7 +4,7 @@ namespace BoletoNetCore { public class TCampoRegistroEDI { - #region Variáveis Privadas + #region Vari�veis Privadas private string _DescricaoCampo; private TTiposDadoEDI _TipoCampo; private int _TamanhoCampo; @@ -21,7 +21,7 @@ public class TCampoRegistroEDI #region Propriedades /// - /// Descrição do campo no registro EDI (meramente descritivo) + /// Descri��o do campo no registro EDI (meramente descritivo) /// public string DescricaoCampo { @@ -30,7 +30,7 @@ public string DescricaoCampo } /// - /// Tipo de dado de ORIGEM das informações do campo EDI. + /// Tipo de dado de ORIGEM das informa��es do campo EDI. /// public TTiposDadoEDI TipoCampo { @@ -48,8 +48,8 @@ public int TamanhoCampo } /// - /// Quantidade de casas decimais do campo, caso ele seja do tipo numérico sem decimais. Caso - /// não se aplique ao tipo de dado, o valor da propriedade será ignorado nas funções de formatação. + /// Quantidade de casas decimais do campo, caso ele seja do tipo num�rico sem decimais. Caso + /// n�o se aplique ao tipo de dado, o valor da propriedade ser� ignorado nas fun��es de formata��o. /// public int QtdDecimais { @@ -58,7 +58,7 @@ public int QtdDecimais } /// - /// Valor de ORIGEM do campo, sem formatação, no tipo de dado adequado ao campo. O valor deve ser atribuido + /// Valor de ORIGEM do campo, sem formata��o, no tipo de dado adequado ao campo. O valor deve ser atribuido /// com o tipo de dado adequado ao seu proposto, por exemplo, Double para representar valor, DateTime para /// representar datas e/ou horas, etc. /// @@ -69,10 +69,10 @@ public object ValorNatural } /// - /// Valor formatado do campo, pronto para ser utilizado no arquivo EDI. A formatação será de acordo - /// com a especificada na propriedade TipoCampo, com numéricos alinhados à direita e zeros à esquerda - /// e campos alfanuméricos alinhados à esquerda e com brancos à direita. - /// Também pode receber o valor vindo do arquivo EDI, para ser decodificado e o resultado da decodificação na propriedade + /// Valor formatado do campo, pronto para ser utilizado no arquivo EDI. A formata��o ser� de acordo + /// com a especificada na propriedade TipoCampo, com num�ricos alinhados � direita e zeros � esquerda + /// e campos alfanum�ricos alinhados � esquerda e com brancos � direita. + /// Tamb�m pode receber o valor vindo do arquivo EDI, para ser decodificado e o resultado da decodifica��o na propriedade /// ValorNatural /// public string ValorFormatado @@ -82,7 +82,7 @@ public string ValorFormatado } /// - /// Número de ordem do campo no registro EDI + /// N�mero de ordem do campo no registro EDI /// public int OrdemNoRegistroEDI { @@ -92,7 +92,7 @@ public int OrdemNoRegistroEDI /// /// Caractere separador dos elementos de campos com o tipo DATA. Colocar null caso esta propriedade - /// não se aplique ao tipo de dado. + /// n�o se aplique ao tipo de dado. /// public string SeparadorDatas { @@ -102,7 +102,7 @@ public string SeparadorDatas /// /// Caractere separador dos elementos de campos com o tipo HORA. Colocar null caso esta propriedade - /// não se aplique ao tipo de dado. + /// n�o se aplique ao tipo de dado. /// public string SeparadorHora { @@ -111,7 +111,7 @@ public string SeparadorHora } /// - /// Posição do caracter inicial do campo no arquivo EDI + /// Posi��o do caracter inicial do campo no arquivo EDI /// public int PosicaoInicial { @@ -125,7 +125,7 @@ public int PosicaoFinal set { _PosicaoFinal = value; } } /// - /// Caractere de Preenchimento do campo da posição inicial até a posição final + /// Caractere de Preenchimento do campo da posi��o inicial at� a posi��o final /// public char Preenchimento { @@ -143,16 +143,16 @@ public TCampoRegistroEDI() } /// - /// Cria um objeto do tipo TCampoRegistroEDI inicializando as propriedades básicas. + /// Cria um objeto do tipo TCampoRegistroEDI inicializando as propriedades b�sicas. /// /// Tipo de dado de origem dos dados - /// Posição Inicial do Campo no Arquivo + /// Posi��o Inicial do Campo no Arquivo /// Tamanho em caracteres do campo (destino) /// Quantidade de decimais do campo (destino) - /// Valor do campo (Origem), no tipo de dado adequado ao propósito do campo - /// Caractere de Preenchimento do campo caso o valor não ocupe todo o tamanho - /// Separador de hora padrão; null para sem separador - /// Separador de data padrão; null para sem separador + /// Valor do campo (Origem), no tipo de dado adequado ao prop�sito do campo + /// Caractere de Preenchimento do campo caso o valor n�o ocupe todo o tamanho + /// Separador de hora padr�o; null para sem separador + /// Separador de data padr�o; null para sem separador public TCampoRegistroEDI(TTiposDadoEDI pTipoCampo, int pPosicaoInicial, int pTamanho, int pDecimais, object pValor, char pPreenchimento, string pSeparadorHora, string pSeparadorData) { this._TipoCampo = pTipoCampo; @@ -163,19 +163,19 @@ public TCampoRegistroEDI(TTiposDadoEDI pTipoCampo, int pPosicaoInicial, int pTam this._SeparadorDatas = pSeparadorData; this._OrdemNoRegistroEDI = 0; this._DescricaoCampo = ""; - this._PosicaoInicial = pPosicaoInicial - 1; //Compensa a indexação com base em zero + this._PosicaoInicial = pPosicaoInicial - 1; //Compensa a indexa��o com base em zero this._PosicaoFinal = pPosicaoInicial + this._TamanhoCampo; this._Preenchimento = pPreenchimento; } /// - /// Cria um objeto do tipo TCampoRegistroEDI inicializando as propriedades básicas. + /// Cria um objeto do tipo TCampoRegistroEDI inicializando as propriedades b�sicas. /// /// Tipo de dado de origem dos dados - /// Posição Inicial do Campo no Arquivo + /// Posi��o Inicial do Campo no Arquivo /// Tamanho em caracteres do campo (destino) /// Quantidade de decimais do campo (destino) - /// Valor do campo (Origem), no tipo de dado adequado ao propósito do campo - /// Caractere de Preenchimento do campo caso o valor não ocupe todo o tamanho + /// Valor do campo (Origem), no tipo de dado adequado ao prop�sito do campo + /// Caractere de Preenchimento do campo caso o valor n�o ocupe todo o tamanho public TCampoRegistroEDI(TTiposDadoEDI pTipoCampo, int pPosicaoInicial, int pTamanho, int pDecimais, object pValor, char pPreenchimento) { this._TipoCampo = pTipoCampo; @@ -186,15 +186,15 @@ public TCampoRegistroEDI(TTiposDadoEDI pTipoCampo, int pPosicaoInicial, int pTam this._SeparadorDatas = null; this._OrdemNoRegistroEDI = 0; this._DescricaoCampo = ""; - this._PosicaoInicial = pPosicaoInicial - 1; //Compensa a indexação com base em zero + this._PosicaoInicial = pPosicaoInicial - 1; //Compensa a indexa��o com base em zero this._PosicaoFinal = pPosicaoInicial + this._TamanhoCampo; this._Preenchimento = pPreenchimento; } #endregion - #region Métodos Públicos + #region M�todos P�blicos /// - /// Aplica formatação ao valor do campo em ValorNatural, colocando o resultado na propriedade ValorFormatado + /// Aplica formata��o ao valor do campo em ValorNatural, colocando o resultado na propriedade ValorFormatado /// public void CodificarNaturalParaEDI() { @@ -202,59 +202,32 @@ public void CodificarNaturalParaEDI() { case TTiposDadoEDI.ediAlphaAliEsquerda_____: { - if (this._ValorNatural != null) - { - if (this._ValorNatural.ToString().Trim().Length >= this._TamanhoCampo) - this._ValorFormatado = this._ValorNatural.ToString().Trim().Substring(0, this._TamanhoCampo); - else - this._ValorFormatado = this._ValorNatural.ToString().Trim().PadRight(this._TamanhoCampo, this._Preenchimento); //' ' - } - else - this._ValorFormatado = string.Empty.PadRight(this._TamanhoCampo, this._Preenchimento); //' ' + this._ValorFormatado = FormatarAlphaEsquerda(this._ValorNatural, this._TamanhoCampo, this._Preenchimento); break; } case TTiposDadoEDI.ediAlphaAliDireita______: { - if (this._ValorNatural != null) - { - if (this._ValorNatural.ToString().Trim().Length >= this._TamanhoCampo) - this._ValorFormatado = this._ValorNatural.ToString().Trim().Substring(0, this._TamanhoCampo); - else - this._ValorFormatado = this._ValorNatural.ToString().Trim().PadLeft(this._TamanhoCampo, this._Preenchimento); //' ' - } - else - this._ValorFormatado = string.Empty.PadLeft(this._TamanhoCampo, this._Preenchimento); //' ' + this._ValorFormatado = FormatarAlphaDireita(this._ValorNatural, this._TamanhoCampo, this._Preenchimento); break; } case TTiposDadoEDI.ediInteiro______________: { - this._ValorFormatado = this._ValorNatural.ToString().Trim().PadLeft(this._TamanhoCampo, this._Preenchimento); //'0' + this._ValorFormatado = FormatarInteiro(this._ValorNatural, this._TamanhoCampo, this._Preenchimento); break; } case TTiposDadoEDI.ediNumericoSemSeparador_: { - if (this._ValorNatural == null) - { - string aux = ""; - this._ValorFormatado = aux.Trim().PadLeft(this._TamanhoCampo, ' ');//Se o Número for NULL, preenche com espaços em branco - } - else - { - string Formatacao = "{0:f" + this._QtdDecimais.ToString() + "}"; - this._ValorFormatado = String.Format(Formatacao, this._ValorNatural).Replace(",", "").Replace(".", "").Trim().PadLeft(this._TamanhoCampo, this._Preenchimento); //'0' - } + this._ValorFormatado = FormatarNumericoSemSeparador(this._ValorNatural, this._TamanhoCampo, this._QtdDecimais, this._Preenchimento); break; } case TTiposDadoEDI.ediNumericoComPonto_____: { - string Formatacao = "{0:f" + this._QtdDecimais.ToString() + "}"; - this._ValorFormatado = String.Format(Formatacao, this._ValorNatural).Replace(",", ".").Trim().PadLeft(this._TamanhoCampo, this._Preenchimento); //'0' + this._ValorFormatado = FormatarNumericoComPonto(this._ValorNatural, this._TamanhoCampo, this._QtdDecimais, this._Preenchimento); break; } case TTiposDadoEDI.ediNumericoComVirgula___: { - string Formatacao = "{0:f" + this._QtdDecimais.ToString() + "}"; - this._ValorFormatado = String.Format(Formatacao, this._ValorNatural).Replace(".", ",").Trim().PadLeft(this._TamanhoCampo, this._Preenchimento); //'0' + this._ValorFormatado = FormatarNumericoComVirgula(this._ValorNatural, this._TamanhoCampo, this._QtdDecimais, this._Preenchimento); break; } case TTiposDadoEDI.ediDataAAAAMMDD_________: @@ -441,29 +414,30 @@ public void DecodificarEDIParaNatural() { if (!this._ValorFormatado.Trim().Equals("")) { - string cAno = ""; - string cMes = ""; - string cDia = ""; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int ano, mes, dia; + if (this._SeparadorDatas != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorDatas.ToCharArray()); - cAno = split[0]; - cMes = split[1]; - cDia = split[2]; + SplitDataHora(texto, this._SeparadorDatas[0], out var p1, out var p2, out var p3); + ano = int.Parse(p1.ToString()); + mes = int.Parse(p2.ToString()); + dia = int.Parse(p3.ToString()); } else { - cAno = this._ValorFormatado.Substring(0, 4); - cMes = this._ValorFormatado.Substring(4, 2); - cDia = this._ValorFormatado.Substring(6, 2); + ano = int.Parse(texto.Slice(0, 4).ToString()); + mes = int.Parse(texto.Slice(4, 2).ToString()); + dia = int.Parse(texto.Slice(6, 2).ToString()); } - if ((cDia.Equals("00") && cMes.Equals("00") && cAno.Equals("0000"))) + + if (dia == 0 && mes == 0 && ano == 0) { this._ValorNatural = null; } else { - this._ValorNatural = DateTime.Parse(cDia + "/" + cMes + "/" + cAno); + this._ValorNatural = new DateTime(ano, mes, dia); } } else @@ -476,21 +450,22 @@ public void DecodificarEDIParaNatural() { if (!this._ValorFormatado.Trim().Equals("")) { - string cAno = "1900"; - string cMes = ""; - string cDia = ""; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int dia, mes; + if (this._SeparadorDatas != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorDatas.ToCharArray()); - cMes = split[1]; - cDia = split[0]; + SplitDataHora(texto, this._SeparadorDatas[0], out var p1, out var p2, out _); + dia = int.Parse(p1.ToString()); + mes = int.Parse(p2.ToString()); } else { - cMes = this._ValorFormatado.Substring(2, 2); - cDia = this._ValorFormatado.Substring(0, 2); + dia = int.Parse(texto.Slice(0, 2).ToString()); + mes = int.Parse(texto.Slice(2, 2).ToString()); } - this._ValorNatural = DateTime.Parse(cDia + "/" + cMes + "/" + cAno); + + this._ValorNatural = new DateTime(1900, mes, dia); } else { @@ -500,128 +475,147 @@ public void DecodificarEDIParaNatural() } case TTiposDadoEDI.ediDataDDMMAAAA_________: { - string cDia = ""; - string cMes = ""; - string cAno = ""; - if (this._SeparadorDatas != null) - { - string[] split = this._ValorFormatado.Split(this._SeparadorDatas.ToCharArray()); - cAno = split[2]; - cMes = split[1]; - cDia = split[0]; - } - else - { - cDia = this._ValorFormatado.Substring(0, 2); - cMes = this._ValorFormatado.Substring(2, 2); - cAno = this._ValorFormatado.Substring(4, 4); - } - if ((cDia.Equals("00") && cMes.Equals("00") && cAno.Equals("0000")) || this._ValorFormatado.Trim().Equals("")) + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + + if (texto.IsEmpty) { - this._ValorNatural = DateTime.Parse("01/01/1900"); //data start + this._ValorNatural = new DateTime(1900, 1, 1); //data start } else { - this._ValorNatural = DateTime.Parse(cDia + "/" + cMes + "/" + cAno); + int dia, mes, ano; + + if (this._SeparadorDatas != null) + { + SplitDataHora(texto, this._SeparadorDatas[0], out var p1, out var p2, out var p3); + dia = int.Parse(p1.ToString()); + mes = int.Parse(p2.ToString()); + ano = int.Parse(p3.ToString()); + } + else + { + dia = int.Parse(texto.Slice(0, 2).ToString()); + mes = int.Parse(texto.Slice(2, 2).ToString()); + ano = int.Parse(texto.Slice(4, 4).ToString()); + } + + if (dia == 0 && mes == 0 && ano == 0) + { + this._ValorNatural = new DateTime(1900, 1, 1); //data start + } + else + { + this._ValorNatural = new DateTime(ano, mes, dia); + } } break; } case TTiposDadoEDI.ediDataDDMMAA___________: { - string cDia = ""; - string cMes = ""; - string cAno = ""; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int dia, mes, ano; + if (this._SeparadorDatas != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorDatas.ToCharArray()); - cAno = split[2]; - cMes = split[1]; - cDia = split[0]; + SplitDataHora(texto, this._SeparadorDatas[0], out var p1, out var p2, out var p3); + dia = int.Parse(p1.ToString()); + mes = int.Parse(p2.ToString()); + ano = int.Parse(p3.ToString()); } else { - cDia = this._ValorFormatado.Substring(0, 2); - cMes = this._ValorFormatado.Substring(2, 2); - cAno = this._ValorFormatado.Substring(4, 2); + dia = int.Parse(texto.Slice(0, 2).ToString()); + mes = int.Parse(texto.Slice(2, 2).ToString()); + ano = int.Parse(texto.Slice(4, 2).ToString()); } - this._ValorNatural = DateTime.Parse(cDia + "/" + cMes + "/" + cAno); + + // Ajusta ano de 2 dígitos para 4 dígitos + if (ano < 100) + ano += ano < 50 ? 2000 : 1900; + + this._ValorNatural = new DateTime(ano, mes, dia); break; } case TTiposDadoEDI.ediDataMMAAAA___________: { - string cDia = "01"; - string cMes = ""; - string cAno = ""; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int mes, ano; + if (this._SeparadorDatas != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorDatas.ToCharArray()); - cAno = split[1]; - cMes = split[0]; + SplitDataHora(texto, this._SeparadorDatas[0], out var p1, out var p2, out _); + mes = int.Parse(p1.ToString()); + ano = int.Parse(p2.ToString()); } else { - cMes = this._ValorFormatado.Substring(0, 2); - cAno = this._ValorFormatado.Substring(2, 4); + mes = int.Parse(texto.Slice(0, 2).ToString()); + ano = int.Parse(texto.Slice(2, 4).ToString()); } - this._ValorNatural = DateTime.Parse(cDia + "/" + cMes + "/" + cAno); + + this._ValorNatural = new DateTime(ano, mes, 1); break; } case TTiposDadoEDI.ediDataMMDD_____________: { - string cDia = ""; - string cMes = ""; - string cAno = "1900"; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int mes, dia; + if (this._SeparadorDatas != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorDatas.ToCharArray()); - cMes = split[0]; - cDia = split[1]; + SplitDataHora(texto, this._SeparadorDatas[0], out var p1, out var p2, out _); + mes = int.Parse(p1.ToString()); + dia = int.Parse(p2.ToString()); } else { - cDia = this._ValorFormatado.Substring(2, 2); - cMes = this._ValorFormatado.Substring(0, 2); + mes = int.Parse(texto.Slice(0, 2).ToString()); + dia = int.Parse(texto.Slice(2, 2).ToString()); } - this._ValorNatural = DateTime.Parse(cDia + "/" + cMes + "/" + cAno); + + this._ValorNatural = new DateTime(1900, mes, dia); break; } case TTiposDadoEDI.ediHoraHHMM_____________: { - string cHora = ""; - string cMinuto = ""; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int hora, minuto; + if (this._SeparadorHora != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorHora.ToCharArray()); - cHora = split[0]; - cMinuto = split[1]; + SplitDataHora(texto, this._SeparadorHora[0], out var p1, out var p2, out _); + hora = int.Parse(p1.ToString()); + minuto = int.Parse(p2.ToString()); } else { - cHora = this._ValorFormatado.Substring(0, 2); - cMinuto = this._ValorFormatado.Substring(2, 2); + hora = int.Parse(texto.Slice(0, 2).ToString()); + minuto = int.Parse(texto.Slice(2, 2).ToString()); } - this._ValorNatural = DateTime.Parse(cHora + ":" + cMinuto + ":00"); + + this._ValorNatural = new DateTime(1, 1, 1, hora, minuto, 0); break; } case TTiposDadoEDI.ediHoraHHMMSS___________: { - string cHora = ""; - string cMinuto = ""; - string cSegundo = ""; + ReadOnlySpan texto = this._ValorFormatado.AsSpan().Trim(); + int hora, minuto, segundo; + if (this._SeparadorHora != null) { - string[] split = this._ValorFormatado.Split(this._SeparadorHora.ToCharArray()); - cHora = split[0]; - cMinuto = split[1]; - cSegundo = split[2]; + SplitDataHora(texto, this._SeparadorHora[0], out var p1, out var p2, out var p3); + hora = int.Parse(p1.ToString()); + minuto = int.Parse(p2.ToString()); + segundo = int.Parse(p3.ToString()); } else { - cHora = this._ValorFormatado.Substring(0, 2); - cMinuto = this._ValorFormatado.Substring(2, 2); - cSegundo = this._ValorFormatado.Substring(4, 2); + hora = int.Parse(texto.Slice(0, 2).ToString()); + minuto = int.Parse(texto.Slice(2, 2).ToString()); + segundo = int.Parse(texto.Slice(4, 2).ToString()); } - this._ValorNatural = DateTime.Parse(cHora + ":" + cMinuto + ":00"); + + this._ValorNatural = new DateTime(1, 1, 1, hora, minuto, segundo); break; } case TTiposDadoEDI.ediDataDDMMAAAAWithZeros: @@ -638,7 +632,140 @@ public void DecodificarEDIParaNatural() #endregion - #region Métodos Privados e Protegidos + #region M�todos Privados e Protegidos + + /// /// Divide uma string de data/hora em 3 partes usando um separador (compatível com .NET Standard 2.0) + /// + private static void SplitDataHora(ReadOnlySpan texto, char separador, out ReadOnlySpan parte1, out ReadOnlySpan parte2, out ReadOnlySpan parte3) + { + int firstSep = texto.IndexOf(separador); + if (firstSep < 0) + { + // Sem separador, retorna vazio + parte1 = ReadOnlySpan.Empty; + parte2 = ReadOnlySpan.Empty; + parte3 = ReadOnlySpan.Empty; + return; + } + + parte1 = texto.Slice(0, firstSep); + + int secondSep = texto.Slice(firstSep + 1).IndexOf(separador); + if (secondSep < 0) + { + // Apenas 2 partes + parte2 = texto.Slice(firstSep + 1); + parte3 = ReadOnlySpan.Empty; + return; + } + + secondSep += firstSep + 1; // Ajusta para posição absoluta + parte2 = texto.Slice(firstSep + 1, secondSep - firstSep - 1); + parte3 = texto.Slice(secondSep + 1); + } + + /// /// Formata valor alpha alinhado � esquerda com padding � direita + /// + private static string FormatarAlphaEsquerda(object valor, int tamanho, char preenchimento) + { + if (valor == null) + return new string(preenchimento, tamanho); + + string textoOriginal = valor.ToString().Trim(); + + if (textoOriginal.Length >= tamanho) + return textoOriginal.Substring(0, tamanho); + + // Usa PadRight otimizado + return textoOriginal.PadRight(tamanho, preenchimento); + } + + /// + /// Formata valor alpha alinhado � direita com padding � esquerda + /// + private static string FormatarAlphaDireita(object valor, int tamanho, char preenchimento) + { + if (valor == null) + return new string(preenchimento, tamanho); + + string textoOriginal = valor.ToString().Trim(); + + if (textoOriginal.Length >= tamanho) + return textoOriginal.Substring(0, tamanho); + + // Usa PadLeft otimizado + return textoOriginal.PadLeft(tamanho, preenchimento); + } + + /// + /// Formata valor inteiro com padding � esquerda + /// + private static string FormatarInteiro(object valor, int tamanho, char preenchimento) + { + if (valor == null) + return new string(preenchimento, tamanho); + + string textoOriginal = valor.ToString().Trim(); + + if (textoOriginal.Length >= tamanho) + return textoOriginal.Substring(0, tamanho); + + return textoOriginal.PadLeft(tamanho, preenchimento); + } + + /// + /// Formata valor num�rico sem separador decimal + /// + private static string FormatarNumericoSemSeparador(object valor, int tamanho, int decimais, char preenchimento) + { + if (valor == null) + return new string(' ', tamanho); // Regra espec�fica: NULL = espa�os + + // Formata e remove separadores + string formatacao = $"{{0:f{decimais}}}"; + string resultado = string.Format(formatacao, valor) + .Replace(",", "") + .Replace(".", "") + .Trim(); + + if (resultado.Length >= tamanho) + return resultado.Substring(0, tamanho); + + return resultado.PadLeft(tamanho, preenchimento); + } + + /// + /// Formata valor num�rico com ponto decimal + /// + private static string FormatarNumericoComPonto(object valor, int tamanho, int decimais, char preenchimento) + { + string formatacao = $"{{0:f{decimais}}}"; + string resultado = string.Format(formatacao, valor) + .Replace(",", ".") + .Trim(); + + if (resultado.Length >= tamanho) + return resultado.Substring(0, tamanho); + + return resultado.PadLeft(tamanho, preenchimento); + } + + /// + /// Formata valor num�rico com v�rgula decimal + /// + private static string FormatarNumericoComVirgula(object valor, int tamanho, int decimais, char preenchimento) + { + string formatacao = $"{{0:f{decimais}}}"; + string resultado = string.Format(formatacao, valor) + .Replace(".", ",") + .Trim(); + + if (resultado.Length >= tamanho) + return resultado.Substring(0, tamanho); + + return resultado.PadLeft(tamanho, preenchimento); + } + #endregion diff --git a/BoletoNetCore/Util/Utils.cs b/BoletoNetCore/Util/Utils.cs index 923dcb68..e1c0b433 100644 --- a/BoletoNetCore/Util/Utils.cs +++ b/BoletoNetCore/Util/Utils.cs @@ -139,15 +139,28 @@ internal static string FormataCPFCPPJ(string value) } /// - /// Formata o número do CPF 92074286520 para 920.742.865-20 + /// Formata o n�mero do CPF 92074286520 para 920.742.865-20 /// - /// Sequencia numérica de 11 dígitos. Exemplo: 00000000000 + /// Sequencia num�rica de 11 d�gitos. Exemplo: 00000000000 /// CPF formatado internal static string FormataCPF(string cpf) { try { - return cpf != null && cpf.Length == 11 ? $"{cpf.Substring(0, 3)}.{cpf.Substring(3, 3)}.{cpf.Substring(6, 3)}-{cpf.Substring(9, 2)}" : cpf; + if (cpf == null || cpf.Length != 11) + return cpf; + + // Otimizado: usa array ao invés de múltiplos Substring + char[] result = new char[14]; + cpf.CopyTo(0, result, 0, 3); // 000 + result[3] = '.'; + cpf.CopyTo(3, result, 4, 3); // 000 + result[7] = '.'; + cpf.CopyTo(6, result, 8, 3); // 000 + result[11] = '-'; + cpf.CopyTo(9, result, 12, 2); // 00 + + return new string(result); } catch { @@ -158,13 +171,28 @@ internal static string FormataCPF(string cpf) /// /// Formata o CNPJ. Exemplo 00.316.449/0001-63 /// - /// Sequencia numérica de 14 dígitos. Exemplo: 00000000000000 + /// Sequencia num�rica de 14 d�gitos. Exemplo: 00000000000000 /// CNPJ formatado internal static string FormataCNPJ(string cnpj) { try { - return cnpj != null && cnpj.Length == 14 ? $"{cnpj.Substring(0, 2)}.{cnpj.Substring(2, 3)}.{cnpj.Substring(5, 3)}/{cnpj.Substring(8, 4)}-{cnpj.Substring(12, 2)}" : cnpj; + if (cnpj == null || cnpj.Length != 14) + return cnpj; + + // Otimizado: usa array ao invés de múltiplos Substring + char[] result = new char[18]; + cnpj.CopyTo(0, result, 0, 2); // 00 + result[2] = '.'; + cnpj.CopyTo(2, result, 3, 3); // 000 + result[6] = '.'; + cnpj.CopyTo(5, result, 7, 3); // 000 + result[10] = '/'; + cnpj.CopyTo(8, result, 11, 4); // 0000 + result[15] = '-'; + cnpj.CopyTo(12, result, 16, 2); // 00 + + return new string(result); } catch { @@ -175,13 +203,22 @@ internal static string FormataCNPJ(string cnpj) /// /// Formato o CEP em 00000-000 /// - /// Sequencia numérica de 8 dígitos. Exemplo: 00000000 + /// Sequencia num�rica de 8 d�gitos. Exemplo: 00000000 /// CEP formatado internal static string FormataCEP(string cep) { try { - return cep != null && cep.Length == 8 ? $"{cep.Substring(0, 2)}{cep.Substring(2, 3)}-{cep.Substring(5, 3)}" : cep; + if (cep == null || cep.Length != 8) + return cep; + + // Otimizado: usa array ao invés de múltiplos Substring + char[] result = new char[9]; + cep.CopyTo(0, result, 0, 5); // 00000 + result[5] = '-'; + cep.CopyTo(5, result, 6, 3); // 000 + + return new string(result); } catch { @@ -199,35 +236,35 @@ public static string SubstituiCaracteresEspeciais(string strline) { try { - strline = strline.Replace("ã", "a"); - strline = strline.Replace('Ã', 'A'); - strline = strline.Replace('â', 'a'); - strline = strline.Replace('Â', 'A'); - strline = strline.Replace('á', 'a'); - strline = strline.Replace('Á', 'A'); - strline = strline.Replace('à', 'a'); - strline = strline.Replace('À', 'A'); - strline = strline.Replace('ç', 'c'); - strline = strline.Replace('Ç', 'C'); - strline = strline.Replace('é', 'e'); - strline = strline.Replace('É', 'E'); - strline = strline.Replace('Ê', 'E'); - strline = strline.Replace('ê', 'e'); - strline = strline.Replace('õ', 'o'); - strline = strline.Replace('Õ', 'O'); - strline = strline.Replace('ó', 'o'); - strline = strline.Replace('Ó', 'O'); - strline = strline.Replace('ô', 'o'); - strline = strline.Replace('Ô', 'O'); - strline = strline.Replace('ú', 'u'); - strline = strline.Replace('Ú', 'U'); - strline = strline.Replace('ü', 'u'); - strline = strline.Replace('Ü', 'U'); - strline = strline.Replace('í', 'i'); - strline = strline.Replace('Í', 'I'); - strline = strline.Replace('ª', 'a'); - strline = strline.Replace('º', 'o'); - strline = strline.Replace('°', 'o'); + strline = strline.Replace("�", "a"); + strline = strline.Replace('�', 'A'); + strline = strline.Replace('�', 'a'); + strline = strline.Replace('�', 'A'); + strline = strline.Replace('�', 'a'); + strline = strline.Replace('�', 'A'); + strline = strline.Replace('�', 'a'); + strline = strline.Replace('�', 'A'); + strline = strline.Replace('�', 'c'); + strline = strline.Replace('�', 'C'); + strline = strline.Replace('�', 'e'); + strline = strline.Replace('�', 'E'); + strline = strline.Replace('�', 'E'); + strline = strline.Replace('�', 'e'); + strline = strline.Replace('�', 'o'); + strline = strline.Replace('�', 'O'); + strline = strline.Replace('�', 'o'); + strline = strline.Replace('�', 'O'); + strline = strline.Replace('�', 'o'); + strline = strline.Replace('�', 'O'); + strline = strline.Replace('�', 'u'); + strline = strline.Replace('�', 'U'); + strline = strline.Replace('�', 'u'); + strline = strline.Replace('�', 'U'); + strline = strline.Replace('�', 'i'); + strline = strline.Replace('�', 'I'); + strline = strline.Replace('�', 'a'); + strline = strline.Replace('�', 'o'); + strline = strline.Replace('�', 'o'); strline = strline.Replace('&', 'e'); return strline; }