I am a beginner c# programmer and just had a quick question on an application I am building. My process reads in multiple files with the purpose of stripping out specific records based on a 1 or 0 pipe delimited field in the text file. It is the last delimited field in the file actually. If it is a 0, I write it to a temp file (which will later replace the original that I read), if it is anything else I do not. And not to try to get it too confusing but there are two types of records in the file, a header row, and then that is followed by a few supp rows. The header row is the only one that has the flag, so as you can tell from below, if the bool gets set to a good record by being 0, it writes the header record along with all supp records below it until it hits a bad one in which case it will negate writing them until the next good one.
However, what I am trying to do now (and would like to know the easiest way), is how to write the header record without the last pipe delimited field (IE the flag). Since it should always be the last 2 characters of the row (for example "0|" or "1|" as the preceeding pipe is needed), should it be a string trim on my inputrecord string? Is there an easier way? Is there a way to do a split on the record but not actually include the last field (in this case, field 36)? Any advice would be appreciated. Thank you,
static void Main(string[] args)
{
try
{
string executionDirectory = RemoveFlaggedRecords.Properties.Settings.Default.executionDirectory;
string workDirectory = RemoveFlaggedRecords.Properties.Settings.Default.workingDirectory;
string[] files = Directory.GetFiles(executionDirectory, "FilePrefix*");
foreach (string file in files)
{
string tempFile = Path.Combine(workDirectory,Path.GetFileName(file));
using (StreamReader sr = new StreamReader(file,Encoding.Default))
{
StreamWriter sw = new StreamWriter(tempFile);
string inputRecord = sr.ReadLine();
bool goodRecord = false;
bool isheaderRecord = false;
while (inputRecord != null)
{
string[] fields = inputRecord.Split('|');
if (fields[0].ToString().ToUpper() == "HEADER")
{
goodRecord = Convert.ToInt32(fields[36]) == 0;
isheaderRecord = true;
}
if (goodRecord == true && isheaderRecord == true)
{
// I'm not sure what to do here to write the string without the 36th field***
}
else if (goodRecord == true)
{
sw.WriteLine(inputRecord);
}
inputRecord = sr.ReadLine();
}
sr.Close();
sw.Close();
sw = null;
}
}
string[] newFiles = Directory.GetFiles(workDirectory, "fileprefix*");
foreach (string file in newFiles)
{
string tempFile = Path.Combine(workDirectory, Path.GetFileName(file));
string destFile = Path.Combine(executionDirectory, Path.GetFileName(file));
File.Copy(tempFile, destFile, true);
if (File.Exists(destFile))
{
File.Delete(tempFile);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
// not done
}
}
One way you could do this - if what you want at that point in the code is to always write all but the final element in your string[] - is construct a for loop that terminates before the last item:
for (int i = 0; i < fields.Length - 1; i++)
{
// write your field here
}
This is assuming that you want to write each field individually, and that you want to iterate through fields in the first place. If all you want to do is just write a single string to a single line without using a loop, you could do this:
var truncatedFields = fields.Take(fields.Length - 1);
And then just write the truncatedFields string[] as you see fit. One way you could accomplish all this in a single line might look like so:
sw.WriteLine(String.Join("|", fields.Take(fields.Length - 1)));
goodRecord = fields.Last().Trim() == "0";
if (inputRecord.Contains("|") string outputRecord = inputRecord.Substring(1, inputRecord.LastIndexOf("|"));
Related
I've been trying to read a file line by line for my UNI project.
I am getting an error that I am not sure I understand. I would need your experiene to help me solve it out, please.
Some clarification for the code:
datas is a List, which has a custom class type which class has 3 properties: text1, text2, int1.
v is a simple object with the same custom class type as the datas List.
The data in the text file are in line-by-line, each line contains 1 value for the 3 properties like this: text1value;text2value;int1value.
if (File.Exists("example.txt"))
{
StreamReader sr = new StreamReader("example.txt");
while(!sr.EndOfStream)
{
string[] data = sr.ReadLine().Split(';');
v.text1 = data[0];
v.text2 = data[1];
v.int1 = Convert.ToInt32(data[2]);
datas.Add(v);
}
sr.Close();
Thanks to you guys I have made improvements on my code and made it work!
Now I only have 1 functionality error which I do not understand on the code which is after the read in is completed. (so the code runs without error, crash, etc. - but gives the wrong result SOMETIMES!).
int i = 0;
int cnt = datas.Count;
while (i < cnt)
{
if (datas[i].Text1 == tb_Text1.Text && datas[i].Text2 == tb_Text2.Text)
{
// I do stuff here with the correct combination
DialogResult = DialogResult.OK;
break;
}
else
{
i++;
}
}
if(i==cnt)
{
MessageBox.Show("The following combination is not in the txt file!");
}
}
So in the second part of the code, on the Windows Form, there are 2 textboxes: one is for the text1 property, the other is for the text2 property.
I would like it to work like it would in a username-password scenario.
If the user types a text1 and text2 value in the textboxes, and clicks on the button which is on the Form, and that specific text1 and text2 values are stored in the same line of the txt file which was read in in the first half of the code, it should ACCEPT that combination.
Now, my problem is, I have 2 lines of records in my txt file right now.
So that should mean that in my datas named List, there should be 2 "items".
The first line for example is this in the txt file: Example1;example123;1
And the second line is this: Example2;example234;1
Every time I write Example2 and example234 in the textboxes, it WORKS.
Every time I write Example1 and example123 in the textboxes, it DOESNT WORK and I get the MessageBox message.
Anyone have any idea where did I go wrong?
Remove your loop:
for(int j=0; j<x; j++)
{
sr.ReadLine();
}
I am assuming you are attempting to position to the correct line, but StreamReader.ReadLine() already advances the read position. You don't need the loop.
What is happening is that your loop is reading past the end of the file, so then the ReadLine in
string[] data = sr.ReadLine().Split(';');
returns null, and so the Split() throws a null reference exception.
I think that you are trying to do something along these lines? The ReadLine() will automatically move to the next row in the file.
if (File.Exists("example.txt"))
{
StreamReader sr = new StreamReader("example.txt");
while(!sr.EndOfStream)
{
string[] data = sr.ReadLine().Split(';');
v.text1 = data[0];
v.text2 = data[1];
v.int1 = Convert.ToInt32(data[2]);
datas.Add(v);
}
sr.Close();
}
To propose an additional improvement, use using to create the StreamReader and it will take care of the file handeling for you:
if (File.Exists("example.txt"))
{
using(StreamReader sr = new StreamReader("example.txt"))
{
while(!sr.EndOfStream)
{
string[] data = sr.ReadLine().Split(';');
v.text1 = data[0];
v.text2 = data[1];
v.int1 = Convert.ToInt32(data[2]);
datas.Add(v);
}
}
}
(And maybe include the case that the file does not exist as an error and catch it.)
Your loop is the while. The for() loop will just disrupt the flow. My guess is you think you have to read from the start every time you want to do a ReadLine(). But the stream will remember where you left off after the last ReadLine().
if (File.Exists("example.txt"))
{
StreamReader sr = new StreamReader("example.txt");
while(!sr.EndOfStream)
{
string[] data = sr.ReadLine().Split(';');
v.text1 = data[0];
v.text2 = data[1];
v.int1 = Convert.ToInt32(data[2]);
datas.Add(v);
}
sr.Close();
}
I've been working with some big delimited text (~1GB) files these days. It looks like somewhat below
COlumn1 #COlumn2#COlumn3#COlumn4
COlumn1#COlumn2#COlumn3 #COlumn4
where # is the delimiter.
In case a column is invalid I might have to remove it from the whole text file. The output file when Column 3 is invalid should look like this.
COlumn1 #COlumn2#COlumn4
COlumn1#COlumn2#COlumn4
string line = "COlumn1# COlumn2 #COlumn3# COlumn4";
int junk =3;
int columncount = line.Split(new char[] { '#' }, StringSplitOptions.None).Count();
//remove the [junk-1]th '#' and the value till [junk]th '#'
//"COlumn1# COlumn2 # COlumn4"
I's not able to find a c# version of this in SO. Is there a way I can do that? Please help.
EDIT:
The solution which I found myself is like below which does the job. Is there a way I could modify this to a better way so that it narrows down the performance impact it might have in case of large text files?
int junk = 3;
string line = "COlumn1#COlumn2#COlumn3#COlumn4";
int counter = 0;
int colcount = line.Split(new char[] { '#' }, StringSplitOptions.None).Length - 1;
string[] linearray = line.Split(new char[] { '#' }, StringSplitOptions.None);
List<string> linelist = linearray.ToList();
linelist.RemoveAt(junk - 1);
string finalline = string.Empty;
foreach (string s in linelist)
{
counter++;
finalline += s;
if (counter < colcount)
finalline += "#";
}
Console.WriteLine(finalline);
EDITED
This method can be very memory expensive, as your can read in this post, the suggestion should be:
If you need to run complex queries against the data in the file, the right thing to do is to load the data to database and let DBMS to take care of data retrieval and memory management.
To avoid memory consumption you should use a StreamReader to read file line by line
This could be a start for your task, missing your invalid match logic
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const string fileName = "temp.txt";
var results = FindInvalidColumns(fileName);
using (var reader = File.OpenText(fileName))
{
while (!reader.EndOfStream)
{
var builder = new StringBuilder();
var line = reader.ReadLine();
if (line == null) continue;
var split = line.Split(new[] { "#" }, 0);
for (var i = 0; i < split.Length; i++)
if (!results.Contains(i))
builder.Append(split[i]);
using (var fs = new FileStream("new.txt", FileMode.Append, FileAccess.Write))
using (var sw = new StreamWriter(fs))
{
sw.WriteLine(builder.ToString());
}
}
}
}
private static List<int> FindInvalidColumns(string fileName)
{
var invalidColumnIndexes = new List<int>();
using (var reader = File.OpenText(fileName))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line == null) continue;
var split = line.Split(new[] { "#" }, 0);
for (var i = 0; i < split.Length; i++)
{
if (IsInvalid(split[i]) && !invalidColumnIndexes.Contains(i))
invalidColumnIndexes.Add(i);
}
}
}
return invalidColumnIndexes;
}
private static bool IsInvalid(string s)
{
return false;
}
}
}
First, what you will do is re-write the line to a text file using a 0-length string for COlumn3. Therefore the line after being written correctly would look like this:
COlumun1#COlumn2##COlumn4
As you can see, there are two delimiters between COlumn2 and COlumn4. This is a cell with no data in it. (By "cell" I mean one column of a certain, single row.) Later, when some other process reads this using the Split function, it will still create a new value for Column 3, but in the array generated by Split, the 3rd position would be an empty string:
String[] columns = stream_reader.ReadLine().Split('#');
int lengthOfThirdItem = columns[2].Length; // for proof
// lengthOfThirdItem = 0
This reduces invalid values to null and persists them back in the text file.
For more on String.Split see C# StreamReader save to Array with separator.
It is not possible to write to lines internal to a text file while it is also open for read. This article discusses it some (simultaneous read-write a file in C#), but it looks like that question-asker just wants to be able to write lines to the end. You want to be able to write lines at any point in the interior. I think this is not possible without buffering the data in some way.
The simplest way to buffer the data is rename the file to a temp file first (using File.CoMovepy() // http://msdn.microsoft.com/en-us/library/system.io.file.move(v=vs.110).aspx). Then use the temp file as the data source. Just open the temp file that to read in the data which may have corrupt entries, and write the data afresh to the original file name using the approach I describe above to represent empty columns. After this is complete, then you should delete the temp file.
Important
Deleting the temp file may leave you vulnerable to power and data transients (or software 'transients'). (I.e., a power drop that interrupts part of the process could leave the data in an unusable state.) So you may also want to leave the temp file on the drive as an emergency backup in case of some problem.
I'm trying to get the contents of a Text File, delete a line of string, and re-write back to the Text File, deleting the line of string. I'm using StreamReader to get the text, importing into a List, removing the string, then rewriting using StreamWriter. My problems arises somewhere around the removing or writing of the string. Instead of writing back the existing, non deleted contents to the text file, all the text is replaced with :
System.Collections.Generic.List`1[System.String]
My code for this function is as follows:
{
for (int i = deleteDevice.Count - 1; i >= 0; i--)
{
string split = "";
//deleteDevice[i].Split(',').ToString();
List<string> parts = split.Split(',').ToList();
if (parts.Contains(deviceList.SelectedItem.ToString()))
{
deleteDevice.Remove(i.ToString());
}
}
if (deleteDevice.Count != 0) //Error Handling
{
writer.WriteLine(deleteDevice);
}
}
deviceList.Items.Remove(deviceList.SelectedItem);
}
I would just like the script to write back any string that isn't deleted (If there is any), without replacing it. Any help is appreciated, Cheers
You can read all the info from the text file into a list and then remove from the list and rewrite that to the text file.
I would change the list 'deleteDevice' to store a string array instead and use the code below to determine which item to remove.
List<int> toRemove = new List<int>();
int i = 0;
/*build a list of indexes to remove*/
foreach (string[] x in deleteDevice)
{
if (x[0].Contains(deviceList.SelectedItem.ToString()))
{
toRemove.Add(i);
}
i++;
}
/*Remove items from list*/
foreach (int fd in toRemove)
deleteDevice.RemoveAt(fd);
/*write to text file*/
using (StreamWriter writer = new StreamWriter("Devices.txt"))
{
if (deleteDevice.Count != 0) //Error Handling
{
foreach (string[] s in deleteDevice)
{
StringBuilder sb = new StringBuilder();
for (int fds = 0; fds < s.Length; fds++ )
{
sb.Append(s[fds] + ",");
}
string line = sb.ToString();
writer.WriteLine(line.Substring(0, line.Length - 1));
}
}
}
This isn't the best solution but should work for your needs. There's probably a much easier way of doing this.
The problem is in the following line:
writer.WriteLine(deleteDevice);
You're writing deleteDevice (I assume this is of type List). List.ToString() returns the type name of the list, because this has no specific implementation. What you want is
foreach(String s in deleteDevice)
{
writer.WriteLine(s);
}
Problems
deleteDevice is of type List<string>, and because it also doesn't overload ToString(), the default behaviour of List<string>.ToString() is to return the name of the type.
Hence your line writer.WriteLine(deleteDevice); writes the string System.Collections.Generic.List1[System.String]`.
Other than that, there are many things wrong with your code...
For example, you do this:
string split = "";
and then on the line afterwards you do this:
List<string> parts = split.Split(',').ToList();
But because split is "", this will always return an empty list.
Solution
To simplify the code, you could first write a helper method that will remove from a file all the lines that match a specified predicate:
public void RemoveUnwantedLines(string filename, Predicate<string> unwanted)
{
var lines = File.ReadAllLines(filename);
File.WriteAllLines(filename, lines.Where(line => !unwanted(line)));
}
Then you can write the predicate something like this (this might not be quite right; I don't really know exactly what your code is doing because it's not compilable and omits some of the types):
string filename = "My Filename";
string deviceToRemove= deviceList.SelectedItem.ToString();
Predicate<string> unwanted = line =>
line.Split(new [] {','})
.Contains(deviceToRemove);
RemoveUnwantedLines(filename, unwanted);
I want to read 4-5 CSV files in some array in C#
I know that this question is been asked and I have gone through them...
But my use of CSVs is too much simpler for that...
I have csv fiels with columns of following data types....
string , string
These strings are without ',' so no tension...
That's it. And they aren't much big. Only about 20 records in each.
I just want to read them into array of C#....
Is there any very very simple and direct way to do that?
To read the file, use
TextReader reader = File.OpenText(filename);
To read a line:
string line = reader.ReadLine()
then
string[] tokens = line.Split(',');
to separate them.
By using a loop around the two last example lines, you could add each array of tokens into a list, if that's what you need.
This one includes the quotes & commas in fields. (assumes you're doing a line at a time)
using Microsoft.VisualBasic.FileIO; //For TextFieldParser
// blah blah blah
StringReader csv_reader = new StringReader(csv_line);
TextFieldParser csv_parser = new TextFieldParser(csv_reader);
csv_parser.SetDelimiters(",");
csv_parser.HasFieldsEnclosedInQuotes = true;
string[] csv_array = csv_parser.ReadFields();
Here is a simple way to get a CSV content to an array of strings. The CSV file can have double quotes, carriage return line feeds and the delimiter is a comma.
Here are the libraries that you need:
System.IO;
System.Collection.Generic;
System.IO is for FileStream and StreamReader class to access your file. Both classes implement the IDisposable interface, so you can use the using statements to close your streams. (example below)
System.Collection.Generic namespace is for collections, such as IList,List, and ArrayList, etc... In this example, we'll use the List class, because Lists are better than Arrays in my honest opinion. However, before I return our outbound variable, i'll call the .ToArray() member method to return the array.
There are many ways to get content from your file, I personally prefer to use a while(condition) loop to iterate over the contents. In the condition clause, use !lReader.EndOfStream. While not end of stream, continue iterating over the file.
public string[] GetCsvContent(string iFileName)
{
List<string> oCsvContent = new List<string>();
using (FileStream lFileStream =
new FileStream(iFilename, FileMode.Open, FileAccess.Read))
{
StringBuilder lFileContent = new StringBuilder();
using (StreamReader lReader = new StreamReader(lFileStream))
{
// flag if a double quote is found
bool lContainsDoubleQuotes = false;
// a string for the csv value
string lCsvValue = "";
// loop through the file until you read the end
while (!lReader.EndOfStream)
{
// stores each line in a variable
string lCsvLine = lReader.ReadLine();
// for each character in the line...
foreach (char lLetter in lCsvLine)
{
// check if the character is a double quote
if (lLetter == '"')
{
if (!lContainsDoubleQuotes)
{
lContainsDoubleQuotes = true;
}
else
{
lContainsDoubleQuotes = false;
}
}
// if we come across a comma
// AND it's not within a double quote..
if (lLetter == ',' && !lContainsDoubleQuotes)
{
// add our string to the array
oCsvContent.Add(lCsvValue);
// null out our string
lCsvValue = "";
}
else
{
// add the character to our string
lCsvValue += lLetter;
}
}
}
}
}
return oCsvContent.ToArray();
}
Hope this helps! Very easy and very quick.
Cheers!
I have a .txt file with a list of 174 different strings. Each string has an unique identifier.
For example:
123|this data is variable|
456|this data is variable|
789|so is this|
etc..
I wish to write a programe in C# that will read the .txt file and display only one of the 174 strings if I specify the ID of the string I want. This is because in the file I have all the data is variable so only the ID can be used to pull the string. So instead of ending up with the example about I get just one line.
eg just
123|this data is variable|
I seem to be able to write a programe that will pull just the ID from the .txt file and not the entire string or a program that mearly reads the whole file and displays it. But am yet to wirte on that does exactly what I need. HELP!
Well the actual string i get out from the txt file has no '|' they were just in the example. An example of the real string would be: 0111111(0010101) where the data in the brackets is variable. The brackets dont exsist in the real string either.
namespace String_reader
{
class Program
{
static void Main(string[] args)
{
String filepath = #"C:\my file name here";
string line;
if(File.Exists(filepath))
{
StreamReader file = null;
try
{
file = new StreamReader(filepath);
while ((line = file.ReadLine()) !=null)
{
string regMatch = "ID number here"; //this is where it all falls apart.
Regex.IsMatch (line, regMatch);
Console.WriteLine (line);// When program is run it just displays the whole .txt file
}
}
}
finally{
if (file !=null)
file.Close();
}
}
Console.ReadLine();
}
}
}
Use a Regex. Something along the lines of Regex.Match("|"+inputString+"|",#"\|[ ]*\d+\|(.+?)\|").Groups[1].Value
Oh, I almost forgot; you'll need to substitute the d+ for the actual index you want. Right now, that'll just get you the first one.
The "|" before and after the input string makes sure both the index and the value are enclosed in a | for all elements, including the first and last. There's ways of doing a Regex without it, but IMHO they just make your regex more complicated, and less readable.
Assuming you have path and id.
Console.WriteLine(File.ReadAllLines(path).Where(l => l.StartsWith(id + "|")).FirstOrDefault());
Use ReadLines to get a string array of lines then string split on the |
You could use Regex.Split method
FileInfo info = new FileInfo("filename.txt");
String[] lines = info.OpenText().ReadToEnd().Split(' ');
foreach(String line in lines)
{
int id = Convert.ToInt32(line.Split('|')[0]);
string text = Convert.ToInt32(line.Split('|')[1]);
}
Read the data into a string
Split the string on "|"
Read the items 2 by 2: key:value,key:value,...
Add them to a dictionary
Now you can easily find your string with dictionary[key].
first load the hole file to a string.
then try this:
string s = "123|this data is variable| 456|this data is also variable| 789|so is this|";
int index = s.IndexOf("123", 0);
string temp = s.Substring(index,s.Length-index);
string[] splitStr = temp.Split('|');
Console.WriteLine(splitStr[1]);
hope this is what you are looking for.
private static IEnumerable<string> ReadLines(string fspec)
{
using (var reader = new StreamReader(new FileStream(fspec, FileMode.Open, FileAccess.Read, FileShare.Read)))
{
while (!reader.EndOfStream)
yield return reader.ReadLine();
}
}
var dict = ReadLines("input.txt")
.Select(s =>
{
var split = s.Split("|".ToArray(), 2);
return new {Id = Int32.Parse(split[0]), Text = split[1]};
})
.ToDictionary(kv => kv.Id, kv => kv.Text);
Please note that with .NET 4.0 you don't need the ReadLines function, because there is ReadLines
You can now work with that as any dictionary:
Console.WriteLine(dict[12]);
Console.WriteLine(dict[999]);
No error handling here, please add your own
You can use Split method to divide the entire text into parts sepparated by '|'. Then all even elements will correspond to numbers odd elements - to strings.
StreamReader sr = new StreamReader(filename);
string text = sr.ReadToEnd();
string[] data = text.Split('|');
Then convert certain data elements to numbers and strings, i.e. int[] IDs and string[] Strs. Find the index of the given ID with idx = Array.FindIndex(IDs, ID.Equals) and the corresponding string will be Strs[idx]
List <int> IDs;
List <string> Strs;
for (int i = 0; i < data.Length - 1; i += 2)
{
IDs.Add(int.Parse(data[i]));
Strs.Add(data[i + 1]);
}
idx = Array.FindIndex(IDs, ID.Equals); // we get ID from input
answer = Strs[idx];