Как зайти в Даркнет?!
25th January, 01:11
8
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
899
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
952
0
Очень долго работает Update запрос Oracle
27th January, 09:58
916
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
907
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
942
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1727
0
период по дням
25th October, 10:44
3957
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3722
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4614
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4382
0
Помогите пожалуйста решить задачи
24th November, 23:53
6087
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4352
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4400
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
CSV обработка строк
Типичный способ создания строки CSV (псевдокод):
- Создайте объект контейнера CSV (как StringBuilder в C#).
- Перебирайте строки, которые вы хотите добавить, добавляя запятую после каждой из них.
- После цикла удалите последнюю лишнюю запятую.
Пример кода:
public string ReturnAsCSV(ContactList contactList)
{
StringBuilder sb = new StringBuilder();
foreach (Contact c in contactList)
{
sb.Append(c.Name + ",");
}
sb.Remove(sb.Length - 1, 1);
//sb.Replace(",", "", sb.Length - 1, 1)
return sb.ToString();
}
Мне нравится идея добавления запятой, проверяя, пуст ли контейнер, но не означает ли это больше обработки, так как необходимо проверять длину строки при каждом вхождении?
Я чувствую, что должен быть easier/cleaner/more эффективный способ удаления этой последней запятой. Есть какие-нибудь идеи?
Вы можете использовать LINQ для объектов :
string [] strings = contactList.Select(c => c.Name).ToArray();
string csv = string.Join(",", strings);
Очевидно, что все это можно было бы сделать в одной строке, но немного яснее на двух.
Ваш код не совсем соответствует полному формату CSV . Если вы просто генерируете CSV из данных, которые не содержат запятых, пробелов в начале/trail, табуляций, новых строк или кавычек, это должно быть нормально. Тем не менее, в большинстве реальных сценариев обмена данными вам действительно нужна полная имплементация.
Для генерации правильного CSV, вы можете использовать это:
public static String EncodeCsvLine(params String[] fields)
{
StringBuilder line = new StringBuilder();
for (int i = 0; i < fields.Length; i++)
{
if (i > 0)
{
line.Append(DelimiterChar);
}
String csvField = EncodeCsvField(fields[i]);
line.Append(csvField);
}
return line.ToString();
}
static String EncodeCsvField(String field)
{
StringBuilder sb = new StringBuilder();
sb.Append(field);
// Some fields with special characters must be embedded in double quotes
bool embedInQuotes = false;
// Embed in quotes to preserve leading/tralining whitespace
if (sb.Length > 0 &&
(sb[0] == ' ' ||
sb[0] == '\t' ||
sb[sb.Length-1] == ' ' ||
sb[sb.Length-1] == '\t' ))
{
embedInQuotes = true;
}
for (int i = 0; i < sb.Length; i++)
{
// Embed in quotes to preserve: commas, line-breaks etc.
if (sb[i] == DelimiterChar ||
sb[i]=='\r' ||
sb[i]=='\n' ||
sb[i] == '"')
{
embedInQuotes = true;
break;
}
}
// If the field itself has quotes, they must each be represented
// by a pair of consecutive quotes.
sb.Replace("\"", "\"\"");
String rv = sb.ToString();
if (embedInQuotes)
{
rv = "\"" + rv + "\"";
}
return rv;
}
Возможно, это не самый эффективный код в мире, но он был протестирован. Реальный мир отстой по сравнению с быстрым примером кода :)
Почему бы не использовать одну из библиотек с открытым исходным кодом CSV?
Я знаю, что это звучит как излишество для чего-то, что кажется таким простым, но, как вы можете сказать по комментариям и фрагментам кода, это больше, чем кажется на первый взгляд. В дополнение к обработке полного соответствия CSV, вы в конечном итоге захотите обрабатывать как чтение, так и запись CSVs... и вам может понадобиться манипуляция файлами.
Я уже использовал Open CSV в одном из своих проектов (но есть много других, из которых можно выбрать). Это определенно облегчило мне жизнь. ;)
Не забудь нашего старого друга "for". Он не так красив, как foreach, но у него есть преимущество в том, что он может начать со второго элемента.
public string ReturnAsCSV(ContactList contactList)
{
if (contactList == null || contactList.Count == 0)
return string.Empty;
StringBuilder sb = new StringBuilder(contactList[0].Name);
for (int i = 1; i < contactList.Count; i++)
{
sb.Append(",");
sb.Append(contactList[i].Name);
}
return sb.ToString();
}
Вы также можете обернуть второе добавление в "if", который проверяет, содержит ли свойство Name двойную кавычку или запятую, и если да, то экранировать их соответствующим образом.
Вы также можете создать массив данных c.Name и использовать метод String.Join для создания своей строки.
public string ReturnAsCSV(ContactList contactList)
{
List<String> tmpList = new List<string>();
foreach (Contact c in contactList)
{
tmpList.Add(c.Name);
}
return String.Join(",", tmpList.ToArray());
}
Это может быть не так эффективно, как подход StringBuilder , но он определенно выглядит чище.
Кроме того, вы можете рассмотреть возможность использования .CurrentCulture.TextInfo.ListSeparator вместо жестко заданной запятой - если ваш вывод будет импортирован в другие приложения, у вас могут возникнуть проблемы с ним. ListSeparator может отличаться в разных культурах, и MS Excel, по крайней мере, уважает эту настройку. Так:
return String.Join(
System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator,
tmpList.ToArray());
Я уже использовал этот метод раньше. Свойство длины StringBuilder-это NOT только для чтения, поэтому вычитание его одним из способов усекает последний символ. Но вы должны убедиться, что ваша длина не равна нулю для начала (что произойдет, если ваш список пуст), потому что установка длины меньше нуля является ошибкой.
public string ReturnAsCSV(ContactList contactList)
{
StringBuilder sb = new StringBuilder();
foreach (Contact c in contactList)
{
sb.Append(c.Name + ",");
}
if (sb.Length > 0)
sb.Length -= 1;
return sb.ToString();
}
Я написал небольшой класс для этого на случай, если кто-то еще сочтет его полезным...
public class clsCSVBuilder
{
protected int _CurrentIndex = -1;
protected List<string> _Headers = new List<string>();
protected List<List<string>> _Records = new List<List<string>>();
protected const string SEPERATOR = ",";
public clsCSVBuilder() { }
public void CreateRow()
{
_Records.Add(new List<string>());
_CurrentIndex++;
}
protected string _EscapeString(string str)
{
return string.Format("\"{0}\"", str.Replace("\"", "\"\"")
.Replace("\r\n", " ")
.Replace("\n", " ")
.Replace("\r", " "));
}
protected void _AddRawString(string item)
{
_Records[_CurrentIndex].Add(item);
}
public void AddHeader(string name)
{
_Headers.Add(_EscapeString(name));
}
public void AddRowItem(string item)
{
_AddRawString(_EscapeString(item));
}
public void AddRowItem(int item)
{
_AddRawString(item.ToString());
}
public void AddRowItem(double item)
{
_AddRawString(item.ToString());
}
public void AddRowItem(DateTime date)
{
AddRowItem(date.ToShortDateString());
}
public static string GenerateTempCSVPath()
{
return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().ToLower().Replace("-", "") + ".csv");
}
protected string _GenerateCSV()
{
StringBuilder sb = new StringBuilder();
if (_Headers.Count > 0)
{
sb.AppendLine(string.Join(SEPERATOR, _Headers.ToArray()));
}
foreach (List<string> row in _Records)
{
sb.AppendLine(string.Join(SEPERATOR, row.ToArray()));
}
return sb.ToString();
}
public void SaveAs(string path)
{
using (StreamWriter sw = new StreamWriter(path))
{
sw.Write(_GenerateCSV());
}
}
}
Как насчет отслеживания того, находитесь ли вы на первом элементе, и только добавьте запятую перед элементом, если он не является первым.
public string ReturnAsCSV(ContactList contactList)
{
StringBuilder sb = new StringBuilder();
bool isFirst = true;
foreach (Contact c in contactList) {
if (!isFirst) {
// Only add comma before item if it is not the first item
sb.Append(",");
} else {
isFirst = false;
}
sb.Append(c.Name);
}
return sb.ToString();
}
Я использую CSVHelper - это отличная библиотека с открытым исходным кодом, которая позволяет вам генерировать совместимые потоки CSV по одному элементу за раз или создавать пользовательские карты ваших классов:
public string ReturnAsCSV(ContactList contactList)
{
StringBuilder sb = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(sb))
{
using (var csvWriter = new CsvHelper.CsvWriter(stringWriter))
{
csvWriter.Configuration.HasHeaderRecord = false;
foreach (Contact c in contactList)
{
csvWriter.WriteField(c.Name);
}
}
}
return sb.ToString();
}
или если вы составляете карту, то что-то вроде этого: csvWriter.WriteRecords<ContactList>(contactList);