Counting the number of words from a file - c#

A word in this context is defined is a letter or a number. However, something like \n is not considered a word.
Below in my code I am trying to count the number of words in a file but at the for loop's local variable declaration I get the error Null Reference exception.
I am not sure why I get this error. I get the variable Line equal to null which shouldn't happen because the text file DOES have one word "hello world" in it..
StreamReader sr = new StreamReader(filePath);
while (sr.ReadLine()!=null)
{
Line =sr.ReadLine();
for (**int i = 1**; i < (Line.Length+1); i++)
{
if (Char.IsLetterOrDigit(Line[i]) == true && Char.IsLetterOrDigit(Line[i - 1]) == true)
{
if (LetterRecent == false)
{
wordCount = wordCount + 1;
}
LetterRecent = true;
}
else
{
LetterRecent = false;
}
}
}
sr.Close();

You're doing ReadLine() twice for each line.
You could do something like:
count = 0;
while (line = sr.ReadLine()) {
char oldChar = 0;
for (char c in line) {
if (c != oldChar && Char.IsLetterOrDigit(c)) count++;
oldChar = c;
}
}

You are discarding half of the lines in the file by calling sr.ReadLine() twice. If you read the last line of the file in the while statement, the call to Line.Length will throw the null reference exception.
Try something this:
var wordCount = 0;
var line = sr.ReadLine();
while( line != null ) {
for( var i = 1; i < line.Length; i++ ) {
// Count words
}
line = sr.ReadLine();
}

You need to declare wordCount before using it.
Int wordCount = 0;
while (sr.ReadLine()!=null)
{

Update your loop condition to be like this:
while (sr.Peek() >= 0)
{
Line = sr.ReadLine();
}

Related

How can I use indexof and substring to find words in a string?

In the constructor :
var tempFR = File.ReadAllText(file);
GetResults(tempFR);
Then :
private List<string> GetResults(string file)
{
List<string> results = new List<string>();
string word = textBox1.Text;
string[] words = word.Split(new string[] { ",," }, StringSplitOptions.None);
for(int i = 0; i < words.Length; i++)
{
int start = file.IndexOf(words[i], 0);
results.Add(file.Substring(start));
}
return results;
}
words contains in this case 3 words System , public , test
I want to find all the words in file and add them to the list results using indexof and substring.
The way it is now start value is -1 all the time.
To clear some things.
This is a screenshot of the textBox1 :
That is why I'm using two commas to split and get the words.
This screenshot showing the words after split them from the textBox1 :
And this is the file string content :
I want to add to the List results all the words in the file.
When looking at the last screenshot there should be 11 results.
Three time the word using three times the word system five times the word public.
but the variable start is -1
Update :
Tried Barns solution/s but for me it's not working good.
First the code that make a search and then loop over the files and reporting to backgroundworker :
int numberofdirs = 0;
void DirSearch(string rootDirectory, string filesExtension, string[] textToSearch, BackgroundWorker worker, DoWorkEventArgs e)
{
List<string> filePathList = new List<string>();
int numberoffiles = 0;
try
{
filePathList = SearchAccessibleFilesNoDistinct(rootDirectory, null, worker, e).ToList();
}
catch (Exception err)
{
}
label21.Invoke((MethodInvoker)delegate
{
label21.Text = "Phase 2: Searching in files";
});
MyProgress myp = new MyProgress();
myp.Report4 = filePathList.Count.ToString();
foreach (string file in filePathList)
{
try
{
var tempFR = File.ReadAllText(file);
_busy.WaitOne();
if (worker.CancellationPending == true)
{
e.Cancel = true;
return;
}
bool reportedFile = false;
for (int i = 0; i < textToSearch.Length; i++)
{
if (tempFR.IndexOf(textToSearch[i], StringComparison.InvariantCultureIgnoreCase) >= 0)
{
if (!reportedFile)
{
numberoffiles++;
myp.Report1 = file;
myp.Report2 = numberoffiles.ToString();
myp.Report3 = textToSearch[i];
myp.Report5 = FindWordsWithtRegex(tempFR, textToSearch);
backgroundWorker1.ReportProgress(0, myp);
reportedFile = true;
}
}
}
numberofdirs++;
label1.Invoke((MethodInvoker)delegate
{
label1.Text = string.Format("{0}/{1}", numberofdirs, myp.Report4);
label1.Visible = true;
});
}
catch (Exception err)
{
}
}
}
I have the words array already in textToSearch and the file content in tempFR then I'm using the first solution of Barns :
private List<string> FindWordsWithtRegex(string filecontent, string[] words)
{
var res = new List<string>();
foreach (var word in words)
{
Regex reg = new Regex(word);
var c = reg.Matches(filecontent);
int k = 0;
foreach (var g in c)
{
Console.WriteLine(g.ToString());
res.Add(g + ":" + k++);
}
}
Console.WriteLine("Results of FindWordsWithtRegex");
res.ForEach(f => Console.WriteLine(f));
Console.WriteLine();
return res;
}
But the results I'm getting in the List res is not the same output in Barns solution/s this is the results I'm getting the List res for the first file :
In this case two words system and using but it found only the using 3 times but there is also system 3 times in the file content. and the output format is not the same as in the Barns solutions :
Here is an alternative using Regex instead of using IndexOf. Note I have created my own string to parse, so my results will be a bit different.
EDIT
private List<string> FindWordsWithCountRegex(string filecontent, string[] words)
{
var res = new List<string>();
foreach (var word in words)
{
Regex reg = new Regex(word, RegexOptions.IgnoreCase);
var c = reg.Matches(filecontent).Count();
res.Add(word + ":" + c);
}
return res;
}
Simple change this part and use a single char typically a space not a comma:
string[] words = word.Split(' ');
int start = file.IndexOf(words[i],0);
start will be -1 if the word is not found.
MSDN: IndexOf(String, Int32)
for(int i = 0; i < words.Length; i++)
{
int start = file.IndexOf(words[i], 0);
// only add to results if word is found (index >= 0)
if (start >= 0) results.Add(file.Substring(start));
}
If you want all appearance of the words you need an extra loop
int fileLength = file.Length;
for(int i = 0; i < words.Length; i++)
{
int startIdx = 0;
while (startIdx < fileLength ){
int idx = file.IndexOf(words[i], startIdx]);
if (start >= 0) {
// add to results
results.Add(file.Substring(start));
// and let Word-search continue from last found Word Position Ending
startIdx = (start + words.Length);
}
}
int start = file.IndexOf(words[i], 0);
// only add to results if word is found (index >= 0)
if (start >= 0) results.Add(file.Substring(start));
}
MayBe you want a caseinsensitiv search
file.IndexOf(words[i], 0, StringComparison.CurrentCultureIgnoreCase); MSDN: StringComparer Class

How to extract the content of a text file within a scope delimined by a string marker

I have a Console File,
I need to match a string ("Seq Started"), And if I get the string I want to copy all text till I get another string("Seq Ended") in a txt file.
You can use this.
We load the file and parse lines to search the starting and ending deliminers sequences assuming only one of each of them are allowed.
Then if the section is correclty found, we extract the lines of the soure using Linq and we save the result to the desired file.
using System.Linq;
using System.Collections.Generic;
static void Test()
{
string delimiterStart = "Seq Started";
string delimiterEnd = "Seq Ended";
string filenameSource = "c:\\sample source.txt";
string filenameDest = "c:\\sample dest.txt";
var result = new List<string>();
var lines = File.ReadAllLines(filenameSource);
int indexStart = -1;
int indexEnd = -1;
for ( int index = 0; index < lines.Length; index++ )
{
if ( lines[index].Contains(delimiterStart) )
if ( indexStart == -1 )
indexStart = index + 1;
else
{
Console.WriteLine($"Only one \"{delimiterStart}\" is allowed in file {filenameSource}.");
indexStart = -1;
break;
}
if ( lines[index].Contains(delimiterEnd) )
{
indexEnd = index;
break;
}
}
if ( indexStart != -1 && indexEnd != -1 )
{
result.AddRange(lines.Skip(indexStart).Take(indexEnd - indexStart));
File.WriteAllLines(filenameDest, result);
Console.WriteLine($"Content of file \"{filenameSource}\" extracted to file {filenameDest}.");
}
else
{
if ( indexStart == -1 )
Console.WriteLine($"\"{delimiterStart}\" not found in file {filenameSource}.");
if ( indexEnd == -1 )
Console.WriteLine($"\"{delimiterEnd}\" not found in file {filenameSource}.");
}
}
// Read each line of the file into a string array. Each element
// of the array is one line of the file.
string[] lines = System.IO.File.ReadAllLines(#"C:\Users\Public\TestFolder\WriteLines2.txt");
// Display the file contents by using a foreach loop.
System.Console.WriteLine("Contents of WriteLines2.txt = ");
foreach (string line in lines)
{
if(line == "Seq Started" && line != "Seq Ended")
{
//here you get each line in start to end one by one.
//apply your logic here.
}
}
Check the second example in below link-
[https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-read-from-a-text-file][1]

Splitting CSV files with commas in the values [duplicate]

How to split the CSV file in c sharp? And how to display this?
I've been using the TextFieldParser Class in the Microsoft.VisualBasic.FileIO namespace for a C# project I'm working on. It will handle complications such as embedded commas or fields that are enclosed in quotes etc. It returns a string[] and, in addition to CSV files, can also be used for parsing just about any type of structured text file.
Display where? About splitting, the best way is to use a good library to that effect.
This library is pretty good, I can recommend it heartily.
The problems using naïve methods is that the usually fail, there are tons of considerations without even thinking about performance:
What if the text contains commas
Support for the many existing formats (separated by semicolon, or text surrounded by quotes, or single quotes, etc.)
and many others
Import Micorosoft.VisualBasic as a reference (I know, its not that bad) and use Microsoft.VisualBasic.FileIO.TextFieldParser - this handles CSV files very well, and can be used in any .Net language.
read the file one line at a time, then ...
foreach (String line in line.Split(new char[] { ',' }))
Console.WriteLine(line);
This is a CSV parser I use on occasion.
Usage: (dgvMyView is a datagrid type.)
CSVReader reader = new CSVReader("C:\MyFile.txt");
reader.DisplayResults(dgvMyView);
Class:
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
public class CSVReader
{
private const string ESCAPE_SPLIT_REGEX = "({1}[^{1}]*{1})*(?<Separator>{0})({1}[^{1}]*{1})*";
private string[] FieldNames;
private List<string[]> Records;
private int ReadIndex;
public CSVReader(string File)
{
Records = new List<string[]>();
string[] Record = null;
StreamReader Reader = new StreamReader(File);
int Index = 0;
bool BlankRecord = true;
FieldNames = GetEscapedSVs(Reader.ReadLine());
while (!Reader.EndOfStream)
{
Record = GetEscapedSVs(Reader.ReadLine());
BlankRecord = true;
for (Index = 0; Index <= Record.Length - 1; Index++)
{
if (!string.IsNullOrEmpty(Record[Index])) BlankRecord = false;
}
if (!BlankRecord) Records.Add(Record);
}
ReadIndex = -1;
Reader.Close();
}
private string[] GetEscapedSVs(string Data)
{
return GetEscapedSVs(Data, ",", "\"");
}
private string[] GetEscapedSVs(string Data, string Separator, string Escape)
{
string[] Result = null;
int Index = 0;
int PriorMatchIndex = 0;
MatchCollection Matches = Regex.Matches(Data, string.Format(ESCAPE_SPLIT_REGEX, Separator, Escape));
Result = new string[Matches.Count];
for (Index = 0; Index <= Result.Length - 2; Index++)
{
Result[Index] = Data.Substring(PriorMatchIndex, Matches[Index].Groups["Separator"].Index - PriorMatchIndex);
PriorMatchIndex = Matches[Index].Groups["Separator"].Index + Separator.Length;
}
Result[Result.Length - 1] = Data.Substring(PriorMatchIndex);
for (Index = 0; Index <= Result.Length - 1; Index++)
{
if (Regex.IsMatch(Result[Index], string.Format("^{0}[^{0}].*[^{0}]{0}$", Escape))) Result[Index] = Result[Index].Substring(1, Result[Index].Length - 2);
Result[Index] = Result[Index].Replace(Escape + Escape, Escape);
if (Result[Index] == null) Result[Index] = "";
}
return Result;
}
public int FieldCount
{
get { return FieldNames.Length; }
}
public string GetString(int Index)
{
return Records[ReadIndex][Index];
}
public string GetName(int Index)
{
return FieldNames[Index];
}
public bool Read()
{
ReadIndex = ReadIndex + 1;
return ReadIndex < Records.Count;
}
public void DisplayResults(DataGridView DataView)
{
DataGridViewColumn col = default(DataGridViewColumn);
DataGridViewRow row = default(DataGridViewRow);
DataGridViewCell cell = default(DataGridViewCell);
DataGridViewColumnHeaderCell header = default(DataGridViewColumnHeaderCell);
int Index = 0;
ReadIndex = -1;
DataView.Rows.Clear();
DataView.Columns.Clear();
for (Index = 0; Index <= FieldCount - 1; Index++)
{
col = new DataGridViewColumn();
col.CellTemplate = new DataGridViewTextBoxCell();
header = new DataGridViewColumnHeaderCell();
header.Value = GetName(Index);
col.HeaderCell = header;
DataView.Columns.Add(col);
}
while (Read())
{
row = new DataGridViewRow();
for (Index = 0; Index <= FieldCount - 1; Index++)
{
cell = new DataGridViewTextBoxCell();
cell.Value = GetString(Index).ToString();
row.Cells.Add(cell);
}
DataView.Rows.Add(row);
}
}
}
I had got the result for my query. its like simple like i had read a file using io.file. and all the text are stored into a string. After that i splitted with a seperator. The code is shown below.
using System;
using System.Collections.Generic;
using System.Text;
namespace CSV
{
class Program
{
static void Main(string[] args)
{
string csv = "user1, user2, user3,user4,user5";
string[] split = csv.Split(new char[] {',',' '});
foreach(string s in split)
{
if (s.Trim() != "")
Console.WriteLine(s);
}
Console.ReadLine();
}
}
}
The following function takes a line from a CSV file and splits it into a List<string>.
Arguments:
string line = the line to split
string textQualifier = what (if any) text qualifier (i.e. "" or "\"" or "'")
char delim = the field delimiter (i.e. ',' or ';' or '|' or '\t')
int colCount = the expected number of fields (0 means don't check)
Example usage:
List<string> fields = SplitLine(line, "\"", ',', 5);
// or
List<string> fields = SplitLine(line, "'", '|', 10);
// or
List<string> fields = SplitLine(line, "", '\t', 0);
Function:
private List<string> SplitLine(string line, string textQualifier, char delim, int colCount)
{
List<string> fields = new List<string>();
string origLine = line;
char textQual = '"';
bool hasTextQual = false;
if (!String.IsNullOrEmpty(textQualifier))
{
hasTextQual = true;
textQual = textQualifier[0];
}
if (hasTextQual)
{
while (!String.IsNullOrEmpty(line))
{
if (line[0] == textQual) // field is text qualified so look for next unqualified delimiter
{
int fieldLen = 1;
while (true)
{
if (line.Length == 2) // must be final field (zero length)
{
fieldLen = 2;
break;
}
else if (fieldLen + 1 >= line.Length) // must be final field
{
fieldLen += 1;
break;
}
else if (line[fieldLen] == textQual && line[fieldLen + 1] == textQual) // escaped text qualifier
{
fieldLen += 2;
}
else if (line[fieldLen] == textQual && line[fieldLen + 1] == delim) // must be end of field
{
fieldLen += 1;
break;
}
else // not a delimiter
{
fieldLen += 1;
}
}
string escapedQual = textQual.ToString() + textQual.ToString();
fields.Add(line.Substring(1, fieldLen - 2).Replace(escapedQual, textQual.ToString())); // replace escaped qualifiers
if (line.Length >= fieldLen + 1)
{
line = line.Substring(fieldLen + 1);
if (line == "") // blank final field
{
fields.Add("");
}
}
else
{
line = "";
}
}
else // field is not text qualified
{
int fieldLen = line.IndexOf(delim);
if (fieldLen != -1) // check next delimiter position
{
fields.Add(line.Substring(0, fieldLen));
line = line.Substring(fieldLen + 1);
if (line == "") // final field must be blank
{
fields.Add("");
}
}
else // must be last field
{
fields.Add(line);
line = "";
}
}
}
}
else // if there is no text qualifier, then use existing split function
{
fields.AddRange(line.Split(delim));
}
if (colCount > 0 && colCount != fields.Count) // count doesn't match expected so throw exception
{
throw new Exception("Field count was:" + fields.Count.ToString() + ", expected:" + colCount.ToString() + ". Line:" + origLine);
}
return fields;
}
Problem: Convert a comma separated string into an array where commas in "quoted strings,,," should not be considered as separators but as part of an entry
Input:
String: First,"Second","Even,With,Commas",,Normal,"Sentence,with ""different"" problems",3,4,5
Output:
String-Array: ['First','Second','Even,With,Commas','','Normal','Sentence,with "different" problems','3','4','5']
Code:
string sLine;
sLine = "First,\"Second\",\"Even,With,Commas\",,Normal,\"Sentence,with \"\"different\"\" problems\",3,4,5";
// 1. Split line by separator; do not split if separator is within quotes
string Separator = ",";
string Escape = '"'.ToString();
MatchCollection Matches = Regex.Matches(sLine,
string.Format("({1}[^{1}]*{1})*(?<Separator>{0})({1}[^{1}]*{1})*", Separator, Escape));
string[] asColumns = new string[Matches.Count + 1];
int PriorMatchIndex = 0;
for (int Index = 0; Index <= asColumns.Length - 2; Index++)
{
asColumns[Index] = sLine.Substring(PriorMatchIndex, Matches[Index].Groups["Separator"].Index - PriorMatchIndex);
PriorMatchIndex = Matches[Index].Groups["Separator"].Index + Separator.Length;
}
asColumns[asColumns.Length - 1] = sLine.Substring(PriorMatchIndex);
// 2. Remove quotes
for (int Index = 0; Index <= asColumns.Length - 1; Index++)
{
if (Regex.IsMatch(asColumns[Index], string.Format("^{0}[^{0}].*[^{0}]{0}$", Escape))) // If "Text" is sourrounded by quotes (but ignore double quotes => "Leave ""inside"" quotes")
{
asColumns[Index] = asColumns[Index].Substring(1, asColumns[Index].Length - 2); // "Text" => Text
}
asColumns[Index] = asColumns[Index].Replace(Escape + Escape, Escape); // Remove double quotes ('My ""special"" text' => 'My "special" text')
if (asColumns[Index] == null) asColumns[Index] = "";
}
The output array is asColumns

Use the output(StreamReader) line as file name(rename)

I am trying to use the output("line")as a new file name(files already exist, just rename).
e.g. "A,tampqer,n:.jpg"
but it looks like:
e.g. line ="A\tampqer\tn:\t\t"
I get an error: illegal character for the file name.
int counter = 0;
string line;
// Read the file and display it line by line.
StreamReader file = new StreamReader(#"C:\\german-czech.txt");
while ((line = file.ReadLine()) != null)
{
//replace /t doesnt work
for (int i = 0; i < line.Length; i++)
{
if (line[i] == '\t')
{
line.Replace(line[i], ',');
}
}
//Console.WriteLine(string.Format(line, #"\\t").ToString());
Console.WriteLine( line);
counter++;
if (counter == 100)
break;
}
file.Close();
And guess what the content of the "line" is still the same ... can anyone help me up with this little problem ?
what else did I try ?
line.Replace(System.Environment.NewLine, ",");
string.empty...
"\n"
Best regards
You need
line = line.Replace(line[i], ',');
It doesn't change it in place. It returns a new string.
You also don't need to transverse the string one by one. Replace will replace every occurrence in the string. So instead of your:
//replace /t doesnt work
for (int i = 0; i < line.Length; i++)
{
if (line[i] == '\t')
{
line.Replace(line[i], ',');
}
}
Just have:
line = line.Replace("\t", ',');
(This is correcting your code. But I think mrzli in his comment got the real error.)

c# Get array index id

Not sure if am on the right track but I want to find a index id of an array and check if it's equal to another id then display a message. it not working for some reason.
int _findID = 1;
using (StreamReader reader = new StreamReader("textfile.txt"))
{
string line = reader.ReadLine();
while (line != null)
{
string[] array = {line};
for (int i = 0; i < array.Length; i++)
{
if (array[i] == findID)
{
MesssageBox.show("Line found!!")
}
}
}
}
Any help appreciated
try
if(int.Parse(array[i]) == findID)
instead of
if (array[i] == findID)
You have to convert your string representation of your number to a int and then compare both int's.
this is even shorter than iterating over an array which always has 1 element:
while (line != null) {
if(int.Parse(array[i]) == findID)
MesssageBox.Show("Line found!!")
}
EDIT
Try
int _findID = 1;
List<String> names = new List<string>()
using (StreamReader reader = new StreamReader("textfile.txt"))
{
string line = reader.ReadLine();
int lineCount = 0;
while (line != null)
{
lineCount++;
names.Add(line);
if (lineCount == _findID)
MessageBox.Show("Line found!!");
}
}
#user1285872: array.Length this will give array length 1 in your case when you check if (array[i] == findID) thi will be (0==findID) if the value of findID ==0 then message will show.
Your code has various problems:
First if the file is not empty you have an endless loop as you read the line once and always check the same line:
string line = reader.ReadLine();
while (line != null) {}
The array is always 1 item long as you create it in the loop itself.
But for the problem with the lineID, what exactly is the content of such a line?

Categories

Resources