Каков наилучший способ создания файла фиксированной ширины в С#. У меня есть куча полей, длина которых выписана. Скажем, 20,80.10,2 и т.д. Все выровненные слева. Есть простой способ сделать это?
Создание файла фиксированной ширины в С#
Ответ 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, который быстрее, чем объединение неизменяемых строк вместе, особенно если вы обрабатываете файлы с несколькими мегабайтами.
Ответ 3
Попробуйте FileHelpers: www.filehelpers.com
Вот пример: http://www.filehelpers.com/quick_start_fixed.html
Ответ 4
Используйте функцию .PadRight(для выровненных по левому краю данных) класса String. Итак:
handle.WriteLine(s20.PadRight(20));
handle.WriteLine(s80.PadRight(80));
handle.WriteLine(s10.PadRight(10));
handle.WriteLine(s2.PadRight(2));
Ответ 5
Вы можете использовать http://www.filehelpers.com/
Ответ 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
Вы не можете использовать стандартный текстовый файл? Вы можете читать данные по строкам.
Ответ 9
используйте String.Format()
Ответ 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);