c # Удалить строки из csv

#c# #.net #linq #csv

#c# #.net #linq #csv

Вопрос:

У меня есть два файла CSV. В первом файле у меня есть список пользователей, а во втором файле у меня есть список повторяющихся пользователей. Я пытаюсь удалить строки из первого файла, которые равны второму файлу.

Вот код, который у меня есть до сих пор:

 StreamWriter sw = new StreamWriter(path3);
        StreamReader sr = new StreamReader(path2);

        string[] lines = File.ReadAllLines(path);

        foreach (string line in lines)
        {
            string user = sr.ReadLine();

            if (line != user) 
            {
                sw.WriteLine(line);

            }
  

Пример файла 1:

 Modify,ABAMA3C,Allpay - Free State - HO,09072701

Modify,ABCG327,Processing Centre,09085980
  

Пример файла 2:

 Modify,ABAA323,Group HR Credit Risk amp; Finance

Modify,ABAB959,Channel Sales amp; Service,09071036
  

Есть предложения?

Спасибо.

Комментарии:

1. Пожалуйста, укажите пример формата файла в вашем сообщении…

2. Ваш код выглядит неполным, вам нужно закрыть цикл foreach и закрыть StreamWriter / Reader

3. Похоже, тоже домашнее задание. Помечено как so, не стесняйтесь изменять, если вы возражаете против этого.

4. Почему бы вам не разделить оба файла CSV на список<String>, где каждый элемент представляет собой строку из вашего файла csv, а затем выполнить предварительный поиск в первом списке csv и проверить, содержится ли строка во втором списке? Что-то вроде: foreach(элемент строки в списке){ if(ListTwo.Contains(элемент)){ //удалить строку из списка здесь}} В конце просто перезапишите свой файл оставшимися результатами.

5. где дублирование, вы имеете в виду дублирование на токен или на строку?

Ответ №1:

Все, что вам нужно сделать, это изменить следующие пути к файлам в приведенном ниже коде, и вы получите файл обратно (file one) без дубликатов пользователей из файла 2. Этот код был написан с учетом идеи, что вам нужно что-то, что легко понять. Конечно, есть другие, более элегантные решения, но я хотел сделать это как можно более простым для вас:

(Вставьте это в основной метод вашей программы)

         string line;
        StreamReader sr = new StreamReader(@"C:UsersJDesktoptextsFirst.txt");

        StreamReader sr2 = new StreamReader(@"C:UsersJDesktoptextsSecond.txt");

        List<String> fileOne = new List<string>();
        List<String> fileTwo = new List<string>();

        while (sr.Peek() >= 0)
        {
            line = sr.ReadLine();
            if(line != "")
            {
                fileOne.Add(line);
            }
        }
        sr.Close();
        while (sr2.Peek() >= 0)
        {
            line = sr2.ReadLine();
            if (line != "")
            {
                fileTwo.Add(line);
            }
        }
        sr2.Close();
        var t = fileOne.Except(fileTwo);

        StreamWriter sw = new StreamWriter(@"C:UsersjustinDesktoptextsFirst.txt");

        foreach(var z in t)
        {
            sw.WriteLine(z);
        }
        sw.Flush();
  

Ответ №2:

Если это не домашнее задание, а производственная задача, и вы можете устанавливать сборки, вы сэкономите 3 часа своей жизни, если проглотите свою гордость и воспользуетесь частью библиотеки VB:

Существует множество исключений (CR / LF между запятыми = допустимо в кавычках; различные типы кавычек; и т.д.) Это будет обрабатывать все, что Excel будет экспортировать / импортировать.

Пример кода для загрузки класса ‘Person’, извлеченного из программы, в которой я его использовал:

     Using Reader As New Microsoft.VisualBasic.FileIO.TextFieldParser(CSVPath)

        Reader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
        Reader.Delimiters = New String() {","}
        Reader.TrimWhiteSpace = True
        Reader.HasFieldsEnclosedInQuotes = True

        While Not Reader.EndOfData
            Try
                Dim st2 As New List(Of String)
                st2.addrange(Reader.ReadFields())
                If iCount > 0 Then ' ignore first row = field names
                    Dim p As New Person
                    p.CSVLine = st2
                    p.FirstName = st2(1).Trim
                    If st2.Count > 2 Then
                        p.MiddleName = st2(2).Trim
                    Else
                        p.MiddleName = ""
                    End If
                    p.LastNameSuffix = st2(0).Trim
                    If st2.Count >= 5 Then
                        p.TestCase = st2(5).Trim
                    End If
                    If st2(3) > "" Then
                        p.AccountNumbersFromCase.Add(st2(3))
                    End If
                    While p.CSVLine.Count < 15
                        p.CSVLine.Add("")
                    End While
                    cases.Add(p)
                End If
            Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
                MsgBox("Line " amp; ex.Message amp; " is not valid and will be skipped.")
            End Try
            iCount  = 1
        End While
    End Using
  

Ответ №3:

это для правильного закрытия потоков:

 using(var sw = new StreamWriter(path3))
using(var sr = new StreamReader(path2))
{
    string[] lines = File.ReadAllLines(path);

    foreach (string line in lines)
    {
        string user = sr.ReadLine();

        if (line != user)
        {
            sw.WriteLine(line);
        }
    }
}
  

для получения справки о реальной логике удаления или сравнения, ответьте на комментарий El Ronnoco выше…

Ответ №4:

Вам нужно закрыть потоки или использовать предложение using

 sw.Close();

using(StreamWriter sw = new StreamWriter(@"c:test3.txt"))
  

Ответ №5:

Вы можете использовать LINQ…

 class Program
{
    static void Main(string[] args)
    {
        var fullList = "TextFile1.txt".ReadAsLines();
        var removeThese = "TextFile2.txt".ReadAsLines();

        //Change this line if you need to change the filter results.
        //Note: this assume you are wanting to remove results from the first 
        //      list when the entire record matches.  If you want to match on 
        //      only part of the list you will need to split/parse the records 
        //      and then filter your results.
        var cleanedList = fullList.Except(removeThese);

        cleanedList.WriteAsLinesTo("result.txt");
    }
}
public static class Tools
{
    public static IEnumerable<string> ReadAsLines(this string filename)
    {
        using (var reader = new StreamReader(filename))
            while (!reader.EndOfStream)
                yield return reader.ReadLine();
    }

    public static void WriteAsLinesTo(this IEnumerable<string> lines, string filename)
    {
        using (var writer = new StreamWriter(filename) { AutoFlush = true, })
            foreach (var line in lines)
                writer.WriteLine(line);
    }
}
  

Ответ №6:

 using(var sw = new StreamWriter(path3))
using(var sr = new StreamReader(path))
{
    string []arrRemove = File.ReadAllLines(path2);
    HashSet<string> listRemove = new HashSet<string>(arrRemove.Count);
    foreach(string s in arrRemove)
    {
        string []sa = s.Split(',');
        if( sa.Count < 2 ) continue;
        listRemove.Add(sa[1].toUpperCase());
    }

    string line = sr.ReadLine();
    while( line != null )
    {
        string []sa = line.Split(',');
        if( sa.Count < 2 )
            sw.WriteLine(line);
        else if( !listRemove.contains(sa[1].toUpperCase()) )
            sw.WriteLine(line);
        line = sr.ReadLine();
    }
}