Writing ObservableCollection to file - c#

What I have:
I currently have this class:
class Storico
{
private string rigaStorico;
public string Name
{
get { return rigaStorico; }
set { rigaStorico = value; }
}
public Storico(string storic)
{
rigaStorico = storic;
}
}
This ObservableCollection:
ObservableCollection<Storico> rigaStorico = new ObservableCollection<Storico>();
And this DataTemplate, located into a LongListSelector:
<DataTemplate>
<Grid>
<TextBlock Text="●" Margin="0,8,0,0" Foreground="#EE7B00"/>
<TextBlock Text="{Binding Name}" ManipulationStarted="TextBlock_ManipulationStarted" ManipulationCompleted="TextBlock_ManipulationCompleted">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Name="ContextMenu" >
<toolkit:MenuItem Name="DeleteItem" Header="Delete Item" Click="DeleteItem_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</TextBlock>
</Grid>
</DataTemplate>
What I need to do:
I actually need take all the strings contained into the LongListSelector, that may be like:
String1
String2
String3
and write them to a file into IsolatedStorage, in inversed order. Something like this:
String3
String2
String1
Obviously enough, the ItemsSource of the Storico is rigaStorico itself:
Storico.ItemsSource = rigaStorico;
I hope my goal is clear and I gave all the stuff that's needed to solve it.

Try this:
IsolatedStorageFile savegameStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream fs = null;
using (fs = savegameStorage.CreateFile("FILENAME"))
{
if (fs != null)
{
var index = 0;
foreach(var rigaStor in rigaStorico.Reverse())
{
byte[] bytes = Encoding.UTF8.GetBytes(rigaStor.Name);
fs.Write(bytes, index, bytes.Length);
index = bytes.Length;
}
}
}

Not sure why you need them in reverse order, but you could use this FileStorage to write the data out.
FileStorage.WriteSharedData("MyItems.js", rigaStorico.Reverse());
The FileStorage uses json.net under the hood, if you want you could just do:
var storage = IsolatedStorageFile.GetUserStoreForApplication();
using (var fileStream = storage.CreateFile(fileName))
{
//Write the data
using (var isoFileWriter = new StreamWriter(fileStream))
{
var json = JsonConvert.SerializeObject(rigaStorico.Reverse());
isoFileWriter.WriteLine(json);
}
}

Thanks for the help. I solved my issue with the following code:
string[] contenutoStorico = rigaStorico.Select(x => x.Name).ToArray();
IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream("history.txt", FileMode.Create, FileAccess.Write, myIsolatedStorage)))
{
for (int i = 0; i < contenutoStorico.Length; i++)
{
writeFile.WriteLine(contenutoStorico[contenutoStorico.Length-i-1]);
}
writeFile.Close();
}

Related

UWP - Image doesn't change content when image file it references to are change

I'm trying to take video snapshot and preview it by a scroller. Assumed that we have bohemian.mp4, my app would take snapshot at some point and save to bohemian.png. Here's mentioned part.
xaml:
<Image x:Name="thumbnail" CacheMode="BitmapCache"
MinHeight="200" MaxHeight="400" Margin="0,20,0,0" Stretch="Uniform"
Source="{x:Bind mp3ViewerViewModel.mp3ViewerModel.ImagePath, Mode=OneWay}"/>
<Slider x:Name="SnapshotSlider"
Minimum="0" Maximum="{x:Bind mp3ViewerViewModel.mp3ViewerModel.Duration}"
Value="{x:Bind mp3ViewerViewModel.mp3ViewerModel.Snapshot}"
IsEnabled="{x:Bind mp3ViewerViewModel.mp3ViewerModel.UseSnapshot, Mode=OneWay}"
/>
<Button x:Name="SaveButton"
RelativePanel.Below="detailGrid" RelativePanel.AlignRightWith="detailGrid"
Foreground="White" BorderBrush="#FF004CFF" Background="#FF0096FF"
Content="Save As"
Command="{x:Bind mp3ViewerViewModel.UpdateSnapshotCmd}"
CommandParameter="{Binding ElementName=SnapshotSlider, Path=Value}"/>
For debugging purpose, I would let Button handle my Command
View Model:
public CommandEventHandler<double> UpdateSnapshotCmd
{
get
{
return new CommandEventHandler<double>(
async (second) => await UpdateSnapshotPreview(second)
);
}
}
public async Task UpdateSnapshotPreview(double second)
{
try
{
if (mp4 == null)
{
mp4 = await StorageFile.GetFileFromPathAsync(mp4Path);
}
var imgName = Path.ChangeExtension(mp4.Name, ".png");
StorageFile imgFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(imgName, CreationCollisionOption.ReplaceExisting);
MediaComposition mediaComposition = new MediaComposition();
MediaClip mediaClip = await MediaClip.CreateFromFileAsync(mp4);
mediaComposition.Clips.Add(mediaClip);
var interval = TimeSpan.FromSeconds(second);
var thumbnail = await mediaComposition.GetThumbnailAsync(interval,
1024, 768, VideoFramePrecision.NearestKeyFrame);
using (var randomAccess = await imgFile.OpenAsync(FileAccessMode.ReadWrite))
using (var destStream = randomAccess.AsStream())
using (var sourceStream = thumbnail.AsStream())
{
await sourceStream.CopyToAsync(destStream);
}
mp3ViewerModel.ImagePath = imgFile.Path;
}
catch (Exception ex)
{
await new MessageDialog(ex.Message).ShowAsync();
}
}
Model:
string imagePath = "/Assets/default_thumbnail.jpg";
string defaultImg = "/Assets/default_thumbnail.jpg";
public string ImagePath
{
get
{
if (UseSnapshot)
return imagePath;
else
return defaultImg;
}
set
{
imagePath = value;
this.OnPropertyChanged();
}
}
When I checked saved folder, png file was changed as Slider.Value but Image in the app just responsed once. How to fix this?
I think because file path remains unchanged so that lead to Image behaves the same despite of different content or OnPropertyChanged invoke.
Therefore, I come to a solution which is in ViewModel:
StorageFile imgFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(imgName,
CreationCollisionOption.ReplaceExisting);
Change to:
StorageFile imgFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(imgName,
CreationCollisionOption.GenerateUniqueName);
However, this will create a bunch of png and look ugly. I wonder if there any alternative solution that I could re-use the first and only png?
Use a FileSystemWatcher to watch for when a file changes:
i think this because Image does some caching internally so you have to explicitly set CreateOptions = BitmapCreateOptions.IgnoreImageCache
in my case:
service Task
public async Task<BitmapImage> TakePhotoAsync()
{
if (IsInitialized && IsPreviewing && !IsRecordiing)
{
try
{
var file = await storageFolder.CreateFileAsync("photo.jpg", CreationCollisionOption.ReplaceExisting);
await mediaCapture.CapturePhotoToStorageFileAsync(ImageEncodingProperties.CreateJpeg(), file);
return new BitmapImage
{
UriSource = new Uri(file.Path),
CreateOptions = BitmapCreateOptions.IgnoreImageCache
};
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
return null;
}
ViewModel Property
private ImageSource photoImage;
public ImageSource PhotoImage
{
get => photoImage;
set => SetProperty(ref photoImage, value);
}
ViewModel Command to get image
public RelayCommand TakePhotoCommand { get; set; }
ViewModel Constructor
TakePhotoCommand = new RelayCommand(async () => PhotoImage = await CameraService.TakePhotoAsync());
Image Binding
<Image Source="{x:Bind ViewModel.PhotoImage, Mode=OneWay}/>
So basically you have yo change:
string ImagePath
to
ImageSource ImagePath
and
ImagePath = new BitmapImage
{
UriSource = new Uri(imgFile.Path),
CreateOptions = BitmapCreateOptions.IgnoreImageCache
}
Hope that helps

C# Event fires a lot of times

I have a problem. I am using a CollectionView that receives data in a custom ViewModel from my webpage as long as it returns a JSON with the data. Once the Offset in the call >= num_of_rows the webpage prints "Nothing". If that happens I set a boolean HitBottomOfList = true;. Now everytime when it wants to do a webcall it checks if the HitBottomOfList == false.
Full Code
ViewModel:
public class TemplateListViewModel
{
public double WidthHeight { get; set; }
public ICommand LoadTemplates => new Command(LoadTemplateList);
public int CurrentTemplateCountReceived;
public bool HitBottomOfList = false;
public ObservableCollection<TemplateSource> sourceList { get; set; }
public TemplateListViewModel()
{
CurrentTemplateCountReceived = 0;
sourceList = new ObservableCollection<TemplateSource>();
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
var width = mainDisplayInfo.Width;
var density = mainDisplayInfo.Density;
var ScaledWidth = width / density;
WidthHeight = (ScaledWidth / 2);
loadingTemplates += onLoadingTemplates;
LoadTemplateList();
}
private event EventHandler loadingTemplates = delegate { };
private void LoadTemplateList()
{
loadingTemplates(this, EventArgs.Empty);
}
private async void onLoadingTemplates(object sender, EventArgs args)
{
if (HitBottomOfList == false)
{
List<Template> templateList = await App.RestService.GetTemplates(App.User, CurrentTemplateCountReceived);
if (templateList != null)
{
foreach (var template in templateList)
{
ImageSource source = ImageSource.FromUri(new Uri("mysite.org/myapp/" + template.FileName));
TemplateSource templateSource = new TemplateSource { Id = template.Id, Source = source, WidthHeight = WidthHeight, FileName = template.FileName };
sourceList.Add(templateSource);
}
CurrentTemplateCountReceived = sourceList.Count;
}
else
{
HitBottomOfList = true;
}
}
}
}
The XAML:
<CollectionView ItemsSource="{Binding sourceList}" RemainingItemsThreshold="6"
RemainingItemsThresholdReachedCommand="{Binding LoadTemplates}">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ff:CachedImage
Source="{Binding Source}"
VerticalOptions="Center"
HorizontalOptions="Center"
WidthRequest="{Binding WidthHeight}"
HeightRequest="{Binding WidthHeight}">
<ff:CachedImage.GestureRecognizers>
<TapGestureRecognizer Tapped="imgTemplate_Clicked" />
</ff:CachedImage.GestureRecognizers>
</ff:CachedImage>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
And finally the WebCall that I do:
public async Task<List<Template>> GetTemplates(User user, int offset)
{
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("un", user.Username));
postData.Add(new KeyValuePair<string, string>("pw", user.Password));
postData.Add(new KeyValuePair<string, string>("offset", offset.ToString()));
var content = new FormUrlEncodedContent(postData);
var weburl = "mysite.org/myapp/get_templates.php";
List<Template> response = await PostResponseTemplates(weburl, content);
return response;
}
public async Task<List<Template>> PostResponseTemplates(string weburl, FormUrlEncodedContent content)
{
var response = await client.PostAsync(weburl, content);
var json = await response.Content.ReadAsStringAsync();
if (json != "Nothing")
{
var jObject = JObject.Parse(json);
var templatePropery = jObject["Templates"] as JArray;
List<Template> templateList = new List<Template>();
foreach (var property in templatePropery)
{
List<Template> propertyList = new List<Template>();
propertyList = JsonConvert.DeserializeObject<List<Template>>(property.ToString());
templateList.AddRange(propertyList);
}
var sourcePropery = (JObject)jObject["Source"];
foreach (var property in sourcePropery)
{
string tempplateSource = property.Value.Value<string>();
App.TemplateSource = tempplateSource;
}
return templateList;
}
else
{
ErrorMessage = json;
return default(List<Template>);
}
}
Now the problem is that when it does trigger the RemainingItemsThresholdReachedCommand="{Binding LoadTemplates}"
it executes the command a lot of times after each other, thinking it needs more data, while there is already a command to get new data. This causes the app to get new data with the same offset a few times, so the app will the same data in the CollectionView a lot of times.
I want the app to call the webpage 1 time to receive more images and just let it load, without asking again for new data, so the duplicates in the list will disappear.
So how can I make sure it only asks the data once, when almost hit the bottom?
Update
Using #Jason his code the following is going wrong:
When the code goes through the MyHandler, it fires the LoadTemplateList(); But jumps to the handling = false; before it finished, so the next command is allowed to start, without finishing the other. Any idea how to wait for the method to finish?
use a bool to track if you are already handling the event and ignore any new ones
bool handling = false;
public void MyHandler()
{
// already handling an event, ignore the new one
if (handling) return;
handling = true;
// process event here
handling = false;
}

Lines missing in file split

I'm writing a program which splits a CSV file in four almost-equal parts.
I'm using a 2000-lines CSV input file as example, and when reviewing the output files, there are lines missing in the first file, and also there are uncomplete lines which makes no sense, since I'm writing line by line. Here the code:
using System.IO;
using System;
class MainClass {
public static void Main(string[] args){
string line;
int linesNumber = 0, linesEach = 0, cont = 0;
StreamReader r = new StreamReader("in.csv");
StreamWriter w1 = new StreamWriter("out-1.csv");
StreamWriter w2 = new StreamWriter("out-2.csv");
StreamWriter w3 = new StreamWriter("out-3.csv");
StreamWriter w4 = new StreamWriter("out-4.csv");
while((line = r.ReadLine()) != null)
++linesNumber;
linesEach = linesNumber / 4;
r.DiscardBufferedData();
r.BaseStream.Seek(0, SeekOrigin.Begin);
r.BaseStream.Position = 0;
while((line = r.ReadLine()) != null){
++cont;
if(cont == 1){
//fisrt line must be skipped
continue;
}
if(cont < linesEach){
Console.WriteLine(line);
w1.WriteLine(line);
}
else if(cont < (linesEach*2)){
w2.WriteLine(line);
}
else if(cont < (linesEach*3)){
w3.WriteLine(line);
}
else{
w4.WriteLine(line);
}
}
}
}
Why is the writing part doing wrong? How can I fix it?
Thank you all for your help.
You could simplify your approach by using a Partitioner and some LINQ. It also has the benefit of only having two file handles open at once, instead of 1 for each output file plus the original input file.
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
namespace FileSplitter
{
internal static class Program
{
internal static void Main(string[] args)
{
var input = File.ReadLines("in.csv").Skip(1);
var partitioner = Partitioner.Create(input);
var partitions = partitioner.GetPartitions(4);
for (int i = 0; i < partitions.Count; i++)
{
var enumerator = partitions[i];
using (var stream = File.OpenWrite($"out-{i + 1}.csv"))
{
using (var writer = new StreamWriter(stream))
{
while (enumerator.MoveNext())
{
writer.WriteLine(enumerator.Current);
}
}
}
}
}
}
}
This is not direct answer to your question, just an alternative.
Linq can be used to create shorter codes
int inx = 0;
var fInfo = new FileInfo(filename);
var lines = File.ReadAllLines(fInfo.FullName);
foreach (var groups in lines.GroupBy(x => inx++ / (lines.Length / 4)))
{
var newFileName = $"{fInfo.DirectoryName}\\{fInfo.Name}_{groups.Key}{fInfo.Extension}";
File.WriteAllLines(newFileName, groups);
}
Thank you all for your answers.
The problem is, as Jegan and spender suggested, that the StreamWriter needs to be wrapped in the using clause. That said, problem solved.

Temporarily Sort WP8 Listbox

I have an WP C# app built on the Local Database Sample (http://code.msdn.microsoft.com/wpapps/Local-Database-Sample-57b1614c).
The main page displays a list of items from the xml database, which by default shows items in the order created. I would like to be able to offer at least one other sort order - either reversed or sorted by "Subject". Unfortunately Listbox.Sort is not supported in WP.
I have tried various answers found on here, including attempting to sort the xml file itself, but for reasons beyond my level of coding they do not change the order of the list (see Templatestorage) however i suspect it is due to improper implementation.
The code for the listbox is:
<ListBox x:Name="Templates" SelectionChanged="OnSelectionChanged" Background="Transparent" Style="{StaticResource ListBoxStyle1}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Grid.Row="2" Text="{Binding Subject}" Style="{StaticResource PhoneTextLargeStyle}" Margin="12,2" />
<TextBlock Grid.Row="2" Text="{Binding DT}" Style="{StaticResource PhoneTextSmallStyle}" Margin="12,5" />
<Rectangle Height="1" Margin="23,7,50,7" Fill="{StaticResource PhoneAccentBrush}" MinWidth="400" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The c# is:
public partial class MainPage
{
private readonly TemplateStorage storage = new TemplateStorage();
public MainPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Templates.ItemsSource = storage.GetItems();
this.NavigationService.RemoveBackEntry();
}
private void PhoneApplicationPage_GotFocus(object sender, RoutedEventArgs e)
{
Templates.ItemsSource = storage.GetItems();
}
}
The Templatestorage, which shows the various attempts at sorting (commented out) is:
public class TemplateStorage
{
private IList<NanoMemoTemplate> templates;
private const string Filename = "template-list.xml";
protected IList<NanoMemoTemplate> Templates
{
get
{
return templates ?? (templates = LoadTemplates().ToList());
}
set
{
templates = value;
}
}
protected IEnumerable<NanoMemoTemplate> LoadTemplates()
{
using(var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if(!applicationStorage.FileExists(Filename))
return Enumerable.Empty<NanoMemoTemplate>();
using(var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Open, FileAccess.Read))
{
var document = XDocument.Load(speedListFile);
return from t in document.Root.Elements("template")
select new NanoMemoTemplate
{
Id = new Guid(t.Attribute("id").Value),
Subject = t.Attribute("subject").Value,
Body = t.Attribute("body").Value,
DT = t.Attribute("dateCreated").Value,
};
}
}
}
//public IEnumerable<NanoMemoTemplate> SortTemplates()
//{
// using (var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
// {
// if (!applicationStorage.FileExists(Filename))
// return Enumerable.Empty<NanoMemoTemplate>();
// using (var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Open, FileAccess.ReadWrite))
// {
// var documentSort = XDocument.Load(speedListFile);
// XDocument datatemp = new XDocument(documentSort);
// var subjectSort = from p in datatemp.Descendants("template")
// orderby (string)p.Attribute("subject")
// select p;
// //var subjectSort = datatemp.Elements("template").OrderBy(p => (string)p.Attribute("subject")).ToArray();
// string cleanDataDump = subjectSort.ToString();
// MessageBox.Show(cleanDataDump);
// documentSort.Descendants("template").Remove();
// documentSort.Element("template").Add(subjectSort);
// return Templates;
// }
// }
//}
//public IEnumerable<NanoMemoTemplate> SortItems()
//{
// //Sort XML order so order is saved
// using (var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
// {
// if (!applicationStorage.FileExists(Filename))
// return Enumerable.Empty<NanoMemoTemplate>();
// using (var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Open, FileAccess.ReadWrite))
// {
// var documentSort = XDocument.Load(speedListFile);
// IEnumerable<string> codes = from code in documentSort.Elements("template")
// let subs = (string)code.Element("subject")
// orderby subs
// select subs;
// //return Templates as per usual as sorting is done at DB level
// return from t in documentSort.Root.Elements("template")
// select new NanoMemoTemplate
// {
// Id = new Guid(t.Attribute("id").Value),
// Subject = t.Attribute("subject").Value,
// Body = t.Attribute("body").Value,
// DT = t.Attribute("dateCreated").Value,
// };
// }
// }
//}
public IEnumerable<NanoMemoTemplate> GetItems()
{
return Templates;
}
public void Save(NanoMemoTemplate template)
{
Templates.Add(template);
}
public void Delete(NanoMemoTemplate template)
{
Templates.Remove(template);
}
//public void Sort(NanoMemoTemplate template)
//{
// IList<NanoMemoTemplate> list = new List<NanoMemoTemplate>();
// IEnumerable<NanoMemoTemplate> sortedEnum = list.OrderBy(Templates => Templates.Subject);
// IList<NanoMemoTemplate> sortedList = sortedEnum.ToList();
//}
public void SaveChanges()
{
using(var applicationStorage = IsolatedStorageFile.GetUserStoreForApplication())
using(var speedListFile = applicationStorage.OpenFile(Filename, FileMode.Create, FileAccess.Write))
{
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("templates",
from t in Templates
select new XElement("template",
new XAttribute("id", t.Id),
new XAttribute("subject", t.Subject),
new XAttribute("body", t.Body),
new XAttribute("dateCreated", t.DT))));
document.Save(speedListFile);
}
}
}
Instead of having to set Templates.ItemsSource = storage.GetItems(); in your code, you can keep an ObservableCollection (or other enumerable type) as a class-level variable:
//StorageTemplates should be a class-level variable
ObservableCollection<NanoMemoTemplate> StorageTemplates;
//You can assign the value to StorageTemplates when the page loads
StorageTemplates = storage.GetItems();
You would then apply an ItemsSource="{Binding StorageTemplates}" data binding to your ListBox in XAML. (See this for more info on binding)
<ListBox x:Name="Templates" ItemsSource="{Binding StorageTemplates, UpdateSourceTrigger="PropertyChanged"}" SelectionChanged="OnSelectionChanged" Background="Transparent" Style="{StaticResource ListBoxStyle1}" >
<ListBox.ItemTemplate>
....
</ListBox.ItemTemplate>
</ListBox>
Then you can use the built-in Sort methods of the ObservableCollection to set your sort order for the items. You may need to implement a Property Changed handler, you can check this tutorial for more information.

How to delete a line having line number using c#?

My file named as test.txt contains
This document is divided into about 5 logical sections starting with a feature and structure overview, followed by an overview of built in column and cell types. Next is an overview of working with data, followed by an overview of specific major features. Lastly, a “best practice” section concludes the main part of this document.
Now i want to delete 2nd line of the file.
How to do it using c#?
Thanks in advance.
Naveenkumar
List<string> lines = File.ReadAllLines(#"filename.txt").ToList();
if(lines.Count>lineNum){
lines.RemoveAt(lineNum);
}
File.WriteAllLines(#"filename.txt",lines.ToArray());
You can acheive this by splitting the text by \n and then using LINQ to select the lines you want to keep, and re-joining them.
var lineNum=5;
var lines=File
.ReadAllText(#"src.txt")
.Split('\n');
var outTxt=String
.Join(
"\n",
lines
.Take(lineNum)
.Concat(lines.Skip(lineNum+1))
.ToArray()
);
Here's a pretty efficient way to do it.
FileInfo x = new FileInfo(#"path\to\original");
string xpath = x.FullName;
FileInfo y = new FileInfo(#"path\to\temporary\new\file");
using (var reader = x.OpenText())
using (var writer = y.AppendText())
{
// write 1st line
writer.WriteLine(reader.ReadLine());
reader.ReadLine(); // skip 2nd line
// write all remaining lines
while (!reader.EndOfStream)
{
writer.WriteLine(reader.ReadLine());
}
}
x.Delete();
y.MoveTo(xpath);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace rem2ndline
{
class Program
{
static void Main(string[] args)
{
string inPath = #"c:\rem2ndline.txt";
string outPath = #"c:\rem2ndlineresult.txt";
StringBuilder builder = new StringBuilder();
using (FileStream fso = new FileStream(inPath, FileMode.Open))
{
using (StreamReader rdr = new StreamReader(fso))
{
int lineCount = 0;
bool canRead = true;
while (canRead)
{
var line = rdr.ReadLine();
lineCount++;
if (line == null)
{
canRead = false;
}
else
{
if (lineCount != 2)
{
builder.AppendLine(line);
}
}
}
}
}
using(FileStream fso2 = new FileStream(outPath, FileMode.OpenOrCreate))
{
using (StreamWriter strw = new StreamWriter(fso2))
{
strw.Write(builder.ToString());
}
}
}
}
}
Here's what I'd do. The advantage is that you don't have to have the file in memory all at once, so memory requirements should be similar for files of varying sizes (as long as the lines contained in each of the files are of similar length). The drawback is that you can't pipe back to the same file - you have to mess around with a Delete and a Move afterwards.
The extension methods may be overkill for your simple example, but those are two extension methods I come to rely on again and again, as well as the ReadFile method, so I'd typically only have to write the code in Main().
class Program
{
static void Main()
{
var file = #"C:\myFile.txt";
var tempFile = Path.ChangeExtension(file, "tmp");
using (var writer = new StreamWriter(tempFile))
{
ReadFile(file)
.FilterI((i, line) => i != 1)
.ForEach(l => writer.WriteLine(l));
}
File.Delete(file);
File.Move(tempFile, file);
}
static IEnumerable<String> ReadFile(String file)
{
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
yield return reader.ReadLine();
}
}
}
}
static class IEnumerableExtensions
{
public static IEnumerable<T> FilterI<T>(
this IEnumerable<T> seq,
Func<Int32, T, Boolean> filter)
{
var index = 0;
foreach (var item in seq)
{
if (filter(index, item))
{
yield return item;
}
index++;
}
}
public static void ForEach<T>(
this IEnumerable<T> seq,
Action<T> action)
{
foreach (var item in seq)
{
action(item);
}
}
}

Categories

Resources