C# file line remove with arraylist - c#

I am trying to make a class which will help me delete one specific line from a file. So I came up with the idea to put all lines in an arraylist, remove from the list the line i don't need, wipe clear my .txt file and write back the remaining objects of the list. My problem is that I encounter some sort of logical error i can't fint, that doesn't remove the line from the arraylist and writes it back again. Here's my code:
public class delete
{
public void removeline(string line_to_delete)
{
string[] lines = File.ReadAllLines("database.txt");
ArrayList list = new ArrayList(lines);
list.Remove(line_to_delete);
File.WriteAllText("database.txt", String.Empty);
using (var writer = new StreamWriter("database.txt"))
{
foreach (object k in lines)
{
writer.WriteLine(k);
}
}
}
}
What is that I am missing? I tried lots of things on removing a line from a text file that did not work. I tried this because it has the least file operations so far.
Thanks!

You can do:
var line_to_delete = "line to delete";
var lines = File.ReadAllLines("database.txt");
File.WriteAllLines("database.txt", lines.Where(line => line != line_to_delete));
File.WriteAllLines will overwrite the existing file.
Do not use ArrayList, there is a generic alternative List<T>. Your code is failing due to the use of ArrayList as it can only remove a single line matching the criteria. With List<T> you can use RemoveAll to remove all the lines matching criteria.
If you want to do the comparison with ignore case you can do:
File.WriteAllLines("database.txt", lines.Where(line =>
!String.Equals(line, line_to_delete, StringComparison.InvariantCultureIgnoreCase)));

I believe you're intending:
public static void RemoveLine(string line)
{
var document = File.ReadAllLines("...");
document.Remove(line);
document.WriteAllLines("..");
}
That would physically remove the line from the collection. Another approach would be to simply filter out that line with Linq:
var filter = document.Where(l => String.Compare(l, line, true) == 0);
You could do the Remove in an ArrayList proper documentation on how is here. Your code should actually work, all of these answers are more semantic oriented. The issue could be due to actual data, could you provide a sample? I can't reproduce with your code.

Related

Simplify double foreach instruction

I need to browse a word document and to retrieve some Text Boxes in order to modify them.
But I need to count them before, and I do think that what I wrote is really inefficient.
I'd like to know if it's possible to simplify the following:
foreach (Microsoft.Office.Interop.Word.HeaderFooter OHeader in documentOld.Sections[1].Headers)
{
foreach (Microsoft.Office.Interop.Word.Shape shape in OHeader.Shapes)
{
if (shape.Name.Contains("Text Box"))
{
listTextBox.Add(new KeyValuePair<string, string>(shape.Name.ToString(), shape.TextFrame.TextRange.Text.ToString()));
}
}
}
int count = listTextBox.Count();
I want to know how many elements which contain "Text Box" are in the Shapes.
I see two ways you can do this.
Using LINQ syntax:
var count = (
from OHeader in documentOld.Sections[1].Headers
from shape in OHeader.Shapes
where shape.Name.Contains("Text Box")).Count();
Or, using IEnumerable extension methods:
var count = documentOld.Sections[1].Headers
.SelectMany(h => h.Shapes)
.Count(s => s.Name.Contains("Text Box"));
Note that your version is inefficient in that it creates a list and the KeyValuePairs needlessly, given that you only want to count the number of shapes that match some condition. Other that that, nested foreach blocks are fine for performance, but may lack in readability versus the LINQ equivalents.
Also, please note that I have not tested the code above.
Keeping your code the same by using the foreach loops still all you need to do is have your count variable before the loops and increment it each time you find a match.
int count = 0;
foreach (Microsoft.Office.Interop.Word.HeaderFooter OHeader in documentOld.Sections[1].Headers)
{
foreach (Microsoft.Office.Interop.Word.Shape shape in OHeader.Shapes)
{
if (shape.Name.Contains("Text Box"))
{
++count;
}
}
}

Creation and recalling of objects in a foreach loop?

Beginner programmer here so please keep (explanation of) answers as simple as possible.
For an assignment we got a text file that contains a large amount of lines.
Each line is a different 'Order' with an ordernumber, location, frequency (amount of times per week), and a few other arguments.
Example:
18; New York; 3PWK; ***; ***; etc
I've made it so that each line is read and stored in a string with
string[] orders = System.IO.File.ReadAllLines(#<filepath here>);
And I've made a separate class called "Order" which has a get+set for all the properties like ordernumber, etc.
Now I'm stuck on how to actually get the values in there. I know how string splitting works but not how I can make unique objects in a loop.
I'd need something that created a new Order for every line and then assign Order.ordernumber, Order.location etc.
Any help would be MUCH appreciated!
An easy approach will be to make a class to define the orders like this:
public class Order{
public string OrderNumber{get;set;}
public string OrderId{get;set;}
public string OrderSomeThingElse{get;set;}
}
Then initialize a List:
var orderList = new List<Order>();
Then loop through and populate it:
foreach( var order in orders ){
var splitString = order.Split(';');
orderList.Add( new Order{
OrderNumber = splitString[0],
OrderId = splitString[1],
OrderSomeThingElse = splitString[2]
});
}
If you want an easy, but not that elegant approach, this is it.
In addition to all the good answers you've already received. I recommend you to use File.ReadLines() instead File.ReadAllLines() Because you are reading large file.
The ReadLines and ReadAllLines methods differ as follows: When you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned; when you use ReadAllLines, you must wait for the whole array of strings be returned before you can access the array. Therefore, when you are working with very large files, ReadLines can be more efficient. MSDN
Unless I misunderstand... do you mean something like this?
var ordersCollection = new List<Order>();
foreach (var order in orders)
{
var o = new Order();
o.PropertyName = ""; // Assign your property values like this
ordersCollection.Add(o);
}
// ordersCollection is now full of your orders.

Select row from List<string> where matches

I have the following List<string>
List<string> RFD = new List<string>(File.ReadAllLines("FDIASNC"));
the file it is reading has this:
000821031300000000080
001921031300000000080
004221031300000000080
008121031300000000080
009321031300000000080
011221031300000000080
012221031300000000080
0128200313010000330790000033
207721031300000000080
So what I want is to find a match and select this line, for example if I want to read the line for "0128" it will be RFD[7] manually, but I want to search that line and output the line for manipulation... Any idea?
Thank you.
EDIT
I found it once I posted, I'm a dumb hehe... thank you for your answers, in 10 min I will select one as the correct :)
You can use LINQ to search:
var line = File.ReadAllLines("FDIASNC")
.FirstOrDefault(line => line.StartWith("0128"));
if (line != null)
{
//Do something with this line
}
You don't need to create a List<string> since ReadAllLines returns IEnumerable<string>
If your file is large, you might think to use deferred execution by ReadLines instead of loading all lines into memory
RFD.Where(r=>r.Contains("0128")).FirstOrDefault()
If you want to select exactly one line, that matches you criteria, use First or Single LINQ extension method. For example:
string str = RFD.First(line => line.StartWith("0128"));
returns 0128200313010000330790000033 string
Something like this?
using System.Linq;
// later in your program...
foreach (string match in RFD.Where(l => l.StartsWith("0128"))) {
Console.WriteLine(match);
}
try this:
string line = RFD.FirstOrDefault(line => line.StartsWith("0128"));
If there's no line with 0128 at the beginning then line is null

Retrieving objects from ArrayList - cast to type?

I'm taking a basic course in C# programming, and this is a part of our assignment.
Very new to programming so I feel more than a bit lost with this.
The assignment is to add an ArrayList and insert strings from a file to this, which I hopefully have done with the code below:
Read () is a method in another class (FileReader) that reads the files from "info.txt" and returns an ArrayList.
The ArrayList items are supposed to store the object items, although I'm not quite sure why I need two arrays?
The problem I have is: when you retrieve the "items" from the array, they have to be cast to a type, string, (if I understand it correctly, otherwise they are returned as objects?). How do I do that?
Can you cast the entire ArrayList?
public PriceFlux () //Constructor
{
ArrayList items;
items = new ArrayList();
FileReader infoFile = new FileReader("info.txt");
items = infoFile.Read();
}
The files from the info.txt looks approximately like this:
G&34&Kellogs K frukostflingor&Sverige&29.50&5/11/2005&29/10/2005&29/10/2006
Here is the FileReader Read() method:
public ArrayList Read ()
{
ArrayList fileContent = new ArrayList ();
try
{
while (line != null)
{
fileContent.Add (line);
line = reader.ReadLine ();
}
reader.Close ();
}
catch
{
Console.WriteLine ("CouldnĀ“t read from file.");
}
return fileContent;
}
Very grateful for suggestions on how to solve this.
You can use linq to do this easily:
This will cast all items to string and return an IEnumerable<string>. It will fail if any items can't be cast to a string:
items.Cast<string>();
This will cast all items that can be to a string and skip over any that can't:
items.OfType<string>();
You can access single elements in the ArrayList doing a cast, for example...
string s = myArrayList[100] as string;
myArrayList.Remove("hello");
myArrayList[100] = "ciao"; // you don't need a cast here.
You can also iterate through all elements without a cast...
foreach (string s in myArrayList)
Console.WriteLine(s);
You can also use CopyTo method to copy all items in a string array...
string[] strings = new string[myArrayList.Count];
myArrayList.CopyTo(strings);
You can create another List<string> with all items in the ArrayList.
Since ArrayList implements IEnumerable you can call the List<string> constructor.
List<string> mylist = new List<string>(myArrayList);
But this doesn't makes much sense... why you don't just use List<string> directly?
Using directly a List<string> seems more useful to me, and is faster.
ArrayList still exists mostly for compatibility purposes since generics were introduced in version 2 of the language.
I just noticed that there may be an error in your code:
while (line != null)
{
fileContent.Add (line);
line = reader.ReadLine ();
}
Should be instead
for (;;)
{
string line = reader.ReadLine();
if (line == null)
break;
fileContent.Add(line);
}
You cast each element on its own before using it.

How to add items to a collection while consuming it?

The example below throws an InvalidOperationException, "Collection was modified; enumeration operation may not execute." when executing the code.
var urls = new List<string>();
urls.Add("http://www.google.com");
foreach (string url in urls)
{
// Get all links from the url
List<string> newUrls = GetLinks(url);
urls.AddRange(newUrls); // <-- This is really the problematic row, adding values to the collection I'm looping
}
How can I rewrite this in a better way? I'm guessing a recursive solution?
You can't, basically. What you really want here is a queue:
var urls = new Queue<string>();
urls.Enqueue("http://www.google.com");
while(urls.Count != 0)
{
String url = url.Dequeue();
// Get all links from the url
List<string> newUrls = GetLinks(url);
foreach (string newUrl in newUrls)
{
queue.Enqueue(newUrl);
}
}
It's slightly ugly due to there not being an AddRange method in Queue<T> but I think it's basically what you want.
There are three strategies you can use.
Copy the List<> to a second collection (list or array - perhaps use ToArray()). Loop through that second collection, adding urls to the first.
Create a second List<>, and loop through your urls List<> adding new values to the second list. Copy those to the original list when done looping.
Use a for loop instead of a foreach loop. Grab your count up front. List should leave things indexed correctly, so it you add things they will go to the end of the list.
I prefer #3 as it doesn't have any of the overhead associated with #1 or #2. Here is an example:
var urls = new List<string>();
urls.Add("http://www.google.com");
int count = urls.Count;
for (int index = 0; index < count; index++)
{
// Get all links from the url
List<string> newUrls = GetLinks(urls[index]);
urls.AddRange(newUrls);
}
Edit: The last example (#3) assumes that you don't want to process additional URLs as they are found in the loop. If you do want to process additional URLs as they are found, just use urls.Count in the for loop instead of the local count variable as mentioned by configurator in the comments for this answer.
Use foreach with a lambda, it's more fun!
var urls = new List<string>();
var destUrls = new List<string>();
urls.Add("http://www.google.com");
urls.ForEach(i => destUrls.Add(GetLinks(i)));
urls.AddRange(destUrls);
alternately, you could treat the collection as a queue
IList<string> urls = new List<string>();
urls.Add("http://www.google.com");
while (urls.Count > 0)
{
string url = urls[0];
urls.RemoveAt(0);
// Get all links from the url
List<string> newUrls = GetLinks(url);
urls.AddRange(newUrls);
}
I would create two lists add into the second and then update the reference like this:
var urls = new List<string>();
var destUrls = new List<string>(urls);
urls.Add("http://www.google.com");
foreach (string url in urls)
{
// Get all links from the url
List<string> newUrls = GetLinks(url);
destUrls.AddRange(newUrls);
}
urls = destUrls;
Consider using a Queue with while loop (while q.Count > 0, url = q.Dequeue()) instead of iteration.
I assume you want to iterate over the whole list, and each item you add to it? If so I would suggest recursion:
var urls = new List<string>();
var turls = new List<string();
turls.Add("http://www.google.com")
iterate(turls);
function iterate(List<string> u)
{
foreach(string url in u)
{
List<string> newUrls = GetLinks(url);
urls.AddRange(newUrls);
iterate(newUrls);
}
}
You can probably also create a recursive function, like this (untested):
IEnumerable<string> GetUrl(string url)
{
foreach(string u in GetUrl(url))
yield return u;
foreach(string ret_url in WHERE_I_GET_MY_URLS)
yield return ret_url;
}
List<string> MyEnumerateFunction()
{
return new List<string>(GetUrl("http://www.google.com"));
}
In this case, you will not have to create two lists, since GetUrl does all the work.
But I may have missed the point of you program.
Don't change the collection you're looping through via for each. Just use a while loop on the Count property of the list and access the List items by index. This way, even if you add items, the iteration should pick up the changes.
Edit: Then again, it sort of depends on whether you WANT the new items you added to be picked up by the loop. If not, then this won't help.
Edit 2: I guess the easiest way to do it would be to just change your loop to:
foreach (string url in urls.ToArray())
This will create an Array copy of your list, and it will loop through this instead of the original list. This will have the effect of not looping over your added items.
Jon's approach is right; a queue's the right data structure for this kind of application.
Assuming that you'd eventually like your program to terminate, I'd suggest two other things:
don't use string for your URLs, use System.Web.Uri: it provides a canonical string representation of the URL. This will be useful for the second suggestion, which is...
put the canonical string representation of each URL you process in a Dictionary. Before you enqueue a URL, check to see if it's in the Dictionary first.
It's hard to make the code better without knowing what GetLinks() does. In any event, this avoids recursion. The standard idiom is you don't alter a collection when you're enumerating over it. While the runtime could have let you do it, the reasoning is that it's a source of error, so better to create a new collection or control the iteration yourself.
create a queue with all urls.
when dequeueing, we're pretty much saying we've processed it, so add it to result.
If GetLinks() returns anything, add those to the queue and process them as well.
.
public List<string> ExpandLinksOrSomething(List<string> urls)
{
List<string> result = new List<string>();
Queue<string> queue = new Queue<string>(urls);
while (queue.Any())
{
string url = queue.Dequeue();
result.Add(url);
foreach( string newResult in GetLinks(url) )
{
queue.Enqueue(newResult);
}
}
return result;
}
The naive implementation assumes that GetLinks() will not return circular references. e.g. A returns B, and B returns A. This can be fixed by:
List<string> newItems = GetLinks(url).Except(result).ToList();
foreach( string newResult in newItems )
{
queue.Enqueue(newResult);
}
* As others point out using a dictionary may be more efficient depending on how many items you process.
I find it strange that GetLinks() would return a value, and then later resolve that to more Url's. Maybe all you want to do is 1-level expansion. If so, we can get rid of the Queue altogether.
public static List<string> StraightProcess(List<string> urls)
{
List<string> result = new List<string>();
foreach (string url in urls)
{
result.Add(url);
result.AddRange(GetLinks(url));
}
return result;
}
I decided to rewrite it because while other answers used queues, it wasn't apparent that they didn't run forever.

Categories

Resources