Remove a specific line in text file with c# - c#

I'm building an app for windows 8 desktop, I'm reading in a text file and I want to change one specific line but not sure how so what I have is a text file that says
username|false
username|false
username|false
And I want to remove the middle line when something happens, this is what I have so far;
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile storageFile = await folder.GetFileAsync("students.txt");
var text = await Windows.Storage.FileIO.ReadLinesAsync(storageFile);
var list_false = "";
foreach (var line in text)
{
string name = "" + line.Split('|')[0];
string testTaken = "" + line.Split('|')[1];
if (your_name.Text == name)
{
if (testTaken == "false") {
pageTitle.Text = name;
enter_name_grid.Opacity = 0;
questions_grid.Opacity = 1;
var md = new MessageDialog("Enjoy the test");
await md.ShowAsync();
}
else
{
the_name.Text = "You have already taken the test";
var md1 = new MessageDialog("You have already taken the test");
await md1.ShowAsync();
}
return;
}
else
{
list_false = "You're not on the list";
}
}
if (list_false == "You're not on the list") {
var md2 = new MessageDialog("You're not on the list");
await md2.ShowAsync();
}
Help please, it reads in names perfectly and allows them to take the test, I just need it to remove the correct line. Thanks in advance!!

The important thing to consider is that you are modifying a file. So whatever you choose to change then you need to write it back to the file.
In your case you are opting to read the whole file into memory, this actually works in your favor for something like this as you can just remove any unwanted lines and write back to the file. However, you cannot remove an item while you are iterating through the list using a foreach loop.
The best practice for removing items from an array you are looping is to use a for loop and loop in reverse. It also makes it easier to remove items if we work with a List<string> too, like so:
var list = new List<string>(text);
for(int i = text.Length - 1; i >=0; i--)
{
string line = text[i];
//rest of code
}
text = list.ToArray();
The next part of your task is to remove the line. You can do this in your else statement as this is the part that handles users already having taken the test. For example:
the_name.Text = "You have already taken the test";
list.RemoveAt(i);
Finally, after your loop you need to write the whole thing back to the file:
await Windows.Storage.FileIO.WriteLinesAsync(storageFile, text);

When you read the file, you could store the contents in a list. When your "something happens" you could remove the content at the appropriate index and save (overwrite) the list to the file.

Related

MessageBox doesn't show Folder path when two keywords are searched, but it shows the folder path when one keyword is searched

try
{
string[] SetupFolderKeywords = {"Setup", "Installed"};
DirectoryInfo SearchedDirectory = new DirectoryInfo(Game.SelectedPath);
FileSystemInfo[] filesAndDirs = SearchedDirectory.GetFileSystemInfos($"*{SetupFolderKeywords[0]}*|*{SetupFolderKeywords[1]}*"); // <-- This doesn't work
// FileSystemInfo[] filesAndDirs = SearchedDirectory.GetFileSystemInfos("*" + SetupFolderKeywords[0] + "*"); <-- This Works
foreach (FileSystemInfo foundFile in filesAndDirs)
{
string FullName = foundFile.FullName;
MessageBox.Show(FullName);
}
}
catch (IOException ExpMoveFolder)
{
MessageBox.Show(Convert.ToString(ExpMoveFolder));
}
I'm trying to look for a folder that has either the keyword "Setup" or "Installed" inside the Game.SelectedPath directory. (I used a FolderBrowserDialog to select this folder) and make a MessageBox appear with its path.
When I try to search for a folder that matches one keyword, the MessageBox appears with the path of the folder. It works great, but when I try to search for keyword "Setup" or "Installed" MessageBox doesn't show at all.
No error messages or warnings appear in visual studio and no program exception occurs when I try to look for either one of the keywords instead of just one keyword.
You can't search for multiple patterns with a single call. Your attempt at a Boolean expression is just interpreted as a single pattern and, of course, there are no entries that match that pattern. If you want to match multiple patterns then you have to make multiple calls. One option might be like this:
var folder = new DirectoryInfo(Game.SelectedPath);
var entries = folder.EnumerateFileSystemInfos(patterns[0]);
for (var i = 1; i < patterns.Length; i++)
{
entries = entries.Concat(folder.EnumerateFileSystemInfos(patterns[i]));
}
foreach (var entry in entries)
{
// Use entry here.
}
EDIT:
I just created this folder:
I then executed this code:
var patterns = new[] { "123", "789" };
var folder = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test"));
var entries = folder.EnumerateFileSystemInfos($"*{patterns[0]}*");
for (var i = 1; i < patterns.Length; i++)
{
entries = entries.Concat(folder.EnumerateFileSystemInfos($"*{patterns[i]}*"));
}
foreach (var entry in entries)
{
Console.WriteLine(entry.Name);
}
That's basically exactly what I posted above except I added wildcards to the EnumerateFileSystemInfos calls where the original code would have required them to be in strings already. This is the output I got:
File123.txt
Folder123
File789.txt
Folder789
I then changed the filters to this:
var patterns = new[] { "456" };
and ran the code again and got this output:
File456.txt
Folder456
Clearly, the code works exactly as it is supposed to and, if what you did didn't work then you did it wrong. If you can't work out what you did wrong, I suggest that you update your question and add the new relevant information.

C# - Using File.Exists, when file DOESN'T exist does not run the last else statement

Super new to C#. I'm having an input get split and then find an ID from the pointsTarget var.
When the file DOES exist, it seems that the line
else if (File.Exists(filePath+userId+txt))
returns true;
because it runs just fine and sets the argument "addPointsCompleted" to TRUE. It works just how I would expect. But when the file does NOT exist, I want it to return false and run the last else statement:
CPH.SetArgument("missing", "True");
and set "missing" to TRUE.
I feel like there is something wrong with the way I put in the if/else if/else statement because I get an error :
"System.IO.FileNotFoundException: Could not find file 'E:\Users\Troy\Documents\Stuff\PointNames\Test.txt'.
using System;
using System.IO;
public class CPHInline
{
public bool Execute()
{
string rawInput = args["rawInput"].ToString();
string[] split= rawInput.Split('+');
var pointsTarget = split[0].ToString();
var addPoints = split[1].ToString();
CPH.LogInfo($"pointsTarget is {pointsTarget}");
CPH.LogInfo($"addPoints is {addPoints}");
var user = args["user"].ToString();
CPH.SetArgument("pointsTarget", pointsTarget);
string userPath = #"E:/Users/Troy/Documents/Stuff/PointNames/";
string filePath = #"E:/Users/Troy/Documents/Stuff/PointIDs/";
string txt = ".txt";
var userId = File.ReadAllText(userPath+pointsTarget+txt);
CPH.LogInfo($"userId is {userId}");
if (user == pointsTarget)
{
CPH.SetArgument("corrupt", "True");
}
else if (File.Exists(filePath+userId+txt))
{
//DO THIS
string fileName = filePath+userId+txt;
string points = File.ReadAllText(fileName);
int x = Convert.ToInt32(points);
int y = Convert.ToInt32(addPoints);
int sum = x + y;
String newPoints;
newPoints = sum.ToString();
File.WriteAllText(fileName, newPoints);
CPH.SetArgument("newPoints", newPoints);
CPH.SetArgument("addPointsCompleted", "True");
}
else
{
//do this
CPH.SetArgument("missing", "True");
}
return true;
}
}
I tried looking around, but all the issues are from people where the file DOES exist and they can't find it. My problem is kind of the opposite.
I feel like there is something wrong with the way I put in the if/else if/else statement because I get an error "System.IO.FileNotFoundException: Could not find file 'E:\Users\Troy\Documents\Stuff\PointNames\Test.txt'.
This is a good opportunity for you to start familiarizing yourself with using a debugger to step through the code and observe its behavior. Because the problem has nothing to do with your if structure. It's happening before your if block. Right here:
var userId = File.ReadAllText(userPath+pointsTarget+txt);
Look at the error message. It's trying to read a file in the "PointNames" folder. Which is in your userPath variable:
string userPath = #"E:/Users/Troy/Documents/Stuff/PointNames/";
Which is only ever used in that one line of code that tries to read a file. And File.ReadAllText will throw a FileNotFoundException if the file is not found.
It seems you're already aware of how to check if a file exists. So why not apply that here? For example:
var userId = string.Empty;
if (File.Exists(userPath+pointsTarget+txt))
{
userId = File.ReadAllText(userPath+pointsTarget+txt);
}
else
{
// handle the error in some way
}

unable to read text file using stream reader and place txt into custom class

so i am trying to read text from a txt file and then add the text in to a custom class list,
the code is
public static List<BookInfo> LoadCSVFile(string fileName, out string qError)
{
qError = "";
fileName = "books.txt";
List<BookInfo> Book_Info = new List<BookInfo>();
StreamReader read = null;
try
{
read = new StreamReader(fileName);
while (!read.EndOfStream)
{
string line = read.ReadLine();
string[] values = line.Split(',');
if (values.Length == 3)
{
string Title = values[0].Trim();
string Author = values[1].Trim();
string ISBN = values[2].Trim();
try
{
Book_Info.Add(new BookInfo(Title, Author, ISBN));
}
catch (Exception ex)
{
qError = ex.Message;
return null;
}
}
else
{
qError = $"line {line} was unable to be read";
return null;
}
}
}
catch
{
qError = $"failed to open file: {fileName}";
return null;
}
finally
{
if (read != null)
{
read.Close();
}
}
if (qError == "")
{
return Book_Info;
}
return null;
}
once i have read the text it will be displayed in a form which i believe to be coded correctly
i have placed an error message in to show when the file has been read and each time i try something new the same error appears.
have i gone wrong somewhere when reading the txt file?
Edit:
Text file was created using visual studio and is in the same solution, text file is in bin/debug
I totally agree with thegeneral's answer, but to answer your initial question I suspect your books.txt file was not located in your Bin/Debug folder. I did test your code ;-P
Some notes
if you are going to use something that Implements IDisposable, it's always good practice to use the using statement
If this is only a small file, why bother with StreamReader when you can just use File.ReadAllLines
Linq is your friend, projection is a wonderful thing.
If you really want to parse a CSV file, I'd seriously consider a dedicated CSV parser library (like CsvHelper). It will save you many headaches
This is not really the bastion of perfect coding, however I tried to work with what you had and the spirit of what you were trying to do.
Some code:
public static List<BookInfo> LoadCSVFile(string fileName, out string qError)
{
try
{
// read all lines in to another type
// just makes it easier for errors which you seem to want
var lines = File.ReadAllLines(fileName)
.Select(x => new { Values = x.Split(','), Text = x })
.ToList();
// get a list of errors,
var errors = lines.Where(x => x.Values.Length != 3)
.Select((s, i) => $"Bad book! Line {i} : {s.Text}");
// return some errors
qError = string.Join(Environment.NewLine, errors);
// project lines to your books
return lines.Where(x => x.Values.Length == 3)
.Select(x => new BookInfo(x.Values[0], x.Values[0], x.Values[0]))
.ToList();
}
catch (Exception e)
{
qError = e.Message;
}
return null;
}
Disclaimer
I wouldn't usually catch errors like this, it's rather nasty
The whole anonymous type and returning errors is a bit smelly, if the file is corrupt, I'd just throw a big nasty exception and be done with it. Make the user do the right thing
This is going to fail the second you have a title with a comma in it
This is completely untested!

Remove text from PDF document using Aspose.PDF library?

I need to delete a text from a PDF document. I am using Aspose for the purpose
am currently using TextFragmentAbsorber.
FYI, I cannot use any other 3rd party library.
Below is the code I am using :
private string DeleteMachineReadableCode(string inputFilePath)
{
var outputFilePath = Path.Combine(Path.GetTempPath(), string.Format(#"{0}.pdf", Guid.NewGuid()));
try
{
// Open document
Document pdfDocument = new Document(inputFilePath);
// Create TextAbsorber object to find all the phrases matching the regular expression
TextFragmentAbsorber textFragmentAbsorber = new TextFragmentAbsorber("#START#((.|\r\n)*?)#END#");
// Set text search option to specify regular expression usage
TextSearchOptions textSearchOptions = new TextSearchOptions(true);
textFragmentAbsorber.TextSearchOptions = textSearchOptions;
// Accept the absorber for all pages
pdfDocument.Pages.Accept(textFragmentAbsorber);
// Get the extracted text fragments
TextFragmentCollection textFragmentCollection = textFragmentAbsorber.TextFragments;
// Loop through the fragments
foreach (TextFragment textFragment in textFragmentCollection)
{
// Update text and other properties
textFragment.Text = string.Empty;
// Set to an instance of an object.
textFragment.TextState.Font = FontRepository.FindFont("Verdana");
textFragment.TextState.FontSize = 1;
textFragment.TextState.ForegroundColor = Aspose.Pdf.Color.FromRgb(System.Drawing.Color.White);
textFragment.TextState.BackgroundColor = Aspose.Pdf.Color.FromRgb(System.Drawing.Color.White);
}
pdfDocument.Save(outputFilePath);
}
finally
{
if (File.Exists(inputFilePath))
File.Delete(inputFilePath);
}
return outputFilePath;
}
I am able to replace the content if the content to be deleted is on a single page.
My problem is that if the text spans over multiple pages the TextFragmentAbsorber does not recognize the text with the mentioned regex pattern ("#START#((.|\r\n)*?)#END#").
Please suggest if anything can be done on the regex or the some setting in Aspose can fix my issue.
As shared earlier, we can not promise earlier resolution of the issue reported by you, because of architecture limitation. However, we have modified the code snippet to meet your requirements.
The idea is to find text starting from '#START#' on the one of the document pages. Then to find text ending with '#END#' on the one of subsequent pages. And also to process all text fragments that placed on the pages between those two pages (if it exists).
private string DeleteMachineReadableCodeUpdated(string inputFilePath)
{
string outputFilePath = Path.Combine(Path.GetTempPath(), string.Format(#"{0}.pdf", Guid.NewGuid()));
try
{
// Open document
Document pdfDocument = new Document(inputFilePath);
// Create TextAbsorber object to find all the phrases matching the regular expression
TextFragmentAbsorber absorber = new TextFragmentAbsorber("#START#((.|\r\n)*?)#END#");
// Set text search option to specify regular expression usage
TextSearchOptions textSearchOptions = new TextSearchOptions(true);
absorber.TextSearchOptions = textSearchOptions;
// Accept the absorber for all pages
pdfDocument.Pages.Accept(absorber);
// Get the extracted text fragments
TextFragmentCollection textFragmentCollection = absorber.TextFragments;
// If pattern found on one of the pages
if (textFragmentCollection.Count > 0)
{
RemoveTextFromFragmentCollection(textFragmentCollection);
}
else
{
// In case nothing was found tries to find by parts
string startingPattern = "#START#((.|\r\n)*?)\\z";
string endingPattern = "\\A((.|\r\n)*?)#END#";
bool isStartingPatternFound = false;
bool isEndingPatternFound = false;
ArrayList fragmentsToRemove = new ArrayList();
foreach (Page page in pdfDocument.Pages)
{
// If ending pattern was already found - do nothing
if (isEndingPatternFound)
continue;
// If starting pattern was already found - activate textFragmentAbsorber with ending pattern
absorber.Phrase = !isStartingPatternFound ? startingPattern : endingPattern;
page.Accept(absorber);
if (absorber.TextFragments.Count > 0)
{
// In case something is found - add it to list
fragmentsToRemove.AddRange(absorber.TextFragments);
if (isStartingPatternFound)
{
// Both starting and ending patterns found - the document processing
isEndingPatternFound = true;
RemoveTextFromFragmentCollection(fragmentsToRemove);
}
else
{
// Only starting pattern found yet - continue
isStartingPatternFound = true;
}
}
else
{
// In case neither starting nor ending pattern are found on current page
// If starting pattern was found previously - get all fragments from the page
if (isStartingPatternFound)
{
absorber.Phrase = String.Empty;
page.Accept(absorber);
fragmentsToRemove.AddRange(absorber.TextFragments);
}
// Otherwise do nothing (continue)
}
}
}
pdfDocument.Save(outputFilePath);
}
finally
{
if (File.Exists(inputFilePath))
File.Delete(inputFilePath);
}
return outputFilePath;
}
private void RemoveTextFromFragmentCollection(ICollection fragmentCollection)
{
// Loop through the fragments
foreach (TextFragment textFragment in fragmentCollection)
{
textFragment.Text = string.Empty;
}
}
Note:
This code assumed that the only one text block starting from '#START#' and ending with '#END#' is in the document. However the above code can be easly modified to process several those blocks.
Instead of processing text on intermediate page(s) you may store page number(s) and than delete using pdfDocument.Pages.Delete(pageNumber) before the saving document. It lets to avoid 'blank' pages if them undesirable.

Async operation showing wrong file

I am trying to add an item to an ObservableCollection while in an aysnc operation. If I run the application, the collection does not have the correct file. If I step through it, I see the correct file does get added, which obviously shows a timing issue. Trouble is I cannot figure out how to fix it. Besides this, everything else works as I expect.
Can someone explain to me what I am doing wrong and how to fix it so the correct filename is written to the ObservableCollection?
private void ChangeFile(INotificationComplete notification)
{
FileInfo currentFileInfo = null;
var destinationImageFilename = string.Empty;
var imageDestinationFolder = Path.Combine(messageBrokerInstance.GetProgramPath("LevelThreeFilesWebLocation", this.SelectedPlatform), "images");
var fileDestinationFolder = Path.Combine(messageBrokerInstance.GetProgramPath("LevelThreeFilesWebLocation", this.SelectedPlatform));
try
{
Task.Factory.StartNew((Action)delegate
{
string[] files = null;
if (directoryInfo.Exists)
{
files = Directory.GetFiles(directoryInfo.FullName, #"*.htm", SearchOption.TopDirectoryOnly);
}
foreach (string file in files)
{
currentFileInfo = new FileInfo(file);
**// bunch of code
// I've found what I want and now am ready to write the file
// and add the filename to the collection the user sees.**
if (writeFile)
{
var fileDestination = Path.Combine(fileDestinationFolder, currentFileInfo.Name);
File.WriteAllLines(webFileDestination, fileArray);
**// Correct file was written but the wrong filename
// is added to the collection.**
// If I step through this, the correct filename is added.
UIDispatcher.Current.BeginInvoke((Action)delegate
{
this.ChangedFiles.Add(currentFileInfo.Name); // ChangedFiles is an ObservableCollection<string>
});
}
}
WaitAnimationNotification offNotification = new WaitAnimationNotification()
{
IsWaitAnimationOn = false,
WaitAnimationMessage = "Please wait while the operation completes..."
};
WaitAnimationNotification waitNotification = notification as WaitAnimationNotification;
if (waitNotification.IsWaitAnimationOn)
{
this.SendMessage("ToggleWaitAnimation", new NotificationEventArgs<WaitAnimationNotification, INotificationComplete>("ToggleWaitAnimation", offNotification));
}
});
}
}
I dont know what UIDispatcher.Current is, but the correct Dispatcher to use is Application.Current.Dispatcher It's easy to spin up a separate dispatcher on a background thread unintentionally - which will give you the behavior you see. Application.Current.Dispatcher is going to be the correct dispatcher for the main application message pump

Categories

Resources