Подтвердить что ты не робот

Создание файла фиксированной ширины в С#

Каков наилучший способ создания файла фиксированной ширины в С#. У меня есть куча полей, длина которых выписана. Скажем, 20,80.10,2 и т.д. Все выровненные слева. Есть простой способ сделать это?

4b9b3361

Ответ 1

Вы можете использовать string.Format, чтобы легко вводить значение с пробелами например.

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  |"

Ответ 2

Это система, которую я создал для настраиваемого модуля записи фиксированной ширины. Он сконфигурирован с XML файлом, соответствующая часть выглядит следующим образом:

<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 сообщает программе, будут ли ваши позиции основаны на 0 или 1. Я сделал это настраиваемым, потому что я буду копировать смещения из спецификаций и хотел бы, чтобы конфигурация была похожа на спецификацию как можно больше, независимо от того, какой начальный индекс выбрал автор.

Атрибут Name в тегах Position ссылается на имена столбцов в DataTable.

Следующий код был написан для .Net 3.5, используя LINQ-to-XML, поэтому предполагалось, что ему будет передан XElement с указанной выше конфигурацией, который вы можете получить после использования XDocument.Load(filename) для загрузки XML файл, затем вызовите .Descendants("WriteFixedWidth") в объекте XDocument, чтобы получить элемент конфигурации.

    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();
    }

Двигатель - это StringBuilder, который быстрее, чем объединение неизменяемых строк вместе, особенно если вы обрабатываете файлы с несколькими мегабайтами.

Ответ 4

Используйте функцию .PadRight(для выровненных по левому краю данных) класса String. Итак:

handle.WriteLine(s20.PadRight(20));
handle.WriteLine(s80.PadRight(80));
handle.WriteLine(s10.PadRight(10));
handle.WriteLine(s2.PadRight(2));

Ответ 6

Я использую метод расширения в строке, да, комментарий XML может показаться OTT для этого, но если вы хотите, чтобы другие разработчики повторно использовали...

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);
    }
}

Ответ 7

Вы можете использовать StreamWriter, а в вызове Write (string) используйте String.Format(), чтобы создать строку, которая является правильной шириной для данного поля.

Ответ 8

Вы не можете использовать стандартный текстовый файл? Вы можете читать данные по строкам.

Ответ 10

Попробуйте использовать myString.PadRight(totalLengthForField, '')

Ответ 11

Вы хотите, чтобы вы заполняли все числа справа пробелами?

Если да, String.PadRight или String.Format должен быть в курсе.

Ответ 12

Вы можете использовать ASCIIEncoding.UTF8.GetBytes(текст), чтобы преобразовать его в массив байтов. Затем напишите байтовые массивы в файл как запись фиксированного размера.

UTF8 изменяется в количестве байтов, необходимых для представления некоторых символов, UTF16 является немного более предсказуемым, 2 байта на символ.

Ответ 13

Различные сообщения о дополнении/форматировании будут работать достаточно достаточно, но вы можете быть заинтересованы в реализации ISerializable.

Здесь статья msdn о Сериализация объектов в .NET

Ответ 14

Даррен ответил на этот вопрос, вдохновив меня на использование методов расширения, но вместо расширения String я расширил StringBuilder. Я написал два метода:

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);
}

Сначала он молча игнорирует слишком длинную строку и просто отсекает ее, а второй возвращает отрезанную часть через параметр out метода.

Пример:

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);