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();
}
Hi I am trying to make an atom simulation application as my school project. I have created Arrays of each element that I will store like in Atomic number the Atomic number will be stored in Symbol H will be stored and so on. I have read the file using stream reader but i am having difficulty reading each element into the right array.
Txt file is here
1,H,Hydrogen,1,1+
3,Li,Lithium,7,1+
11,Na,Sodium,23,1+
19,K,Potassium,39,1+
37,Rb,Rubidium,85,1+
55,Cs,Casesium,133,1+
87,Fr,Francium,223,1+
4,Be,Beryllium,9,2+
12,Mg,Magnesium,24,2+
20,Ca,Calcium,40,2+
38,Sr,Strontium,88,2+
56,Ba,Barium,137,2+
88,Ra,Radium,226,2+
5,B,Boron,10,3+
13,Al,Aluminium,27,3+
31,Ga,Gallium,70,3+
49,In,Indium,115,3+
81,Tl,Thallium,204,3+
6,C,Carbon,12,0
14,Si,Silicon,28,0
32,Ge,Germanium,73,0
50,Sn,Tin,119,0
82,Pb,Lead,207,0
7,N,Nitrogen,14,3-
15,P,Phosphorus,31,3-
33,As,Arsenic,75,3-
51,Sb,Antimony,122,3-
83,Bi,Bismuth,209,3-
8,O,Oxygen,16,2-
16,S,Sulfur,32,2-
34,Se,Selenium,79,2-
52,Te,Tellurium,128,2-
84,Po,Polonium,209,2-
9,F,Fluorine,19,1-
17,Cl,Chlorine,35,1-
35,Br,Bromine,80,1-
53,I,Iodine,127,1-
85,At,Astatine,210,1-
2,He,Helium,4,0
10,Ne,Neon,20,0
18,Ar,Argon,40,0
36,Kr,Kryoton,85,0
54,Xe,Xenon,131,0
86,Rn,Radon,222,0
.
struct TAtom
{
public int atomicNumber;
public string symbol;
public string name;
public int mass;
public string charge;
}
class Atom
{
static void Main(string[] args)
{
{
TAtom[] Atom = new TAtom[44];
Atom[0].atomicNumber = 1;
TAtom[] Symbol = new TAtom[44];
Symbol[1].symbol = "";
TAtom[] Name = new TAtom[44];
Name[2].name = "";
TAtom[] Mass = new TAtom[44];
Mass[3].mass = '1';
TAtom[] Charge = new TAtom[44];
Charge[4].charge = "";
string[] words;
StreamReader File = new StreamReader(#"JUNK1.txt");
while (File.EndOfStream == false)
{
string line = File.ReadLine();
words = line.Split(',');
}
File.Close();
}
}
}
This variable contains the data from a "line" (or "record") in the file:
words = line.Split(',');
And such a record looks like this:
1,H,Hydrogen,1,1+
Assuming that the data is consistent (you'd want to add error-checking in a variety of places if it isn't), then you can construct an instance of TAtom like this:
var atom = new TAtom
{
atomicNumber = int.Parse(words[0]),
symbol = words[1],
name = words[2],
mass = int.Parse(words[3]),
charge = words[4]
};
Potential runtime errors include, but may not be limited to:
Invalid int data would result in an exception from int.Parse(). Look into the use of int.TryParse() as an alternative. It would require a bit of restructuring of this single line of code into multiple lines, reading inputs separately and constructing the TAtom instance at the end.
Incomplete "records" would result in an exception when attempting to access an invalid index on words. You might check the length of words before attempting to use it.
It would be in your best interests to take a look at what your code is doing in a debugger. As you step through each line of code while it's executing, you can see what's in your variables. Examining the runtime contents of words is your biggest clue on how to construct your TAtom instance.
Once you have this instance in the atom variable above, you can add that to any array/list/collection/etc. that you like. (See my comments on the question above regarding your overall design approach.)
I would read the data in first, as you are doing. Since each line is a TAtom, assign a new TAtom to the appropriate indicies of the comma delimited words(line) array, then add the atom you create for each line to an array or arraylist.
List<TAtom> atomList = new List<TAtom>();
StreamReader File = new StreamReader(#"JUNK1.txt");
while (File.EndOfStream == false)
{
string line = File.ReadLine();
TAtom tatom = new TAtom();
words = line.Split(',');
tatom.atomicNumber=words[0]
tatom.symbol=words[1]
tatom.name=words[2]
tatom.mass=words[3]
tatom.charge=words[4]
//add the current line's atom to a data structure
atomList.Add(tatom);
}
File.Close();
Now you can access all of the TAtom structs through the atomList!
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 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];
I'm trying to read in a text file in a c# application, but I don't want to read the first two lines, or the last line. There's 8 lines in the file, so effectivly I just want to read in lines, 3, 4, 5, 6 and 7.
Is there any way to do this?
example file
_USE [Shelley's Other Database]
CREATE TABLE db.exmpcustomers(
fName varchar(100) NULL,
lName varchar(100) NULL,
dateOfBirth date NULL,
houseNumber int NULL,
streetName varchar(100) NULL
) ON [PRIMARY]_
EDIT
Okay, so, I've implemented Callum Rogers answer into my code and for some reason it works with my edited text file (I created a text file with the lines I didn't want to use omitted) and it does exactly what it should, but whenever I try it with the original text file (above) it throws an exception. I display this information in a DataGrid and I think that's where the exception is being thrown.
Any ideas?
The Answer by Rogers is good, I am just providing another way of doing this.
Try this,
List<string> list = new List<string>();
using (StreamReader reader = new StreamReader(FilePath))
{
string text = "";
while ((text = reader.ReadLine()) != null)
{
list.Add(text);
}
list.RemoveAt(0);
list.RemoveAt(0);
}
Hope this helps
Why do you want to ignore exactly the first two and the last line?
Depending on what your file looks like you might want to analyze the line, e.g. look at the first character whether it is a comment sign, or ignore everything until you find the first empty line, etc.
Sometimes, hardcoding "magic" numbers isn't such a good idea. What if the file format needs to be changed to contain 3 header lines?
As the other answers demonstrate: Nothing keeps you from doing what you ever want with a line you have read, so of course, you can ignore it, too.
Edit, now that you've provided an example of your file: For your case I'd definitely not use the hardcoded numbers approach. What if some day the SQL statement should contain another field, or if it appears on one instead of 8 lines?
My suggestion: Read in the whole string at once, then analyze it. Safest way would be to use a grammar, but if you presume the SQL statement is never going to be more complicated, you can use a regular expression (still much better than using line numbers etc.):
string content = File.ReadAllText(filename);
Regex r = new Regex(#"CREATE TABLE [^\(]+\((.*)\) ON");
string whatYouWant = r.Match(content).Groups[0].Value;
Why not just use File.ReadAllLines() and then remove the first 2 lines and the last line? With such a small file speed differences will not be noticeable.
string[] allLines = File.ReadAllLines("file.ext");
string[] linesWanted = new string[allLines.Length-3];
Array.Copy(allLines, 2, linesWanted, 0, allLines.Length-3);
If you have a TextReader object wrapping the filestream you could just call ReadLine() two times.
StreamReader inherits from TextReader, which is abstract.
Non-fool proof example:
using (var fs = new FileStream("blah", FileMode.Open))
using (var reader = new StreamReader(fs))
{
reader.ReadLine();
reader.ReadLine();
// Do stuff.
}
string filepath = #"C:\whatever.txt";
using (StreamReader rdr = new StreamReader(filepath))
{
rdr.ReadLine(); // ignore 1st line
rdr.ReadLine(); // ignore 2nd line
string fileContents = "";
while (true)
{
string line = rdr.ReadLine();
if (rdr.EndOfStream)
break; // finish without processing last line
fileContents += line + #"\r\n";
}
Console.WriteLine(fileContents);
}
How about a general solution?
To me, the first step is to enumerate over the lines of a file (already provided by ReadAllLines, but that has a performance cost due to populating an entire string[] array; there's also ReadLines, but that's only available as of .NET 4.0).
Implementing this is pretty trivial:
public static IEnumerable<string> EnumerateLines(this FileInfo file)
{
using (var reader = file.OpenText())
{
while (!reader.EndOfStream)
{
yield return reader.ReadLine();
}
}
}
The next step is to simply skip the first two lines of this enumerable sequence. This is straightforward using the Skip extension method.
The last step is to ignore the last line of the enumerable sequence. Here's one way you could implement this:
public static IEnumerable<T> IgnoreLast<T>(this IEnumerable<T> source, int ignoreCount)
{
if (ignoreCount < 0)
{
throw new ArgumentOutOfRangeException("ignoreCount");
}
var buffer = new Queue<T>();
foreach (T value in source)
{
if (buffer.Count < ignoreCount)
{
buffer.Enqueue(value);
continue;
}
T buffered = buffer.Dequeue();
buffer.Enqueue(value);
yield return buffered;
}
}
OK, then. Putting it all together, we have:
var file = new FileInfo(#"path\to\file.txt");
var lines = file.EnumerateLines().Skip(2).IgnoreLast(1);
Test input (contents of file):
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is line number 5.
This is line number 6.
This is line number 7.
This is line number 8.
This is line number 9.
This is line number 10.
Output (of Skip(2).IgnoreLast(1)):
This is line number 3.
This is line number 4.
This is line number 5.
This is line number 6.
This is line number 7.
This is line number 8.
This is line number 9.
You can do this:
var valid = new int[] { 3, 4, 5, 6, 7 };
var lines = File.ReadAllLines("file.txt").
Where((line, index) => valid.Contains(index + 1));
Or the opposite:
var invalid = new int[] { 1, 2, 8 };
var lines = File.ReadAllLines("file.txt").
Where((line, index) => !invalid.Contains(index + 1));
If you're looking for a general way to remove the last and the first 2, you can use this:
var allLines = File.ReadAllLines("file.txt");
var lines = allLines
.Take(allLines.Length - 1)
.Skip(2);
But from your example it seems that you're better off looking for the string pattern that you want to read from the file. Try using regexes.