Click here to Skip to main content
15,884,472 members
Articles / Desktop Programming / WPF
Tip/Trick

Text Line Remover - Remove Lines Containing Specific Words

Rate me:
Please Sign up or sign in to vote.
3.30/5 (9 votes)
13 Nov 2013CPOL 26.7K   205   8   12
Removes text lines containing specific words/string from files.

Introduction 

A small application addressing one task: remove lines containing specific words from a bunch of files under given folder.

Background  

Once a while I get a task need to clear something from existing text files which are well formatted.

At first I search the keyword using NodePad++ and then double click and delete the lines one by one manually, but soon I find that there are too many lines to remove and doing it manually is boring and  cannot guarantee correctness.

So I begin the code of this small tool written in WPF.

Using the code

To use the tool, follow steps below:

  1. Set the string/words to search.
  2. Set the folder to detect files.
  3. Set the file pattern which can contain wildcards. E.g., *.txt for all Text files; prefix*.txt for all test files naming start with 'prefix'.
  4. Click 'Analyze' button to preview the lines that will be removed.
  5. And then click 'Remove lines' and it will do the job to clean up the files.

Here is code for SearchAndRemoveViewModel

C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.ViewModel;

namespace LineRemover.ViewModel
{
    class SearchAndRemoveViewModel : NotificationObject, IDataErrorInfo
    {
        private string _searchString;
        private string _searchFolder;
        private string _filePattern;
        private string _status;

        private readonly List<string> _filesToModify = new List<string>();

        public string SearchFolder
        {
            get { return _searchFolder; }
            set
            {
                if (value == _searchFolder) return;
                _searchFolder = value;
                RaisePropertyChanged("SearchFolder");
                RaiseCanExecuteChanged();
            }
        }

        public string SearchString
        {
            get { return _searchString; }
            set
            {
                if (_searchString != value)
                {
                    _searchString = value;
                    RaisePropertyChanged("SearchString");
                    RaiseCanExecuteChanged();
                }
            }
        }

        public string FilePattern
        {
            get { return _filePattern; }
            set
            {
                if (_filePattern != value)
                {
                    _filePattern = value;
                    RaisePropertyChanged("FilePattern");
                }
            }
        }

        public string Status
        {
            get { return _status; }
            set
            {
                if (_status == value) return;
                _status = value;
                RaisePropertyChanged("Status");
            }
        }

        private readonly ObservableCollection<MatchResult> _searchResults = 
                new ObservableCollection<MatchResult>();

        public ObservableCollection<MatchResult> AllResults { get { return _searchResults; } }

        public SearchAndRemoveViewModel()
        {
            SearchString = "Results";
            SearchFolder = @"t:\components";
        }

        private void RaiseCanExecuteChanged()
        {
            AnalyzeCommand.RaiseCanExecuteChanged();
            ApplyCommand.RaiseCanExecuteChanged();
        }

        private DelegateCommand _analyzeCommand;
        public DelegateCommand AnalyzeCommand
        {
            get
            {
                return _analyzeCommand ?? (_analyzeCommand = 
                          new DelegateCommand(Analyze, CanExecuteAnalyze));
            }
        }

        private DelegateCommand _applyCommand;

        public DelegateCommand ApplyCommand
        {
            get { return _applyCommand ?? (_applyCommand = new DelegateCommand(Apply, CanAppy)); }
        }

        private bool CanExecuteAnalyze()
        {
            return this["SearchString"] == null && 
                        this["SearchFolder"] == null;
        }

        private bool CanAppy()
        {
            return this._filesToModify.Any();
        }

        private void Analyze()
        {
            _filesToModify.Clear();

            try
            {
                var allResults = new List<MatchResult>();
                var files = Directory.GetFiles(SearchFolder, FilePattern ?? "*");
                foreach (var file in files)
                {
                    var results = AnalyzeOneFile(file);
                    if (results.Any())
                    {
                        _filesToModify.Add(file);
                        allResults.AddRange(results);
                    }
                }

                AllResults.Clear();
                allResults.ForEach(r=>AllResults.Add(r));
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString(), "Error");
            }

            RaisePropertyChanged("AllResults");

            ApplyCommand.RaiseCanExecuteChanged();
        }

        private void Apply()
        {
            try
            {
                foreach (var file in _filesToModify)
                {
                    Apply(file);
                }

                Status = string.Format("updated {0} files.", _filesToModify.Count);

                _filesToModify.Clear();
                AllResults.Clear();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString(), "Error");
            }
        }

        private void Apply(string file)
        {
            Status = "applying to " + file;

            var lines = File.ReadAllLines(file);
            bool noChange = true;
            var updated = new List<string>();
            foreach (var line in lines)
            {
                if (line != null && line.IndexOf(SearchString, 
                          StringComparison.InvariantCultureIgnoreCase) < 0)
                {
                    updated.Add(line);
                }
                else
                {
                    noChange = false;
                }
            }

            if (!noChange)
            {
                using (var stream = File.OpenWrite(file))
                {
                    using (var writer = new StreamWriter(stream))
                    {
                        writer.BaseStream.Seek(0, SeekOrigin.Begin);
                        writer.BaseStream.SetLength(writer.Encoding.GetPreamble().Length);

                        foreach (var line in updated)
                        {
                            writer.WriteLine(line);
                        }

                        writer.Flush();
                    }
                }
            }

            Status = "applied to " + file;
        }

        private IList<MatchResult> AnalyzeOneFile(string file)
        {
            var results = new List<MatchResult>();

            var lines = File.ReadAllLines(file);
            int lineNo = 0;
            foreach (string line in lines)
            {
                if (line != null && line.IndexOf(SearchString, 
                          StringComparison.InvariantCultureIgnoreCase) >= 0)
                {
                    var result = new MatchResult { LineNo = lineNo, LineContent = line, FileName = file };
                    results.Add(result);
                }
                ++lineNo;
            }

            return results;
        }

        public string this[string columnName]
        {
            get
            {
                if (columnName == "SearchString")
                {
                    if (String.IsNullOrEmpty(SearchString))
                    {
                        return "search string is required.";
                    }
                }

                if (columnName == "SearchFolder")
                {
                    if (String.IsNullOrEmpty(SearchFolder))
                    {
                        return "search folder is required.";
                    }
                }

                return null;
            }
        }

        public string Error { get; private set; }
    }
}

Points of Interest 

As an engineer surely we can figure out ways to automate manual work.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
China China
An ireful software engineer since 2007
- naming is still the most hard/challenging thing for programming

Lean code ~ better life

I hold experience with C#/.NET/WPF and C++ while I am currently learning Android development and Python.

Comments and Discussions

 
Questionok Pin
BillW3313-Nov-13 6:02
professionalBillW3313-Nov-13 6:02 
AnswerRe: ok Pin
Ireful13-Nov-13 19:56
Ireful13-Nov-13 19:56 
Suggestionuseful code Pin
Brian A Stephens13-Nov-13 3:46
professionalBrian A Stephens13-Nov-13 3:46 
GeneralRe: useful code Pin
Ireful13-Nov-13 19:51
Ireful13-Nov-13 19:51 
GeneralMy vote of 2 Pin
Marco Bertschi13-Nov-13 3:35
protectorMarco Bertschi13-Nov-13 3:35 
Question"sed '/whatever/d' fn" or ":g/whatever/d" from within vim. Pin
Ivor O'Connor12-Nov-13 7:32
Ivor O'Connor12-Nov-13 7:32 
AnswerRe: "sed '/whatever/d' fn" or ":g/whatever/d" from within vim. Pin
Ireful12-Nov-13 20:44
Ireful12-Nov-13 20:44 
SuggestionYou shouldn't need to pull the whole file into memory to process it Pin
John Brett12-Nov-13 2:21
John Brett12-Nov-13 2:21 
GeneralRe: You shouldn't need to pull the whole file into memory to process it Pin
Ireful12-Nov-13 20:29
Ireful12-Nov-13 20:29 
QuestionMissing download file Pin
Smitha Nishant12-Nov-13 1:40
protectorSmitha Nishant12-Nov-13 1:40 
Hello author

The download file is missing. Could you please upload a copy?

Thanks
Smitha Vijayan
The Code Project
Are you an aspiring author? Read how to submit articles to CodeProject: Article Submission Guidelines[^]

Questions? Ask an editor here...

AnswerRe: Missing download file Pin
Ireful12-Nov-13 20:21
Ireful12-Nov-13 20:21 
AnswerRe: Missing download file Pin
Thanks787212-Nov-13 20:59
professionalThanks787212-Nov-13 20:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.