I am trying to develop an application to read .tdms (National Instruments) files, for which I'm using the "TDMSReader" package link to the package + use. This works fine except for those files which use a set duration or time interval.
In the .tdms example file I'm providing it can be noted that the file consists of five channels, each of which holds 174080 items.
(The content of the file can be viewed with this excel add-in)
However, the C# package I mentioned doesn't take this into account, it can only read the amount of items equal to the "wf_samples" field (10240), discarding the rest. Has anyone found a solution on how read the "Length" property of the channel and extract the rest of the array values?
Example of my code to convert a .tdms file to .csv
//file.Fullname = full path to the .tdms file
using (var output = new StreamWriter(File.Create(file.FullName + ".csv")))
using (var tdms = new NationalInstruments.Tdms.File(file.FullName))
{
tdms.Open();
List<object[]> All_Values = new List<object[]>();
//Headers
string channels = "";
foreach (var group in tdms)
{
foreach (var channel in group)
{
channels = channels + channel.Name + ";";
All_Values.Add(channel.GetData<object>().ToArray());
}
}
output.WriteLine(channels);
//Values
long cnt = tdms.First().Channels.First().Value.DataCount;
for (int i = 0; i < cnt; i++)
{
string values = "";
foreach (object[] columnValues in All_Values)
{
values = values + columnValues[i] + ";";
}
output.WriteLine(values);
}
}
Any other alternative that provides a way to read .tdms files with C# is welcome.
EDIT: TDMS sample files:
NO Interval sample
This one works fine
Interval Sample
This one discards most of the array values
I have submitted a PR for a fix to https://github.com/mikeobrien/TDMSReader. Mike has made a new release on https://www.nuget.org/packages/TDMSReader/.
Related
I have to generate a list of transaction IDs consistently every time I run my code and write them into a log. It looks something like this:
String path = #"C:\MyFolder\MyFile.csv";
for (int i = 1; i < 10; i++)
{
int transactionId = i;
string TransactionID = transactionId.ToString();
string date1 = DateTime.Today.ToString("yyyyMMdd");
string time1 = DateTime.Now.ToString();
string appendText = TransactionID + ";" + date1 + ";" + time1;
Environment.NewLine;
File.AppendAllText(path, appendText);
}
That will write 10 rows numbered with 1 to 10 every time I run this but I need to consistently update the transaction Ids with each time. So I run this code today rows are numbered 1 - 10, I run this code tomorrow rows should be numbered 11-20. How can I have a universal variable that increments with each run?
Thank you very much in advance!
At the startup of your program, read the last transaction ID from the last line of the CSV file.
In case no data exists, you know that you should start counting from 1 or 0
The core problem is that you need to know what the last written transaction ID is, for that you'll need to read in the file you're writing to and parse the last ID written, if none are present (or none were parsed) default to starting from 0 (or 1), something like this:
// Default to 0
var transactionId = 0;
var path = #"C:\MyFolder\MyFile.csv";
// Note ReadLines **not** ReadAllLines
var file = File.ReadLines(path);
var lastWrite = file.Last();
// Try and read the first part of the last line, if it fails just use the default
if (int.TryParse(lastWrite.Split(';')[0], out var lastWrittenId))
transactionId = lastWrittenId;
var sb = new StringBuilder();
var toWrite = new List<string>();
for (int i = 0; i < 10; i++)
{
var currentId = transactionId + i;
var date1 = DateTime.Today.ToString("yyyyMMdd");
// ...
// Without this check there'd be an empty line at the start of the file
if (currentId != 0)
sb.Append(Environment.NewLine);
var logText = $"{currentId};{date1};{...}";
sb.Append(logText);
toWrite.Add(sb.ToString());
sb.Clear();
}
File.AppendAllLines
FYI: You're (as far as I can tell) currently not appending a new line when writing. I also recommend that you prepend the new line, meaning the last line should always contain the last written entry, this makes parsing the last written entry easier and faster*
*Faster because we don't have to call Reverse() on the supplied IEnumerable, we want to avoid this because Reverse() captures the source into an array and as such will stream the entire file into our memory, while using Last() "simply" iterates over the entire thing and returns the last item, meaning we're not buffering the entire file in memory
I have just started to learn C# for one of my projects, in which i should be able to read all the data from a text file and then i should be able to compare the imported data with the default data structure of the file. So far i have been able to do a little bit of stuff, however i am stuck at splitting the imported data in a list with space as delimiter so that i can try to compare it with the default data which i am planning to put in a default data list.
The structure of the file(File1) to be imported(or the user provided) is as follows:-
%emp_first_name% = xxxxxxxx %emp_middle_name% = xxxxxxxx %emp_last_name% = xxxxxxxx;
%emp_age% = nn;
%emp_dept.% = xxxxxxxx;
%emp_joining_date% = xx-xx-xxxx;
the default structure of the file(File2) is:-
%emp_first_name% = xxxxxxxx %emp_middle_name% = xxxxxxxx %emp_last_name% = xxxxxxxx;
%emp_age% = nn;
%emp_total_exp% = xx;
%emp_grade% = x;
%emp_dept.% = xxxxxxxx;
%emp_joining_date% = xx-xx-xxxx;
after reading the File1 in a list, i am unable to split it using space as a delimiter, this is what i am doing to read the File1 into a list.
public static void readFinL(string filename)
{
string readAllLines = File.ReadAllText(filename);
List<string> list = new List<string>();
list.Add(readAllLines);
foreach (string d in list)
{
var f = d.Split(',');
Console.WriteLine(f.GetValue(0));
}
}
what am i not doing or what is it that i am doing incorrectly with this method to read the file in a list. I am passing the data in a list since i should be able to compare File1 with File2 to check which row is missing in File1. Any pointer in correct direction will be helpful.
First of all, d.Split(',') is splitting with the comma. Use var f = d.Split(' ') instead.
If I'm not wrong, File.ReadAllText return a single string. Your list only have one element by this way.
string[] lines = File.ReadAllLines("path to the file")
Should do the work.
I have developed a small tool that will be used to display data discrepancy in c#, what I do is explained point wise below,
fetch data from database and write list of file names in text file based on date criteria -output 1
take dir of path 1 and write into text file- output2
take dir of path2, path3 and path4 similarly and write into text files separately for each path- output 3/4/5
compare option: compare output1 and 2 and write down the difference in text file, this difference is then compared to output3 and again the difference is written in another file, and so on...
my issue is : my last path has more than 2.5 million records of files, whenever I try writing it in text file it hangs the application and it never provides output, I did try filtering it with date criteria but even for a single day where records could be around 30 thousands it hangs
I have searched many sites but did not get solution that I can understand or able to implement it. Below is my attempted code.
if (!txtpath3.Text.Equals(String.Empty) && System.IO.Directory.GetFiles(txtpath3.Text).Length > 0)
{
var directory = txtpath3.Text;
var from_dt = this.dtpickerstart.Value;
var end_dt = this.dtpickerend.Value;
DateTime from_date = from_dt;
DateTime to_date = end_dt;
DirectoryInfo di = new DirectoryInfo(directory);
FileSystemInfo[] files = di.GetFileSystemInfos();
var op = di.GetFiles()
.Where(file => file.LastWriteTime >= from_date && file.LastWriteTime <= to_date);
foreach (string file in System.IO.Directory.GetFiles(txtpath3.Text, "*.*"))
{
TextWriter tw = new StreamWriter(dirfile3, true);
tw.WriteLine("" + file + "");
tw.Close();
}
}
else
{
}
Your foreach-loop opens and closes the file for all lines. You should open and close the file outside of the loop.
using(var tw = new StreamWriter(dirfile3, true))
{
foreach (string file in System.IO.Directory.GetFiles(txtpath3.Text, "*.*"))
{
tw.WriteLine("" + file + "");
}
}
Even easier would be using the already existing functions to do this:
File.AppendAllLines(dirfile3, System.IO.Directory.GetFiles(txtpath3.Text, "*.*"));
As 2.5 million filesnames are a lot to keep in RAM at the same time, you might be better off with just enumerating them:
File.AppendAllLines(dirfile3, System.IO.Directory.EnumerateFiles(txtpath3.Text, "*.*"));
I think that the problem is in the foreach
foreach (string file in System.IO.Directory.GetFiles(txtpath3.Text, "*.*"))
{
TextWriter tw = new StreamWriter(dirfile3, true);
tw.WriteLine("" + file + "");
tw.Close();
}
For each and every one of the many, many files, you are opening a file, appending a line, and closing the file, only to open it again, write another line, etc, etc...
You should just prepare everything in a string first, and then just insert all the text in one go, something like:
StringBuilder sb = new StringBuilder();
foreach (string file in System.IO.Directory.GetFiles(txtpath3.Text, "*.*"))
{
sb.AppendLine(file);
}
File.WriteAllText(dirfile3, sb.ToString());
I am currently trying to work on files, joining multiple of them and having problem because the last work from file 1 is linked with first word from file 2. For example:
File 1:John has got new haircut
File 2: Mike has got new haircut
and it prints me "haircutMike".
The code I am using to split words:
input.Split(' ').ToList().ForEach(n =>{});
I am also making one big file from multiple ones like so:
string[] files = { "f1.txt", "f2.txt" };
FileStream outputFile = new FileStream("new.txt", FileMode.Create);
using (StreamWriter ws = new StreamWriter(outputFile))
{
foreach (string file in files)
{
ws.Write(System.IO.File.ReadAllText(file) + " ");
}
}
#EDIT
Changed some code, of course I meant to use stream not binary,also I am using split because I want to count the number of each word in files so I have to split spaces, dots etc.
You mentioned to use + " " option, although it works, but it added me 1 letter to the total count.
EDIT: for multiple input files:
string[] files = { "f1.txt", "f2.txt" };
var allLines = files.SelectMany(i => System.IO.File.ReadAllLines(i));
System.IO.File.WriteAllLines("new.txt", allLines.ToArray());
I have created some code which pulls a list of file names off the server and stores them, I also get the folder names from a directory on my computer and I need to compare them both to find out if there are any names which are on the local machine and not on the server.
serverlistarray contains the list of folders on the server (1 folder per entry), local listarray contains the directory listing on the local computer, this is also has 1 folder per entry. I read the local files using this:
String[] localfilelistarray = System.IO.Directory.GetDirectories(/*file location*/);
My problem lies within the get directory's, it includes the full path of the file which I cannot compare. The code below is what I have tried to do, I check the serverlistarray for the file in locallistarray and if they match i put them in the list.
for (int b = 0; b < localfilelistarray.Length; b++)
{
//problem with the compare
if (!serverlistarray.Contains(localfilelistarray[b].Replace(/*file path before filename*/, "")))
{
//add to list variable
}
}
I then use this code to do what I want for everything in the list (for now its a message box to show the filename).
for (int f = 0; f < delete.Count(); f++)
{
MessageBox.Show(/*list variable*/);
}
BUT I get every file even if it is in the serverlistarray and I cant work out whats wrong. I believe its to do with the comparison of the two arrays but when I put message boxes in that loop thay come out as expected but the comparison doesnt seem to work.
Can anyone see what ive done wrong?
ive tried using replace and direct comparisons to no avail. I though of adding the file path to the serverlistarray BUT I can not do this as I need the raw file name for server operations and other parts of this code.
EDIT:
serverlistarray example:
[0]folder1
[1]folder2
[2]folder3
localfilearray example:
[0]c:\\users\Noliver\folder1
[1]c:\\users\Noliver\folder2
[2]c:\\users\Noliver\folder3
[3]c:\\users\Noliver\folder4
[4]c:\\users\Noliver\folder5
expected output (message boxes in for loop):
c:\\users\Noliver\folder4
&
c:\\users\Noliver\folder5
EDIT 2:
Ive tried this code with no lick using the file path:
for (int b = 0; b < localfilelistarray.Length; b++)
{
String[] temp = System.IO.Path.GetFileName(localfilelistarray[b]).Split('.');
MessageBox.Show(localfilelistarray[b] + " - " + temp[0]);
for (int t = 0; t < serverlistarray.Length;t++)
{
MessageBox.Show("server " + serverlistarray[t] + " - " + localfilelistarray[b] + " - " + temp[0]);
}
if (!update.Contains(temp[0]))
{
delete.Add(localfilelistarray[b]);
}
}
the output from this showed that the serverlistarray contained the file name from temp[0] (the file name with the extension taken off) but all the files were in the list variable. could this come down to new lines or a space at the end of one of the strings? for some reason the if statement checking if the array contains the value doesnt work.
From the way the string is stored the deepest folder name were just written as if they are a file name. So I used Path.GetFileName to extract it.
localfilearray.Where(x => !serverlistarray.Contains(Path.GetFileName(x)))
This work as well on most of case, if you don't like the name of previous one :
localfilearray.Where(x => !serverlistarray.Contains(x.Split('\\').Last()))
To use the above code with what you have now, you can :
Expand your existing delete list to include what to delete :
delete.AddRange(localfilearray.Where(x => !serverlistarray.Contains(x.Split('\\').Last())));
Or, just declare it like this :
var delete = new List(localfilearray.Where(x => !serverlistarray.Contains(x.Split('\\').Last())));`
I'd advise against splitting on "\" etc., instead use DirectoryInfo to get the name of the directory:
var directoryInfo = new DirectoryInfo(#"c:\MyDir");
String directoryName = directoryInfo.Name; // returns "MyDir"