Split Text file with fixed width with different sizes - c#

I am stuck on a problem where I need to split a file with fixed width. Each field can be identified by its first character.
The file contains multiple format for example, the first row's format is {1, 11, 12} while second row's format is {1, 10, 12}. Both are identified with the first character.
AFirstField SecondFields
BBField SecondFields
However, sometimes a row can come with less characters like below.
AFirstField S
What I have tried so far is using text parser getting the current line and check the first character to decide the format, but the application falls over because sometimes rows have less data on them, like the A example above.
string[] data;
using (TextFieldParser myReader = new TextFieldParser(filePath))
{
myReader.TextFieldType = FieldType.FixedWidth;
while (!myReader.EndOfData)
{
currentLine = myReader.ReadLine();
recordType = currentLine[0].ToString();
if (!recordType.Equals("H"))
{
myReader.FieldWidths = returnLineFormat();
myReader.HasFieldsEnclosedInQuotes = true;
data = myReader.ReadFields();
//if (recordType.Equals("R"))
//{
// ISD.Add(data);
//}
}
}
}
private int[] returnLineFormat()
{
int[] format = null;
if ((recordType == "A"))
{
format = new int[] { 1, 11, 12};
}
else if ((recordType == "B"))
{
format = new int[] { 1, 10, 12};
}
return format;
}
These are the errors I am getting cause of row having less stuff:
Line 3 cannot be parsed using the current FieldWidths.
Is there a way around this problem?

Try this. It should work
static int[] returnLineFormat(string recordType)
{
int[] format = null;
if ((recordType == "A"))
{
format = new int[] { 1, 11, 12 };
}
else if ((recordType == "B"))
{
format = new int[] { 1, 10, 12 };
}
return format;
}
static void Main(string[] args)
{
string[] data;
using (TextFieldParser myReader = new TextFieldParser(#"TextParserExample.txt"))
{
myReader.TextFieldType = FieldType.FixedWidth;
while (!myReader.EndOfData)
{
var recordType = myReader.PeekChars(1);
if (!recordType.Equals("H"))
{
var lineLength = myReader.PeekChars(1000).Length;
var currentLine = myReader.ReadLine();
var lengths = returnLineFormat(recordType);
lengths[2] = lineLength - (lengths[0] + lengths[1]);
myReader.FieldWidths = lengths;
myReader.HasFieldsEnclosedInQuotes = true;
data = myReader.ReadFields();
}
}
}
}

Related

How to write a method in C# to write in a textbox according to the size of data (quantity of registries in database)

I have a working code that has to many lines of repeated code. I would like to write a method to save lines of code.
I have a query that gets the number of registries in the database. The number of registries may vary from 1 to 20. The number of registries is saved in the decimal numberOfLines.
Today I am using 20 if's, that writes 4 textboxes per line for each possible number of lines I have.
If I have only one line it write 1 line of 4 textboxes, if I have 2 lines it writes 2 lines of 4 textboxes and so one.
I will only show the code for the quantity of lines from 1 to 4 just to save space (the rest of the code is just copy/paste and change the textboxes new indexes).
//write the lines according to the number of lines
if (numberOfLines == 1)
{
txt_A1.Text = tableAs.Rows[0][0].ToString();
txt_B1.Text = tableAs.Rows[0][1].ToString();
txt_C1.Text = tableAs.Rows[0][2].ToString();
txt_D1.Text = tableAs.Rows[0][3].ToString();
}
if (numberOfLines ==2)
{
txt_A1.Text = tableAs.Rows[0][0].ToString();
txt_B1.Text = tableAs.Rows[0][1].ToString();
txt_C1.Text = tableAs.Rows[0][2].ToString();
txt_D1.Text = tableAs.Rows[0][3].ToString();
txt_A2.Text = tableAs.Rows[1][0].ToString();
txt_B2.Text = tableAs.Rows[1][1].ToString();
txt_C2.Text = tableAs.Rows[1][2].ToString();
txt_D2.Text = tableAs.Rows[1][3].ToString();
}
if (numberOfLines == 3)
{
txt_A1.Text = tableAs.Rows[0][0].ToString();
txt_B1.Text = tableAs.Rows[0][1].ToString();
txt_C1.Text = tableAs.Rows[0][2].ToString();
txt_D1.Text = tableAs.Rows[0][3].ToString();
txt_A2.Text = tableAs.Rows[1][0].ToString();
txt_B2.Text = tableAs.Rows[1][1].ToString();
txt_C2.Text = tableAs.Rows[1][2].ToString();
txt_D2.Text = tableAs.Rows[1][3].ToString();
txt_A3.Text = tableAs.Rows[2][0].ToString();
txt_B3.Text = tableAs.Rows[2][1].ToString();
txt_C3.Text = tableAs.Rows[2][2].ToString();
txt_D3.Text = tableAs.Rows[2][3].ToString();
}
if (numberOfLines == 4)
{
txt_A1.Text = tableAs.Rows[0][0].ToString();
txt_B1.Text = tableAs.Rows[0][1].ToString();
txt_C1.Text = tableAs.Rows[0][2].ToString();
txt_D1.Text = tableAs.Rows[0][3].ToString();
txt_A2.Text = tableAs.Rows[1][0].ToString();
txt_B2.Text = tableAs.Rows[1][1].ToString();
txt_C2.Text = tableAs.Rows[1][2].ToString();
txt_D2.Text = tableAs.Rows[1][3].ToString();
txt_A3.Text = tableAs.Rows[2][0].ToString();
txt_B3.Text = tableAs.Rows[2][1].ToString();
txt_C3.Text = tableAs.Rows[2][2].ToString();
txt_D3.Text = tableAs.Rows[2][3].ToString();
txt_A4.Text = tableAs.Rows[3][0].ToString();
txt_B4.Text = tableAs.Rows[3][1].ToString();
txt_C4.Text = tableAs.Rows[3][2].ToString();
txt_D4.Text = tableAs.Rows[3][3].ToString();
}
With the amount of possible lines I have (20) the code gets very big, and not as beautiful as I expected (with the use of a method for example).
I write a method that you could create textboxes according to the line.
Code:
private void button1_Click(object sender, EventArgs e)
{
int a = Convert.ToInt32(richTextBox1.Text);
CreateTextbox(a);
}
public void CreateTextbox(int line)
{
int count = 0;
int num = 0;
for (int i = 0; i < line*4; i++)
{
TextBox box = new TextBox();
box.Name = "A" + i.ToString();
if (count >= 4)
{
count = 0;
num++;
}
box.Location = new Point(count*(box.Width+20),num*40);
count++;
this.Controls.Add(box);
}
}
}
Try this refactoring:
var items = new Dictionary<int, List<TextBox>>()
{
{ 1, new List<TextBox>() { txt_A1, txt_B1, txt_C1, txt_D1 } },
{ 2, new List<TextBox>() { txt_A1, txt_B1, txt_C1, txt_D1, txt_A2, txt_B2, txt_C2, txt_D2 } },
{ 3, new List<TextBox>() { txt_A1, txt_B1, txt_C1, txt_D1, txt_A2, txt_B2, txt_C2, txt_D2, txt_A3, txt_B3, txt_C3, txt_D3 } },
{ 4, new List<TextBox>() { txt_A1, txt_B1, txt_C1, txt_D1, txt_A2, txt_B2, txt_C2, txt_D2, txt_A3, txt_B3, txt_C3, txt_D3, txt_A4, txt_B4, txt_C4, txt_D4 } }
};
int index1 = 0;
int index2 = 0;
foreach ( var item in items[numberOfLines] )
{
item.Text = tableAs.Rows[index1][index2].ToString();
if ( ++index2 > 3 )
{
index2 = 0;
index1++;
}
}

Text file to two string arrays in wpf using streamreader

I'm trying to read a text file to two string arrays. Array1 is to be all the odd lines, array2 all the even lines. I then add all the items of array1 to a combobox and when that is selected, or as it gets typed, outputs array2 to a textbox.
So far, I have tried a few methods from here, but the big issue seems to be creating the arrays. I tried to get help here before, but the answers didn't actually answer my question. They must be arrays, not lists (which I tried and worked well). I am really confused by this whole thing and my attempted code is now rubbish:
private void ReadFile(string filePath, string customerPhone, string customerName)
{
string line = string.Empty;
var fileSR = new StreamReader(filePath);
bool number = true;
while((line = fileSR.ReadLine()) != null)
{
if (number)
{
customerPhone(line);
number = false;
}
else
{
customerName(line);
number = true;
}
}
fileSR.Close();
}
I'm losing confidence in this whole process, but I need to find a way to make it work, then I can learn why it does.
You are almost there, just use the List<string>.
private void ReadFile(string filePath, string customerPhone, string customerName)
{
string line = string.Empty;
using (var fileSR = new StreamReader(filePath))
{
bool number = true;
List<string> customerPhone = new List<string>();
List<string> customerName = new List<string>();
while((line = fileSR.ReadLine()) != null)
{
if (number)
{
customerPhone.Add(line);
number = false;
}
else
{
customerName.Add(line);
number = true;
}
}
fileSR.Close();
}
}
If you are interested only in Arrays, you could simply call customerName.ToArray() to convert it to an array.
Linq Solution
Alternatively you could use Linq and do this.
var bothArrays = File.ReadLines("filepath") // Read All lines
.Select((line,index) => new {line, index+1}) // index each line
.GroupBy(x=> x/2) // Convert into two groups
.SelectMany(x=> x.Select(s=>s.line).ToArray()) // Convert it to array
.ToArray();
You should use collections to return data, say IList<String>:
private static void ReadFile(String filePath,
IList<String> oddLines,
IList<String> evenLines) {
oddLines.Clear();
evenLines.Clear();
int index = 1; //TODO: start with 0 or with 1
foreach (String line in File.ReadLines(filePath)) {
if (index % 2 == 0)
evenLines.Add(line);
else
oddLines.Add(line);
index += 1;
}
}
using
List<String> names = new List<String>();
List<String> phones = new List<String>();
ReadFile(#"C:\MyDate.txt", names, phones);
// If you want array representation
String[] myNames = names.ToArray();
String[] myPhones = phones.ToArray();
// Let's print out names
Console.Write(String.Join(Envrironment.NewLine, names));
Please, notice, that using File.ReadLines usually more convenient than StreamReader which should be wrapped in using:
// foreach (String line in File.ReadLines(filePath)) equals to
using (var fileSR = new StreamReader(filePath)) {
while ((line = fileSR.ReadLine()) != null) {
...
}
}
This worked! I have these class level strings:
string cFileName = "customer.txt";
string[] cName = new string[0];
string[] cPhone = new string[0];
And then this in the Window Loaded event, but could be used in it's own method:
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
//read file on start
int counter = 0;
string line;
StreamReader custSR = new StreamReader(cFileName);
line = custSR.ReadLine();
while (custSR.Peek() != -1)
{
Array.Resize(ref cPhone, cPhone.Length + 1);
cPhone[cPhone.Length - 1] = line;
counter++;
line = custSR.ReadLine();
Array.Resize(ref cName, cName.Length + 1);
cName[cName.Length - 1] = line;
counter++;
line = custSR.ReadLine();
phoneComboBox.Items.Add(cPhone[cPhone.Length - 1]);
}
custSR.Close();
//focus when program starts
phoneComboBox.Focus();
}

How to find string with role format in C#?

I'm want to find string have format like:
numbers.H numbers.numbers'
Example:
1H 34'
4H 89'
I tried this code:
string format;
int[] numbers = new int[10] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
foreach(var values in numbers)
{
format = values + "H " + values + values + "'";
}
NOTE: this code seems to fail. I code to you can't easy understand my problem.
So, if my sourceString have values like: 4H 89'.
I want to check this values a have format like numbers.H numbers.numbers'
At this time, I analysis like:
4 is numbers. H . 8 is numbers. 9 is numbers. =====> Correct format.
My code can't find this string. Can anybody have any method to resolve my problem?
Thanks.
Try this:
string input = "9H 99";
if (Regex.Match(input.ToUpper(), #"\d[A-Z]\s\d\d").Success)
{
//Match
}
else
{
//Not
}
Can be simplified using Linq or Regex, may be this can be helpful
string[] stringFormat = new string[7] { "1H 34'", "4H 89'", "4H 89", "42 89", "4H 8H'", "345 t3", "4H 333'" };
List<string> requiredFormattedStrings = new List<string>();
foreach (var stringValue in stringFormat)
{
string[] val = stringValue.Split(' ');
if ((val[0].Length == 2 && val[0].EndsWith("H")) && (val[1].Length == 3 && val[1].EndsWith("'")))
{
bool isNumber = Char.IsDigit(val[0], 0);
if (isNumber)
{
string secondString = val[1].Substring(0, 2);
bool isNumber2 = secondString.All(c => Char.IsDigit(c));
if(isNumber2)
{
requiredFormattedStrings.Add(stringValue);
}
}
}
}
OUTPUT
1H 34' ,4H 89'
#Dr. Stitch answer is best. If you want to understand the regex pattern.
string input = "9H 99";
if (Regex.Match(input.ToUpper(), #"\d\\H [0-9]{2}").Success)
//This is the pattern if you get only H or else replace '\\H' with'[A-Z]'.
//If you also get small case then add [a-zA-Z]
{
//Match
}
else
{
//Not
}

Making a predictive text algorithm go faster

I am working on a windows phone dialler app and I have implemented prediction text in my app. When user taps on keypad, contacts that match input are generated. Prediction is too slow, it also blocks my main thread that's why I have implemented BackGroundWorker But still having problems with the performance
My code is:
private void dialer_TextChanged(object sender, TextChangedEventArgs e)
{
MainPage.DialerText = dialer.Text;
if(!bw1.IsBusy)
bw1.RunWorkerAsync();
}
void bw1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
var digitMap = new Dictionary<int, string>() {
{ 1, "" },
{ 2, "[abcABC]" },
{ 3, "[defDEF]" },
{ 4, "[ghiGHI]" },
{ 5, "[jklJKL]" },
{ 6, "[mnoMNO]" },
{ 7, "[pqrsPQRS]" },
{ 8, "[tuvTUV]" },
{ 9, "[wxyzWXYZ]" },
{ 0, "" },
};
var enteredDigits = DialerText;
var charsAsInts = enteredDigits.ToCharArray().Select(x => int.Parse(x.ToString()));
var regexBuilder = new StringBuilder();
foreach (var val in charsAsInts)
regexBuilder.Append(digitMap[val]);
MainPage.pattern = regexBuilder.ToString();
MainPage.pattern = ".*" + MainPage.pattern + ".*";
}
catch (Exception f)
{
// MessageBox.Show(f.Message);
}
}
void bw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
SearchListbox.ItemsSource = listobj.FindAll(x => x.PhoneNumbers.Any(a=>a.Contains(MainPage.DialerText)) | Regex.IsMatch(x.FirstName, MainPage.pattern));
}
BackGroundWorker also blocking my main thread, hence when I tap on the keypad there's a lag while input values are added to the TextBox. I want to add input to the TextTox without any lag, how to do it? Thank you.
You can grab a real speed-up here by moving away from exhaustive searches of the entire wordlist and instead, putting your words into a more efficient data-structure.
For lightning fast lookups over any size of word list (but more expensive in terms of memory), you should build a tree structure that contains your entire word list.
The root node represents zero dialled digits, and it is connected to (up to) ten more nodes, where the edges connecting the nodes represent one of the possible numbers pressed for 0 to 9.
Each node then contains the possible words that can be formed from the path taken through the tree from the root node, where the path is representative of the numbers pressed.
This means that a search no longer requires iterating the entire word list and can be completed in very few operations.
Here's the concept in practice with a 370000 word-list I found on the web. Search takes around about 0.02ms on my desktop. Nice and fast. Seems to take about ~50MB in memory.
void Main()
{
var rootNode = new Node();
//probably a bad idea, better to await in an async method
LoadNode(rootNode).Wait();
//let's search a few times to get meaningful timings
for(var i = 0; i < 5; ++i)
{
//"acres" in text-ese (specifically chosen for ambiguity)
var searchTerm = "22737";
var sw = Stopwatch.StartNew();
var wordList = rootNode.Search(searchTerm);
Console.WriteLine("Search complete in {0} ms",
sw.Elapsed.TotalMilliseconds);
Console.WriteLine("Search for {0}:", searchTerm);
foreach(var word in wordList)
{
Console.WriteLine("Found {0}", word);
}
}
GC.Collect();
var bytesAllocated = GC.GetTotalMemory(true);
Console.WriteLine("Allocated {0} bytes", bytesAllocated);
}
async Task LoadNode(Node rootNode)
{
var wordListUrl =
"https://raw.githubusercontent.com/dwyl/english-words/master/words_alpha.txt";
Console.WriteLine("Loading words from {0}", wordListUrl);
using(var httpClient = new HttpClient())
using(var stream = await httpClient.GetStreamAsync(wordListUrl))
using(var reader = new StreamReader(stream))
{
var wordCount = 0;
string word;
while( (word = await reader.ReadLineAsync()) != null )
{
word = word.ToLowerInvariant();
if(!Regex.IsMatch(word,#"^[a-z]+$"))
{
continue;
}
rootNode.Add(word);
wordCount++;
}
Console.WriteLine("Loaded {0} words", wordCount);
}
}
class Node
{
static Dictionary<int, string> digitMap = new Dictionary<int, string>() {
{ 1, "" },
{ 2, "abcABC" },
{ 3, "defDEF" },
{ 4, "ghiGHI" },
{ 5, "jklJKL" },
{ 6, "mnoMNO" },
{ 7, "pqrsPQRS" },
{ 8, "tuvTUV" },
{ 9, "wxyzWXYZ" },
{ 0, "" }};
static Dictionary<char,int> letterMap;
static Node()
{
letterMap = digitMap
.SelectMany(m => m.Value.Select(c=>new {ch = c, num = m.Key}))
.ToDictionary(x => x.ch, x => x.num);
}
List<string> words = new List<string>();
//the edges collection has exactly 10
//slots which represent the numbers [0-9]
Node[] edges = new Node[10];
public IEnumerable<string> Words{get{
return words;
}}
public void Add(string word, int pos = 0)
{
if(pos == word.Length)
{
if(word.Length > 0)
{
words.Add(word);
}
return;
}
var currentChar = word[pos];
int edgeIndex = letterMap[currentChar];
if(edges[edgeIndex] == null)
{
edges[edgeIndex] = new Node();
}
var nextNode = edges[edgeIndex];
nextNode.Add(word, pos+1);
}
public Node FindMostPopulatedNode()
{
Stack<Node> stk = new Stack<Node>();
stk.Push(this);
Node biggest = null;
while(stk.Any())
{
var node = stk.Pop();
biggest = biggest == null
? node
: (node.words.Count > biggest.words.Count
? node
: biggest);
foreach(var next in node.edges.Where(e=>e != null))
{
stk.Push(next);
}
}
return biggest;
}
public IEnumerable<string> Search(string numberSequenceString)
{
var numberSequence = numberSequenceString
.Select(n => int.Parse(n.ToString()));
return Search(numberSequence);
}
private IEnumerable<string> Search(IEnumerable<int> numberSequence)
{
if(!numberSequence.Any())
{
return words;
}
var first = numberSequence.First();
var remaining = numberSequence.Skip(1);
var nextNode = edges[first];
if(nextNode == null)
{
return Enumerable.Empty<string>();
}
return nextNode.Search(remaining);
}
}
There is a number of optimizations you could make to improve the speed:
Adding .* prefix and suffix to your regex pattern is not necessary, because IsMatch will detect a match anywhere in a string
Using a local Dictionary<int,string> for parts of your pattern can be replaced by a static array
Converting digits to ints can be replaced with subtraction
The foreach loop and appending could be replaced by string.Join
Here is how:
private static string[] digitMap = new[] {
""
, "", "[abcABC]", "[defDEF]"
, "[ghiGHI]", "[jklJKL]", "[mnoMNO]"
, "[pqrsPQRS]", "[tuvTUV]", "[wxyzWXYZ]"
};
void bw1_DoWork(object sender, DoWorkEventArgs e) {
try {
MainPage.pattern = string.Join("", DialerText.Select(c => digitMap[c-'0']));
} catch (Exception f) {
// MessageBox.Show(f.Message);
}
}
I suspect the reason for the blocking is that your background worker thread doesn't actually do very much.
I expect the bw1_RunWorkerCompleted method (which gets run on the MAIN thread after the background worker has completed) is far more long-running! Can you move some (all?) of this code into the bw1_DoWork method instead? Obviously "listobj" would have to be accessible to the background thread though - just pass it into your delegate and access it by the DoWorkEventArgs.Argument property for this...
private void dialer_TextChanged(object sender, TextChangedEventArgs e)
{
MainPage.DialerText = dialer.Text;
if(!bw1.IsBusy)
bw1.RunWorkerAsync(listobj);
}
void bw1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
var digitMap = new Dictionary<int, string>() {
{ 1, "" },
{ 2, "[abcABC]" },
{ 3, "[defDEF]" },
{ 4, "[ghiGHI]" },
{ 5, "[jklJKL]" },
{ 6, "[mnoMNO]" },
{ 7, "[pqrsPQRS]" },
{ 8, "[tuvTUV]" },
{ 9, "[wxyzWXYZ]" },
{ 0, "" },
};
var enteredDigits = DialerText;
var charsAsInts = enteredDigits.ToCharArray().Select(x => int.Parse(x.ToString()));
var regexBuilder = new StringBuilder();
foreach (var val in charsAsInts)
regexBuilder.Append(digitMap[val]);
MainPage.pattern = regexBuilder.ToString();
MainPage.pattern = ".*" + MainPage.pattern + ".*";
var listobj = (ListObjectType)e.Argument;
e.Result = listobj.FindAll(x => x.PhoneNumbers.Any(a=>a.Contains(MainPage.DialerText)) | Regex.IsMatch(x.FirstName, MainPage.pattern));
}
catch (Exception f)
{
// MessageBox.Show(f.Message);
}
}
void bw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
SearchListbox.ItemsSource = (IEnumerable<ListObjectItemType>)e.Result;
}
NB - you'll obviously need to replace the casts to ListObjectType, and ListObjectItemType with the actual types!

I have a CSV file where the Date and the time are into 2 fields. How can I use FileHelpers to aggregate the 2 fields into the same DateTime data?

I have a CSV file where the Date and the time are into 2 fields.
How can I use FileHelpers to aggregate the 2 fields into the same DateTime data ?
Thanks,
2011.01.07,09:56,1.2985,1.2986,1.2979,1.2981,103
2011.01.07,09:57,1.2981,1.2982,1.2979,1.2982,75
2011.01.07,09:58,1.2982,1.2982,1.2976,1.2977,83
2011.01.07,09:59,1.2977,1.2981,1.2977,1.2980,97
2011.01.07,10:00,1.2980,1.2980,1.2978,1.2979,101
2011.01.07,10:01,1.2980,1.2981,1.2978,1.2978,57
2011.01.07,10:02,1.2978,1.2979,1.2977,1.2978,86
2011.01.07,10:03,1.2978,1.2978,1.2973,1.2973,84
2011.01.07,10:04,1.2973,1.2976,1.2973,1.2975,71
2011.01.07,10:05,1.2974,1.2977,1.2974,1.2977,53
2011.01.07,10:06,1.2977,1.2979,1.2976,1.2978,57
2011.01.07,10:07,1.2978,1.2978,1.2976,1.2976,53
2011.01.07,10:08,1.2976,1.2980,1.2976,1.2980,58
2011.01.07,10:09,1.2979,1.2985,1.2979,1.2980,63
var file = #"2011.01.07,09:56,1.2985,1.2986,1.2979,1.2981,103
2011.01.08,09:57,1.2981,1.2982,1.2979,1.2982,75
2011.01.09,09:58,1.2982,1.2982,1.2976,1.2977,83
2011.01.07,09:59,1.2977,1.2981,1.2977,1.2980,97
2011.01.07,10:00,1.2980,1.2980,1.2978,1.2979,101
2011.01.07,10:01,1.2980,1.2981,1.2978,1.2978,57
2011.01.07,10:02,1.2978,1.2979,1.2977,1.2978,86
2011.01.07,10:03,1.2978,1.2978,1.2973,1.2973,84
2011.01.07,10:04,1.2973,1.2976,1.2973,1.2975,71
2011.01.07,10:05,1.2974,1.2977,1.2974,1.2977,53
2011.01.07,10:06,1.2977,1.2979,1.2976,1.2978,57
2011.01.07,10:07,1.2978,1.2978,1.2976,1.2976,53
2011.01.07,10:08,1.2976,1.2980,1.2976,1.2980,58
2011.01.07,10:09,1.2979,1.2985,1.2979,1.2980,63";
var rows = file.Split('\n');
foreach(var row in rows){
var cols = row.Split(',');
var col1 = new DateTime();
foreach(var col in cols){
if (col == cols[0]) // first col
{
var dateParts = col.Split('.');
col1 = new DateTime(int.Parse(dateParts[0]), int.Parse(dateParts[1]), int.Parse(dateParts[2]));
}
else if (col == cols[1]) // second col
{
var timeParts = col.Split(':');
col1 = col1.AddHours(int.Parse(timeParts[0]));
col1 = col1.AddMinutes(int.Parse(timeParts[1]));
//col1.Dump();
}
else {
// all other columns here
}
}
}
I answer my own question.
Because my need have change, I dont really answer my own question.
My question have evolve to :
I have a CSV file where the Date and the time are into 2 fields. How can I use FileHelpers to aggregate the 2 fields into the same XLDate data ?
Here the full projet where I use this code :
enter link description here
private void CreateGraphic(ZedGraphControl zgc)
{
// référence vers le "canevas"
GraphPane pane = zgc.GraphPane;
pane.Title.Text = "Japanese Candlestick Chart Demo";
pane.XAxis.Title.Text = "Trading Date";
pane.YAxis.Title.Text = "Share Price, $US";
FileHelperEngine<MetaTrader4> engine = new FileHelperEngine<MetaTrader4>();
engine.ErrorManager.ErrorMode = ErrorMode.SaveAndContinue;
MetaTrader4[] res = engine.ReadFile(#"..\..\Data\EURUSD240.csv");
if (engine.ErrorManager.ErrorCount > 0)
engine.ErrorManager.SaveErrors("Errors.txt");
StockPointList spl = new StockPointList();
foreach (MetaTrader4 quotes in res)
{
DateTime dateTime = new DateTime();
dateTime = DateTime.ParseExact(quotes.Date + "-" + quotes.Time, "yyyy.MM.dd-HH:mm",
null);
XDate xDate = new XDate(dateTime);
StockPt pt = new StockPt(xDate, quotes.Hight, quotes.Low, quotes.Open, quotes.Close, quotes.Volume);
spl.Add(pt);
}
JapaneseCandleStickItem myCurve = pane.AddJapaneseCandleStick("trades", spl);
myCurve.Stick.IsAutoSize = true;
myCurve.Stick.Color = Color.Blue;
// Use DateAsOrdinal to skip weekend gaps
pane.XAxis.Type = AxisType.DateAsOrdinal;
// pretty it up a little
pane.Chart.Fill = new Fill(Color.White, Color.LightGoldenrodYellow, 45.0f);
pane.Fill = new Fill(Color.White, Color.FromArgb(220, 220, 255), 45.0f);
zgc.AxisChange();
}
You do this by listening to the engine.BeforeReadRecord event.
As you (engine) read each line, you'll need to remove the delimiter between the date and time so that it fits the expected date time format you specified in the FieldConverter attribute.
Here's is a linqpad snippet to demonstrate.
void Main()
{
string reading = "2011.01.07,09:56,1.2985,1.2986,1.2979,1.2981,103";
var engine = new FileHelperEngine<Reading>();
//engine.Options.Fields.Dump();
char delimiter = ((DelimitedRecordOptions)engine.Options).Delimiter.ToCharArray().First();
int expectedDelimiterCount = engine.Options.Fields.Sum(field => field.FieldType == typeof(DateTime) ? 2 : field.ArrayMinLength == 0 ? 1 : field.ArrayMinLength);
expectedDelimiterCount--; // no ending delimiter
engine.BeforeReadRecord += (ngin, e) => {
int fieldCount = e.RecordLine.Count(c => c == delimiter);
if (fieldCount == expectedDelimiterCount)
{
int delimiterIndex = e.RecordLine.IndexOf(delimiter);
if (delimiterIndex > NOT_FOUND)
{
e.RecordLine = e.RecordLine.Remove(delimiterIndex, 1);
}
}
};
var readings = engine.ReadString(reading);
readings.Dump();
}
const int NOT_FOUND = -1;
// Define other methods and classes here
[DelimitedRecord(",")]
class Reading
{
[FieldOrder(1)]
[FieldConverter(ConverterKind.Date, "yyyy.MM.ddHH:mm")]
public DateTime CollectionDate { get; set; }
[FieldOrder(2)]
[FieldArrayLength(4)]
public decimal[] Data;
[FieldOrder(3)]
public int CollectorID { get; set; }
}
Writing out the DateTime property into two fields is left as an exercise for the reader.

Categories