I have some files which I receive and don't know how many columns and rows I have in those files.
How can I make a generic loader with a generic model where I can always load the file but get different content?
I wrote this but know I know its not always the same table...
public override List<Object> getFile(string ab) {
if (ab == "A") {
ab = A;
} else {
ab = B;
}
List<FundPriceModel> models = new List<FundPriceModel>();
FileStream filestream = new FileStream(ab, FileMode.Open, FileAccess.Read);
using (StreamReader reader = new StreamReader(filestream, Encoding.UTF8)) {
string line;
bool isHeader = true;
while ((line = reader.ReadLine()) != null) {
FundPriceModel model = new FundPriceModel();
if (isHeader) {
headers = line.Split(spliter[0]);
isHeader = false;
continue;
}
string[] attributes = line.Split(spliter);
model.LipperID = IfEmptyInt(attributes[0]);
model.PriceDate = IfEmptyDateTime(attributes[1]);
model.PriceCode = safeValue(attributes[2], v => v[0]);
model.PriceType = safeValue(attributes[3], v => v[0]);
model.PriceCurrency = safeValue(attributes[4], v => attributes[4]);
model.PriceValueLC = IfEmptyFloat(attributes[5]);
model.Estimate = safeValue(attributes[6], v => v[0]);
Console.WriteLine(model.LipperID + "\t" + model.PriceDate + "\t" + model.PriceCode + "\t" + model.PriceType +
"\t" + model.PriceCurrency + "\t" + model.PriceValueLC + "\t" + model.Estimate);
models.Add(model);
}
}
}
If a DataTable is generic enough you could use that, its not really a 'Model'. This code is not production worthy and only demonstrates how to load from a CSV.
private static void Main(string[] args)
{
string filePath = #"C:\temp\tesst.csv";
FileStream filestream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
DataTable inputTable = new DataTable();
using (StreamReader reader = new StreamReader(filestream, Encoding.UTF8))
{
//Get headers and add columns
string headers = reader.ReadLine();
foreach (var s in headers.Split(','))
{
inputTable.Columns.Add(s, typeof (string));
}
//Add rows
string line;
while ((line = reader.ReadLine()) != null)
{
int colIndex = 0;
DataRow dr = inputTable.NewRow();
foreach (var s in line.Split(','))
{
dr[colIndex] = s;
colIndex++;
}
inputTable.Rows.Add(dr);
}
}
}
Related
I would like to read from a text file in the Internet the certain assignment to a word.
In the output "content" I get the complete content of the text file.
But I only want v7.7.3 from the line: version = "v7.7.3".
How can I filter by version with the streamreader?
That is the LastVersion.txt file:
[general]
version = "v7.7.3"
messagenew = "Works with June 2018 Update!\n Plus new Smart Farm strategy\n New Siege Machines\n For more information, go to \n https://mybot.run \n Always free and open source."
messageold = "A new version of MyBot (v7.7.3) is available!\nPlease download the latest from:\nhttps://mybot.run"
Updated: That's my current code.
public string myBotNewVersionURL = "https://raw.githubusercontent.com/MyBotRun/MyBot/master/LastVersion.txt";
public string myBotDownloadURL = null;
public string userDownloadFolder = #"C:\Users\XXX\Download\";
public string newMyBotVersion = null;
public string currentMyBotVersion = null;
public string currentMyBotFileName = null;
public string currentMyBotPath = null;
public void Btn_checkUpdate_Click(object sender, EventArgs e)
{
OpenFileDialog openCurrentMyBot = new OpenFileDialog();
openCurrentMyBot.Title = "Choose MyBot.run.exe";
openCurrentMyBot.Filter = "Application file|*.exe";
openCurrentMyBot.InitialDirectory = userDownloadFolder;
if (openCurrentMyBot.ShowDialog() == DialogResult.OK)
{
MyBot_set.SetValue("mybot_path", Path.GetDirectoryName(openCurrentMyBot.FileName));
MyBot_set.SetValue("mybot_exe", Path.GetFullPath(openCurrentMyBot.FileName));
string latestMyBotPath = Path.GetFullPath(openCurrentMyBot.FileName);
var latestMyBotVersionInfo = FileVersionInfo.GetVersionInfo(latestMyBotPath);
currentMyBotVersion = "v" + latestMyBotVersionInfo.FileVersion;
MyBot_set.SetValue("mybot_version", currentMyBotVersion);
WebClient myBotNewVersionClient = new WebClient();
Stream stream = myBotNewVersionClient.OpenRead(myBotNewVersionURL);
StreamReader reader = new StreamReader(stream);
String content = reader.ReadToEnd();
var sb = new StringBuilder(content.Length);
foreach (char i in content)
{
if (i == '\n')
{
sb.Append(Environment.NewLine);
}
else if (i != '\r' && i != '\t')
sb.Append(i);
}
content = sb.ToString();
var vals = content.Split(
new[] { Environment.NewLine },
StringSplitOptions.None
)
.SkipWhile(line => !line.StartsWith("[general]"))
.Skip(1)
.Take(1)
.Select(line => new
{
Key = line.Substring(0, line.IndexOf('=')),
Value = line.Substring(line.IndexOf('=') + 1).Replace("\"", "").Replace(" ", "")
});
newMyBotVersion = vals.FirstOrDefault().Value;
}
Read From local
var vals = File.ReadLines("..\\..\\test.ini")
.SkipWhile(line => !line.StartsWith("[general]"))
.Skip(1)
.Take(1)
.Select(line => new
{
Key = line.Substring(0, line.IndexOf('=')),
Value = line.Substring(line.IndexOf('=') + 1)
});
Console.WriteLine("Key : " + vals.FirstOrDefault().Key +
" Value : " + vals.FirstOrDefault().Value);
Updated
for read from Git , File.ReadLines not work with URL.
string myBotNewVersionURL = "https://raw.githubusercontent.com/MyBotRun/MyBot/master/LastVersion.txt";
WebClient myBotNewVersionClient = new WebClient();
Stream stream = myBotNewVersionClient.OpenRead(myBotNewVersionURL);
StreamReader reader = new StreamReader(stream);
String content = reader.ReadToEnd();
var sb = new StringBuilder(content.Length);
foreach (char i in content)
{
if (i == '\n')
{
sb.Append(Environment.NewLine);
}
else if (i != '\r' && i != '\t')
sb.Append(i);
}
content = sb.ToString();
var vals = content.Split(
new[] { Environment.NewLine },
StringSplitOptions.None
)
.SkipWhile(line => !line.StartsWith("[general]"))
.Skip(1)
.Take(1)
.Select(line => new
{
Key = line.Substring(0, line.IndexOf('=')),
Value = line.Substring(line.IndexOf('=') + 1)
});
Console.WriteLine("Key : " + vals.FirstOrDefault().Key + " Value : " + vals.FirstOrDefault().Value);
I would like to ask some tips and help on a reading/writing part of my C#.
Situation:
I have to read a CSV file; - OK
If the CSV file name starts with "Load_", I want to write on another CSV the data from line 2 to the last one;
If the CSV file name starts with "RO_", I want to write on 2 different CSVs, 1 with the line 1 to 4 and the other 4 to the last one;
What I have so far is:
public static void ProcessFile(string[] ProcessFile)
{
// Keeps track of your current position within a record
int wCurrLine = 0;
// Number of rows in the file that constitute a record
const int LINES_PER_ROW = 1;
int ctr = 0;
foreach (string filename in ProcessFile)
{
var sbText = new System.Text.StringBuilder(100000);
int stop_line = 0;
int start_line = 0;
// Used for the output name of the file
var dir = Path.GetDirectoryName(filename);
var fileName = Path.GetFileNameWithoutExtension(filename);
var ext = Path.GetExtension(filename);
var folderbefore = Path.GetFullPath(Path.Combine(dir, #"..\"));
var lineCount = File.ReadAllLines(#filename).Length;
string outputname = folderbefore + "output\\" + fileName;
using (StreamReader Reader = new StreamReader(#filename))
{
if (filename.Contains("RO_"))
{
start_line = 1;
stop_line = 5;
}
else
{
start_line = 2;
stop_line = lineCount;
}
ctr = 0;
while (!Reader.EndOfStream && ctr < stop_line)
{
// Add the text
sbText.Append(Reader.ReadLine());
// Increment our current record row counter
wCurrLine++;
// If we have read all of the rows for this record
if (wCurrLine == LINES_PER_ROW)
{
// Add a line to our buffer
sbText.AppendLine();
// And reset our record row count
wCurrLine = 0;
}
ctr++;
} // end of the while
}
int total_lenght = sbText.Length
// When all of the data has been loaded, write it to the text box in one fell swoop
using (StreamWriter Writer = new StreamWriter(dir + "\\" + "output\\" + fileName + "_out" + ext))
{
Writer.Write.(sbText.);
}
} // end of the foreach
} // end of ProcessFile
I was thinking about using the IF/ELSE: "using (StreamWriter Writer = new StreamWriter(dir + "\" + "output\" + fileName + "_out" + ext))" part. However, I am not sure how to pass, to StreamWriter, to only write from/to a specific line number.
Any Help is welcome! If I am missing some information, please, let me know (I am pretty new on stackoverflow).
Thank you.
Code is way too complicated
using System.Collections.ObjectModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication57
{
class Program
{
static void Main(string[] args)
{
}
public static void ProcessFile(string[] ProcessFile)
{
foreach (string filename in ProcessFile)
{
// Used for the output name of the file
var dir = Path.GetDirectoryName(filename);
var fileName = Path.GetFileNameWithoutExtension(filename);
var ext = Path.GetExtension(filename);
var folderbefore = Path.GetFullPath(Path.Combine(dir, #"..\"));
var lineCount = File.ReadAllLines(#filename).Length;
string outputname = folderbefore + "output\\" + fileName;
using (StreamWriter Writer = new StreamWriter(dir + "\\" + "output\\" + fileName + "_out" + ext))
{
int rowCount = 0;
using (StreamReader Reader = new StreamReader(#filename))
{
rowCount++;
string inputLine = "";
while ((inputLine = Reader.ReadLine()) != null)
{
if (filename.Contains("RO_"))
{
if (rowCount <= 4)
{
Writer.WriteLine(inputLine);
}
if (rowCount == 4) break;
}
else
{
if (rowCount >= 2)
{
Writer.WriteLine(inputLine);
}
}
} // end of the while
Writer.Flush();
}
}
} // end of the foreach
} // end of ProcessFile
}
}
You can use LINQ to Take and Skip lines.
public abstract class CsvProcessor
{
private readonly IEnumerable<string> processFiles;
public CsvProcessor(IEnumerable<string> processFiles)
{
this.processFiles = processFiles;
}
protected virtual IEnumerable<string> GetAllLinesFromFile(string fileName)
{
using(var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
using(var reader = new StreamReader(stream))
{
var line = String.Empty;
while((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
protected virtual void ProcessFiles()
{
var sb1 = new StringBuilder();
var sb2 = new StringBuilder();
foreach(var file in this.processFiles)
{
var fileName = Path.GetFileNameWithoutExtension(file);
var lines = GetAllLinesFromFile(file);
if(fileName.StartsWith("RO_", StringComparison.InvariantCultureIgnoreCase))
{
sb1.AppendLine(lines.Take(4)); //take only the first four lines
sb2.AppendLine(lines.Skip(4).TakeWhile(s => !String.IsNullOrEmpty(s))); //skip the first four lines, take everything else
}
else if(fileName.StartsWith("Load_", StringComparison.InvariantCultureIgnoreCase)
{
sb2.AppendLine(lines.Skip(1).TakeWhile(s => !String.IsNullOrEmpty(s)));
}
}
// now write your StringBuilder objects to file...
}
protected virtual void WriteFile(StringBuilder sb1, StringBuilder sb2)
{
// ... etc..
}
}
I have a csv file, and I need to add a unique ID based on the first two characters of the file. I have the following code:
using (StreamReader sr = new StreamReader(f))
{
string currentLine;
int id = 0;
while ((currentLine = sr.ReadLine()) != null)
{
string row = currentLine.ToString();
string FirstTwoCharacters = currentLine.Substring(0, 2);
if (FirstTwoCharacters == "01")
{
id = id + 1;
row += "*" + id.ToString();
using (StreamWriter files = File.AppendText(dir + newfilename))
{
files.WriteLine(row);
}
}
else
{
row += "*" + id.ToString();
using (StreamWriter files = File.AppendText(dir + newfilename))
{
files.WriteLine(row);
}
}
}
}
The csv files can be huge, 1Gb in size, around 6 million rows. Just wanted advice, if there is a quicker way to handling this, as it currently can take 3+ hours to process a file, and multiple files can be received in one go.
Instead of opening new file for appending line for each line of input file you can keep stream writer opened:
using (StreamReader sr = new StreamReader(f))
using (StreamWriter files = File.AppendText(dir + newfilename))
{
string currentLine;
int id = 0;
while ((currentLine = sr.ReadLine()) != null)
{
string firstTwoCharacters = currentLine.Substring(0, 2);
if (firstTwoCharacters == "01")
id++;
files.WriteLine(currentLine + "*" + id);
}
}
You can also use File.ReadLines to enumerate source lines:
using (StreamWriter writer = File.AppendText(dir + newfilename))
{
int id = 0;
foreach(var line in File.ReadLines(f))
{
if (line.Substring(0,2) == "01")
id++;
writer.WriteLine($"{line}*{id}");
}
}
Or even LINQ approach
int id = 0;
var newLines = from line in File.ReadLines(f)
let incrementId = line.Substring(0,2) == "01"
select $"{line}*{incrementId ? (++id) : id}";
File.WriteAllLines(dir + newfilename, newLines);
opening the (File.AppendText) inside the while loop is costly, move this to outside the while
using (StreamReader sr = new StreamReader(f))
{
string currentLine;
int id = 0;
using (StreamWriter files = File.AppendText(dir + newfilename))
{
while ((currentLine = sr.ReadLine()) != null)
{
string row = currentLine.ToString();
string FirstTwoCharacters = currentLine.Substring(0, 2);
if (FirstTwoCharacters == "01")
{
id = id + 1;
row += "*" + id.ToString();
files.WriteLine(row);
}
else
{
row += "*" + id.ToString();
files.WriteLine(row);
}
}
}
}
I have for like 40 rows on My DataTable Displayed in a DataGridView
i'm confused why my method Saves Only One Row in the TextFile :
private void SaveBtn_Click(object sender, EventArgs e)
{
String outputFile;
List<String> ListData = new List<String>();
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "Txt File|*.Txt";
if (sfd.ShowDialog() != DialogResult.OK)
return;
outputFile = sfd.FileName;
}
DataTable tb = pw.SavedInfo(User_info.UserID);
for (int i = 0; i < tb.Rows.Count; i++)
{
ListData.Add("Name==> " + tb.Rows[i][1].ToString() + " LastName ==> " + tb.Rows[i][2].ToString() + " Email ==> " + tb.Rows[i][3].ToString() );
}
foreach (String s in ListData)
{
using (TextWriter Tw = new StreamWriter(outputFile))
{
Tw.WriteLine(s);
}
}
}
Did i missed something ? cause it was a really long day to keep being focused
Use the same StreamWriter:
using (TextWriter Tw = new StreamWriter(outputFile))
{
foreach (String s in ListData)
{
Tw.WriteLine(s);
}
}
or use the constructor that takes a bool for "append":
foreach (String s in ListData)
{
using (TextWriter Tw = new StreamWriter(outputFile, true))
{
Tw.WriteLine(s);
}
}
File.WriteAllLines(outputFile, lisData);
Use this to write in the file. File.WriteAllLines Documentation
I'm trying to read some files with ReadLine, but my file have some break lines that I need to catch (not all of them), and I don't know how to get them in the same array, neither in any other array with these separators... because... ReadLine reads lines, and break these lines, huh?
I can't replace these because I need to check it after the process, so I need to get the breaklines AND the content after that. That's the problem. How can I do that?
Here's my code:
public class ReadFile
{
string extension;
string filename;
System.IO.StreamReader sr;
public ReadFile(string arquivo, System.IO.StreamReader sr)
{
string ext = Path.GetExtension(arquivo);
sr = new StreamReader(arquivo, System.Text.Encoding.Default);
this.sr = sr;
this.extension = ext;
this.filename = Path.GetFileNameWithoutExtension(arquivo);
if (ext.Equals(".EXP", StringComparison.OrdinalIgnoreCase))
{
ReadEXP(arquivo);
}
else MessageBox.Show("Extensão de arquivo não suportada: "+ext);
}
public void ReadEXP(string arquivo)
{
string line = sr.ReadLine();
string[] words;
string[] Separators = new string[] { "<Segment>", "</Segment>", "<Source>", "</Source>", "<Target>", "</Target>" };
string ID = null;
string Source = null;
string Target = null;
DataBase db = new DataBase();
//db.CreateTable_EXP(filename);
db.CreateTable_EXP();
while ((line = sr.ReadLine()) != null)
{
try
{
if (line.Contains("<Segment>"))
{
ID = "";
words = line.Split(Separators, StringSplitOptions.None);
ID = words[0];
for (int i = 1; i < words.Length; i++ )
ID += words[i];
MessageBox.Show("Segment[" + words.Length + "]: " + ID);
}
if (line.Contains("<Source>"))
{
Source = "";
words = line.Split(Separators, StringSplitOptions.None);
Source = words[0];
for (int i = 1; i < words.Length; i++)
Source += words[i];
MessageBox.Show("Source[" + words.Length + "]: " + Source);
}
if (line.Contains("<Target>"))
{
Target = "";
words = line.Split(Separators, StringSplitOptions.None);
Target = words[0];
for (int i = 1; i < words.Length; i++)
Target += words[i];
MessageBox.Show("Target[" + words.Length + "]: " + Target);
db.PopulateTable_EXP(ID, Source, Target);
MessageBox.Show("ID: " + ID + "\nSource: " + Source + "\nTarget: " + Target);
}
}
catch (IndexOutOfRangeException e)
{
MessageBox.Show(e.Message.ToString());
MessageBox.Show("ID: " + ID + "\nSource: " + Source + "\nTarget: " + Target);
}
}
return;
}
If you are trying to read XML, try using the built in libaries, here is a simple example of loading a section of XML with <TopLevelTag> in it.
var xmlData = XDocument.Load(#"C:\folder\file.xml").Element("TopLevelTag");
if (xmlData == null) throw new Exception("Failed To Load XML");
Here is a tidy way to get content without it throwing an exception if missing from the XML.
var xmlBit = (string)xmlData.Element("SomeSubTag") ?? "";
If you really have to roll your own, then look at examples for CSV parsers,
where ReadBlock can be used to get the raw data including line breaks.
private char[] chunkBuffer = new char[4096];
var fileStream = new System.IO.StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
var chunkLength = fileStream.ReadBlock(chunkBuffer, 0, chunkBuffer.Length);