ti-enxame.com

Criando um arquivo de largura fixa em C #

Qual é a melhor maneira de criar um arquivo de largura fixa em C #. Eu tenho um monte de campos com comprimentos para escrever. Diga 20,80.10,2 etc, todos alinhados à esquerda. Existe uma maneira fácil de fazer isso?

38
Brian G

Você pode usar string.Format para preencher facilmente um valor com espaços, por exemplo.

string a = String.Format("|{0,5}|{1,5}|{2,5}", 1, 20, 300);
string b = String.Format("|{0,-5}|{1,-5}|{2,-5}", 1, 20, 300);

// 'a' will be equal to "|    1|   20|  300|"
// 'b' will be equal to "|1    |20   |300  |"
52
Wheelie

Este é um sistema que eu criei para um módulo de gravação de arquivo de largura fixa configurável. Ele está configurado com um arquivo XML, a parte relevante assim:

<WriteFixedWidth Table="orders" StartAt="1" Output="Return">
  <Position Start="1" Length="17" Name="Unique Identifier"/>
  <Position Start="18" Length="3" Name="Error Flag"/>
  <Position Start="21" Length="16" Name="Account Number" Justification="right"/>
  <Position Start="37" Length="8" Name="Member Number"/>
  <Position Start="45" Length="4" Name="Product"/>
  <Position Start="49" Length="3" Name="Paytype"/>
  <Position Start="52" Length="9" Name="Transit Routing Number"/>
</WriteFixedWidth>

StartAt informa ao programa se suas posições são baseadas em 0 ou em 1. Eu fiz isso configurável porque copiava as compensações das especificações e queria que a configuração se parecesse com as especificações o máximo possível, independentemente do índice inicial escolhido pelo autor.

O atributo Name nas marcas Position se refere aos nomes das colunas em uma DataTable.

O código a seguir foi gravado para .Net 3.5, usando LINQ-XML, portanto, o método pressupunha que fosse passado um XElement com a configuração acima, que você pode obter depois de usar XDocument.Load(filename) para carregar o Arquivo XML, chame .Descendants("WriteFixedWidth") no objeto XDocument para obter o elemento de configuração.

    public void WriteFixedWidth(System.Xml.Linq.XElement CommandNode, DataTable Table, Stream outputStream)
    {
        StreamWriter Output = new StreamWriter(outputStream);
        int StartAt = CommandNode.Attribute("StartAt") != null ? int.Parse(CommandNode.Attribute("StartAt").Value) : 0;

        var positions = from c in CommandNode.Descendants(Namespaces.Integration + "Position")
                        orderby int.Parse(c.Attribute("Start").Value) ascending
                        select new
                        {
                            Name = c.Attribute("Name").Value,
                            Start = int.Parse(c.Attribute("Start").Value) - StartAt,
                            Length = int.Parse(c.Attribute("Length").Value),
                            Justification = c.Attribute("Justification") != null ? c.Attribute("Justification").Value.ToLower() : "left"
                        };

        int lineLength = positions.Last().Start + positions.Last().Length;
        foreach (DataRow row in Table.Rows)
        {
            StringBuilder line = new StringBuilder(lineLength);
            foreach (var p in positions)
                line.Insert(p.Start, 
                    p.Justification == "left" ? (row.Field<string>(p.Name) ?? "").PadRight(p.Length,' ')
                                              : (row.Field<string>(p.Name) ?? "").PadLeft(p.Length,' ') 
                    );
            Output.WriteLine(line.ToString());
        }
        Output.Flush();
    }

O mecanismo é o StringBuilder, que é mais rápido do que concatenar seqüências imutáveis ​​juntas, especialmente se você estiver processando arquivos com vários megabytes.

27
Chris Wenham

Experimente o FileHelpers: www.filehelpers.com

Aqui está um exemplo: http://www.filehelpers.com/quick_start_fixed.html

9
Leandro Oliveira

Use a função .PadRight (para dados alinhados à esquerda) da classe String. Assim:

handle.WriteLine(s20.PadRight(20));
handle.WriteLine(s80.PadRight(80));
handle.WriteLine(s10.PadRight(10));
handle.WriteLine(s2.PadRight(2));
7
Andrew Burgess
5
Yitzchok

Estou usando um método de extensão em string, sim, os comentários XML podem parecer OTT para isso, mas se você quiser que outros desenvolvedores reutilizem ...

public static class StringExtensions
{

    /// <summary>
    /// FixedWidth string extension method.  Trims spaces, then pads right.
    /// </summary>
    /// <param name="self">extension method target</param>
    /// <param name="totalLength">The length of the string to return (including 'spaceOnRight')</param>
    /// <param name="spaceOnRight">The number of spaces required to the right of the content.</param>
    /// <returns>a new string</returns>
    /// <example>
    /// This example calls the extension method 3 times to construct a string with 3 fixed width fields of 20 characters, 
    /// 2 of which are reserved for empty spacing on the right side.
    /// <code>
    ///const int colWidth = 20;
    ///const int spaceRight = 2;
    ///string headerLine = string.Format(
    ///    "{0}{1}{2}",
    ///    "Title".FixedWidth(colWidth, spaceRight),
    ///    "Quantity".FixedWidth(colWidth, spaceRight),
    ///    "Total".FixedWidth(colWidth, spaceRight));
    /// </code>
    /// </example>
    public static string FixedWidth(this string self, int totalLength, int spaceOnRight)
    {
        if (totalLength < spaceOnRight) spaceOnRight = 1; // handle silly use.

        string s = self.Trim();

        if (s.Length > (totalLength - spaceOnRight))
        {
            s = s.Substring(0, totalLength - spaceOnRight);
        }

        return s.PadRight(totalLength);
    }
}
2
Darren

Você pode usar um StreamWriter e, na chamada Write (string), use String.Format () para criar uma string com a largura correta para o campo especificado.

1
Scott Dorman

A resposta de Darren a essa pergunta me inspirou a usar métodos de extensão, mas, em vez de estender String, estendi StringBuilder. Eu escrevi dois métodos:

public static StringBuilder AppendFixed(this StringBuilder sb, int length, string value)
{
    if (String.IsNullOrWhiteSpace(value))
        return sb.Append(String.Empty.PadLeft(length));

    if (value.Length <= length)
        return sb.Append(value.PadLeft(length));
    else
        return sb.Append(value.Substring(0, length));
}

public static StringBuilder AppendFixed(this StringBuilder sb, int length, string value, out string rest)
{
    rest = String.Empty;

    if (String.IsNullOrWhiteSpace(value))
        return sb.AppendFixed(length, value);

    if (value.Length > length)
        rest = value.Substring(length);

    return sb.AppendFixed(length, value);
}

O primeiro ignora silenciosamente uma string muito longa e simplesmente corta o final dela, e o segundo retorna a parte cortada através do parâmetro out do método.

Exemplo:

string rest;    

StringBuilder clientRecord = new StringBuilder();
clientRecord.AppendFixed(40, doc.ClientName, out rest); 
clientRecord.AppendFixed(40, rest);
clientRecord.AppendFixed(40, doc.ClientAddress, out rest);
clientRecord.AppendFixed(40, rest);
1
Jojo

As várias postagens de preenchimento/formatação anteriores funcionarão o suficiente, mas você pode estar interessado em implementar o ISerializable.

Aqui está um artigo do msdn sobre serialização de objeto no .NET

0
CrashCodes

Você não pode usar o arquivo de texto padrão? Você pode ler os dados de volta a linha.

0
artur02
0
Chris Ballance

Tente usar myString.PadRight (totalLengthForField, '')

0
viggity

Você quer dizer que deseja preencher todos os números à direita com espaços?

Nesse caso, String.PadRight ou String.Format deve colocá-lo no caminho certo.

0
Tobi

Você pode usar ASCIIEncoding.UTF8.GetBytes (texto) para convertê-lo em uma matriz de bytes. Em seguida, grave as matrizes de bytes no arquivo como um registro de tamanho fixo.

UTF8 varia no número de bytes necessários para representar alguns caracteres; o UTF16 é um pouco mais previsível, 2 bytes por caractere.

0
davenpcj