I've been working on my module exercises and I came across this code snippet which reads the text file and prints the details about it.
It's working fine, but I just want to know how to give the path of the text file in the code itself other than giving the path in the command line.
Below is my code.
class Module06
{
public static void Exercise01(string[] args)
{
string fileName = args[0];
FileStream stream = new FileStream(fileName, FileMode.Open);
StreamReader reader = new StreamReader(stream);
int size = (int)stream.Length;
char[] contents = new char[size];
for (int i = 0; i < size; i++)
{
contents[i] = (char)reader.Read();
}
reader.Close();
Summarize(contents);
}
static void Summarize(char[] contents)
{
int vowels = 0, consonants = 0, lines = 0;
foreach (char current in contents)
{
if (Char.IsLetter(current))
{
if ("AEIOUaeiou".IndexOf(current) != -1)
{
vowels++;
}
else
{
consonants++;
}
}
else if (current == '\n')
{
lines++;
}
}
Console.WriteLine("Total no of characters: {0}", contents.Length);
Console.WriteLine("Total no of vowels : {0}", vowels);
Console.WriteLine("Total no of consonants: {0}", consonants);
Console.WriteLine("Total no of lines : {0}", lines);
}
}
In your static void Main, call
string[] args = {"filename.txt"};
Module06.Exercise01(args);
Reading of a text file is much easier with File.ReadAllText then you don't need to think about closing the file you just use it. It accepts file name as parameter.
string fileContent = File.ReadAllText("path to my file");
string fileName = #"path\to\file.txt";
Related
how do i count the line in log file and create a new log files of it?
Below is my log file :
DDD.CGLOG
ID|AFP|DATE|FOLDER
1|DDD|20181204|B
2|DDD|20181104|B
3|DDD|20181004|B
FFF.CGLOG
ID|AFP|DATE|FOLDER
1|FFF|20181204|B
2|FFF|20181104|B
WWW.CGLOG
ID|AFP|DATE|FOLDER
1|WWW|20181204|B
i want to count the line and create a new log file as below :
DDD_QTY.Log
AFP|QTY
DDD|3
EEE_QTY.Log
AFP|QTY
EEE|2
WWW_QTY.Log
AFP|QTY
WWW|1
Below is what i have tried. I have managed to get the count from each log file inside the folder, now i just need to write the count into a new log file using a same name with existing log file.
string[] ori_Files = Directory.GetFiles(#"F:\Work\FLP Code\test", "*.CGLOG*", SearchOption.TopDirectoryOnly);
foreach (var file in ori_Files)
{
using (StreamReader file1 = new StreamReader(file))
{
string line;
int count = 0;
while ((line = file1.ReadLine()) != null)
{
Console.WriteLine(line);
count++;
}
Console.WriteLine(count);
}
}
Console.ReadLine();
Since you only want to count lines, You can keep it simple. Assuming your file name dictates the AFP value
static long CountLinesInFile(string fileName,string outputfile)
{
var afp = Path.GetFileNameWithoutExtension(fileName);
var lineCount = File.ReadAllLines(fileName).Length;
File.WriteAllText(outputfile,$"AFP|QTY{Environment.NewLine}{afp}|{lineCount -1}");
return lineCount-1;
}
Please note you are counting a line less(headers are not counted as in your example). In case the file is different from AFP term, you can use regex to parse the AFP Term from the any line other than the header line in each term. Example Regex for parsing AFP Term
new Regex(#"^[0-9]+\|(?<AFP>[a-zA-Z]+)\|[0-9]+\|[a-zA-Z]+$")
Update
In case your file is pretty large (say 15-20Gb - considering it is a log file), a better approach would be
static long CountLinesInFile(string fileName,string outputFileName)
{
var afp = Path.GetFileNameWithoutExtension(fileName);
uint count = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(fileName))
{
int current;
do
{
current = stream.ReadByte();
if (current == query)
{
count++;
continue;
}
} while (current!= -1);
}
using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputFileName, true))
{
file.WriteLine($"AFP|QTY{Environment.NewLine}{afp}|{count}");
}
return count;
}
Update 2
To invoke the method for all files in a given folder, you can make use DirectoryInfo.GetFiles, for example
DirectoryInfo d = new DirectoryInfo(#"E:\TestFolder");
FileInfo[] Files = d.GetFiles("*.txt");
foreach(FileInfo file in Files )
{
CountLinesInFile(file.FullName,$"{file.FullName}.processed");
}
a simple 2 liner
static void CountLines(string path,sting outfile)
{
var count = File.ReadLines(path).Count();
File.WriteAllText(outfile, $"AFP|QTY{Environment.NewLine}DDD|{count}");
}
I'm back at it again with another question, this time with regards to editing text files. My home work is as follow
Write a program that reads the contents of a text file and inserts the line numbers at the beginning of each line, then rewrites the file contents.
This is what I have so far, though I am not so sure if this is the most efficient way of doing it. I've only started learning on handling text files at the moment.
static void Main(string[] args)
{
string fileName = #"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 3\Chapter 15 Question 3\TextFile1.txt";
StreamReader reader = new StreamReader(fileName);
int lineCounter = 0;
List<string> list = new List<string>();
using (reader)
{
string line = reader.ReadLine();
while (line != null)
{
list.Add("line " + (lineCounter + 1) + ": " + line);
line = reader.ReadLine();
lineCounter++;
}
}
StreamWriter writer = new StreamWriter(fileName);
using (writer)
{
foreach (string line in list)
{
writer.WriteLine(line);
}
}
}
your help would be appreciated!
thanks once again. :]
this should be enough (in case the file is relatively small):
using System.IO;
(...)
static void Main(string[] args)
{
string fileName = #"C:\Users\Nate\Documents\Visual Studio 2015\Projects\Chapter 15\Chapter 15 Question 3\Chapter 15 Question 3\TextFile1.txt";
string[] lines = File.ReadAllLines(fileName);
for (int i = 0; i< lines.Length; i++)
{
lines[i] = string.Format("{0} {1}", i + 1, lines[i]);
}
File.WriteAllLines(fileName, lines);
}
I suggest using Linq, use File.ReadLinesto read the content.
// Read all lines and apply format
var formatteLines = File
.ReadLines("filepath") // read lines
.Select((line, i) => string.Format("line {0} :{1} ", line, i+1)); // format each line.
// write formatted lines to either to the new file or override previous file.
File.WriteAllLines("outputfilepath", formatteLines);
Just one loop here. I think it will be efficient.
class Program
{
public static void Main()
{
string path = Directory.GetCurrentDirectory() + #"\MyText.txt";
StreamReader sr1 = File.OpenText(path);
string s = "";
int counter = 1;
StringBuilder sb = new StringBuilder();
while ((s = sr1.ReadLine()) != null)
{
var lineOutput = counter++ + " " + s;
Console.WriteLine(lineOutput);
sb.Append(lineOutput);
}
sr1.Close();
Console.WriteLine();
StreamWriter sw1 = File.AppendText(path);
sw1.Write(sb);
sw1.Close();
}
I am working on a method that reads txt file with multiple lines and then turns it into a txt file with only single line.
Like this:
first line,
second line,
third line.
Into this:
first line, second line, third line.
I have this code but it only shows "third line." in file:
MAIN:
static void Main(string[] args)
{
string fLine= "first line,\n second line,\n third line.";
string sLine= "";
Console.Write(fLine);
Console.WriteLine();
Console.Write(sLine);
string filDat1 = "D:\\Dat1.txt";
string filDat2 = "D:\\Dat2.txt";
if (File.Exists(filDat1))
Console.WriteLine("File already exist!");
else
{
File.WriteAllText(filDat1, fLine);
}
File.WriteAllText(filDat2, sLine);
changeFile(filDat1);
Console.ReadKey();
}
METHOD:
public static void changeFile(string name)
{
StringBuilder dato;
string filDat2 = "D:\\Dat2.txt";
try
{
string[] lines = File.ReadAllLines(name);
foreach (string line in lines)
{
Console.WriteLine("Words in file: " + line);
}
Console.WriteLine();
Console.Write("New words in new file: ");
foreach (string line in lines)
{
string line1 = line;
dato = new StringBuilder();
line1 = line1.Replace("\n", " ");
for (int i = 0; i < line1.Count(); i++)
{
if (!line1[i].Equals(""))
{
dato.Append(line1[i] + "");
}
}
Console.WriteLine(dato);
File.WriteAllText(filDat2, dato.ToString());
}
}
I don't know what I'm doing wrong. I only get the last word in my case "third word." in new file Dat2.txt.
Also, is there any way to create a method that can take two files. One file to read and one file to write that changed text to it?
Here is a method to make anew file for given file with only 1 line:
public static void changeFile(string InputPath,string OutputPath)
{
List<string> OUTPUT = new List<string>();
StreamReader sr = new StreamReader(InputPath);
while (!sr.EndOfStream)
{
OUTPUT.Add(sr.ReadLine());
}
sr.Close();
StreamWriter fs = new StreamWriter(OutputPath);
string output = "";
foreach (string line in OUTPUT)
{
output += line + " ";
}
fs.WriteLine(output);
fs.Close();
}
In case you were curious as to why your approach didn't work, it was because you reset dato every iteration, just move that outside and away you go, I've also moved the write outside too since you only need to do that once.
dato = new StringBuilder();
foreach (string line in lines)
{
string line1 = line;
line1 = line1.Replace("\n", " ");
for (int i = 0; i < line1.Count(); i++)
{
if (!line1[i].Equals(""))
{
dato.Append(line1[i] + "");
}
}
Console.WriteLine(dato);
}
File.WriteAllText(filDat2, dato.ToString());
I know this has been asked a few times, but I have seen a lot of regex etc., and I'm sure there is another way to do this with just a stream reader/writer. Below is my code. I'm trying to replace "tea" with the word "cabbage". Can somebody help? I believe I have the wrong syntax.
namespace Week_9_Exer_4
{
class TextImportEdit
{
public void EditorialControl()
{
string fileName;
string lineReadFromFile;
Console.WriteLine("");
// Ask for the name of the file to be read
Console.Write("Which file do you wish to read? ");
fileName = Console.ReadLine();
Console.WriteLine("");
// Open the file for reading
StreamReader fileReader = new StreamReader("C:\\Users\\Greg\\Desktop\\Programming Files\\story.txt");
// Read the lines from the file and display them
// until a null is returned (indicating end of file)
lineReadFromFile = fileReader.ReadLine();
Console.WriteLine("Please enter the word you wish to edit out: ");
string editWord = Console.ReadLine();
while (lineReadFromFile != null)
{
Console.WriteLine(lineReadFromFile);
lineReadFromFile = fileReader.ReadLine();
}
String text = File.ReadAllText("C:\\Users\\Greg\\Desktop\\Programming Files\\story.txt");
fileReader.Close();
StreamWriter fileWriter = new StreamWriter("C:\\Users\\Greg\\Desktop\\Programming Files\\story.txt", false);
string newText = text.Replace("tea", "cabbage");
fileWriter.WriteLine(newText);
fileWriter.Close();
}
}
}
If you don't care about memory usage:
string fileName = #"C:\Users\Greg\Desktop\Programming Files\story.txt";
File.WriteAllText(fileName, File.ReadAllText(fileName).Replace("tea", "cabbage"));
If you have a multi-line file that doesn't randomly split words at the end of the line, you could modify one line at a time in a more memory-friendly way:
// Open a stream for the source file
using (var sourceFile = File.OpenText(fileName))
{
// Create a temporary file path where we can write modify lines
string tempFile = Path.Combine(Path.GetDirectoryName(fileName), "story-temp.txt");
// Open a stream for the temporary file
using (var tempFileStream = new StreamWriter(tempFile))
{
string line;
// read lines while the file has them
while ((line = sourceFile.ReadLine()) != null)
{
// Do the word replacement
line = line.Replace("tea", "cabbage");
// Write the modified line to the new file
tempFileStream.WriteLine(line);
}
}
}
// Replace the original file with the temporary one
File.Replace("story-temp.txt", "story.txt", null);
In the end i used this : Hope it can help out others
public List<string> EditorialResponse(string fileName, string searchString, string replacementString)
{
List<string> list = new List<string>();
using (StreamReader reader = new StreamReader(fileName))
{
string line;
while ((line = reader.ReadLine()) != null)
{
line = line.Replace(searchString, replacementString);
list.Add(line);
Console.WriteLine(line);
}
reader.Close();
}
return list;
}
}
class Program
{
static void Main(string[] args)
{
TextImportEdit tie = new TextImportEdit();
List<string> ls = tie.EditorialResponse(#"C:\Users\Tom\Documents\Visual Studio 2013\story.txt", "tea", "cockrel");
StreamWriter writer = new StreamWriter(#"C:\Users\Tom\Documents\Visual Studio 2013\story12.txt");
foreach (string line in ls)
{
writer.WriteLine(line);
}
writer.Close();
}
}
}
Morning,
I'm trying to split a large text file (15,000,000 rows) using StreamReader/StreamWriter. Is there a quicker way?
I tested it with 130,000 rows and it took 2min 40sec which implies 15,000,000 rows will take approx 5hrs which seems a bit excessive.
//Perform split.
public void SplitFiles(int[] newFiles, string filePath, int processorCount)
{
using (StreamReader Reader = new StreamReader(filePath))
{
for (int i = 0; i < newFiles.Length; i++)
{
string extension = System.IO.Path.GetExtension(filePath);
string temp = filePath.Substring(0, filePath.Length - extension.Length)
+ i.ToString();
string FilePath = temp + extension;
if (!File.Exists(FilePath))
{
for (int x = 0; x < newFiles[i]; x++)
{
DataWriter(Reader.ReadLine(), FilePath);
}
}
else
{
return;
}
}
}
}
public void DataWriter(string rowData, string filePath)
{
bool appendData = true;
using (StreamWriter sr = new StreamWriter(filePath, appendData))
{
{
sr.WriteLine(rowData);
}
}
}
Thanks for your help.
You haven't made it very clear, but I'm assuming that the value of each element of the newFiles array is the number of lines to copy from the original into that file. Note that currently you don't detect the situation where there's either extra data at the end of the input file, or it's shorter than expected. I suspect you want something like this:
public void SplitFiles(int[] newFiles, string inputFile)
{
string baseName = Path.GetFileNameWithoutExtension(inputFile);
string extension = Path.GetExtension(inputFile);
using (TextReader reader = File.OpenText(inputFile))
{
for (int i = 0; i < newFiles.Length; i++)
{
string outputFile = baseName + i + extension;
if (File.Exists(outputFile))
{
// Better than silently returning, I'd suggest...
throw new IOException("File already exists: " + outputFile);
}
int linesToCopy = newFiles[i];
using (TextWriter writer = File.CreateText(outputFile))
{
for (int j = 0; i < linesToCopy; j++)
{
string line = reader.ReadLine();
if (line == null)
{
return; // Premature end of input
}
writer.WriteLine(line);
}
}
}
}
}
Note that this still won't detect if there's any unconsumed input... it's not clear what you want to do in that situation.
One option for code clarity is to extract the middle of this into a separate method:
public void SplitFiles(int[] newFiles, string inputFile)
{
string baseName = Path.GetFileNameWithoutExtension(inputFile);
string extension = Path.GetExtension(inputFile);
using (TextReader reader = File.OpenText(inputFile))
{
for (int i = 0; i < newFiles.Length; i++)
{
string outputFile = baseName + i + extension;
// Could put this into the CopyLines method if you wanted
if (File.Exists(outputFile))
{
// Better than silently returning, I'd suggest...
throw new IOException("File already exists: " + outputFile);
}
CopyLines(reader, outputFile, newFiles[i]);
}
}
}
private static void CopyLines(TextReader reader, string outputFile, int count)
{
using (TextWriter writer = File.CreateText(outputFile))
{
for (int i = 0; i < count; i++)
{
string line = reader.ReadLine();
if (line == null)
{
return; // Premature end of input
}
writer.WriteLine(line);
}
}
}
There are utilities for splitting files that may outperform your solution - e.g. search for "split file by line".
If they don't suit, there are solutions for loading all the source file into memory and then writing out the files but that probably isn't appropriate given the size of the source file.
In terms of improving your code, a minor improvement would be the generation of the destination file path (and also clarifying the confusing between the source filePath you use and the destination files). You don't need to re-establish the source file extension each time in your loop.
The second improvement (and probably more significant improvement - as highlighted by commenters) is about how you write out the destination files - these seem to have a differing number of lines from the source (value in each newFiles entry) that you specify you want in individual destination files? So I'd suggest for each entry you read all the source file relevant to the next destination file, then output the destination rather than repeatedly opening a destination file. You could "gather" the lines in a StringBuilder/List etc - alternatively just write them directly out to the destination file (but only opening it once)
public void SplitFiles(int[] newFiles, string sourceFilePath, int processorCount)
{
string sourceDirectory = System.IO.Path.GetDirectoryName(sourceFilePath);
string sourceFileName = System.IO.Path.GetFileNameWithoutExtension(sourceFilePath);
string extension = System.IO.Path.GetExtension(sourceFilePath);
using (StreamReader Reader = new StreamReader(sourceFilePath))
{
for (int i = 0; i < newFiles.Length; i++)
{
string destinationFileNameWithExtension = string.Format("{0}{1}{2}", sourceFileName, i, extension);
string destinationFilePath = System.IO.Path.Combine(sourceDirectory, destinationFileNameWithExtension);
if (!File.Exists(destinationFilePath))
{
// Read all the lines relevant to this destination file
// and temporarily store them in memory
StringBuilder destinationText = new StringBuilder();
for (int x = 0; x < newFiles[i]; x++)
{
destinationText.Append(Reader.ReadLine());
}
DataWriter(destinationFilePath, destinationText.ToString());
}
else
{
return;
}
}
}
}
private static void DataWriter(string destinationFilePath, string content)
{
using (StreamWriter sr = new StreamWriter(destinationFilePath))
{
{
sr.Write(content);
}
}
}
I've recently had to do this for several hundred files under 2 GB each (up to 1.92 GB), and the fastest method I found (if you have the memory available) is StringBuilder. All the other methods I tried were painfully slow.
Please note that this is memory dependent. Adjust "CurrentPosition = 130000" accordingly.
string CurrentLine = String.Empty;
int CurrentPosition = 0;
int CurrentSplit = 0;
foreach (string file in Directory.GetFiles(#"C:\FilesToSplit"))
{
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(file))
{
while ((CurrentLine = sr.ReadLine()) != null)
{
if (CurrentPosition == 130000) // Or whatever you want to split by.
{
using (StreamWriter sw = new StreamWriter(#"C:\FilesToSplit\SplitFiles\" + Path.GetFileNameWithoutExtension(file) + "-" + CurrentSplit + "." + Path.GetExtension(file)))
{
// Append this line too, so we don't lose it.
sb.Append(CurrentLine);
// Write the StringBuilder contents
sw.Write(sb.ToString());
// Clear the StringBuilder buffer, so it doesn't get too big. You can adjust this based on your computer's available memory.
sb.Clear();
// Increment the CurrentSplit number.
CurrentSplit++;
// Reset the current line position. We've found 130,001 lines of text.
CurrentPosition = 0;
}
}
else
{
sb.Append(CurrentLine);
CurrentPosition++;
}
}
}
// Reset the integers at the end of each file check, otherwise it can quickly go out of order.
CurrentPosition = 0;
CurrentSplit = 0;
}