Definitions
Term |
Definition |
Library |
File |
a byte collection with persistent storage (a.k.a. a backing store) |
|
Stream |
a byte collection of varying storage mediums, including memory |
|
Base stream |
connects to persistent storage (a.k.a. a backing store) |
|
FileStream |
for base stream file read/write operations. |
.Net-specific |
FileInfo |
a class, providing methods for file operations, e.g. copy, more, rename, create, open, delete, append. |
.Net-specific |
Synchronous I/O |
single-treaded (blocked) file IO |
|
Asynchronous I/O |
multi-threaded file IO |
|
Binary Writer/Reader |
for "primitive" data types, rather than character, strings |
.Net-specific |
StreamWriter |
for writing encoded characters (default = UTF-8) to/from a stream |
.Net-specific |
StreamReader |
for reading encoded characters (default = UTF-8) from a stream |
.Net-specific |
TextWriter |
abstract class for implementing StreamWriter & StreamReader |
.Net-specific |
File IO and string manipulation with .Net
.Net string manipulation revolves around the StreamWriter (and StreamReader) classes. Though the FileStream.BaseStream.Seek.SeekOrigin may be set to Begin (of the file), rather than insert; BeginWrite overwrites, starting at the beginning of the file. It's not practical to think of moving around a file and editing (as, for example, in Notepad ... or via a database cursor). Thus, to "insert" it's more practical to grab the content pieces and StreamWrite out to a new file.For example, I was asked to insert header and footer lines into a LinkShare affiliate program text file of data records. LinkShare requires a particular date format in the header and a records count in the footer. Here's how it was accomplished, using C# ...
using System;
using System.IO;
using System.Data;
using System.Data.SqlClient;
class LinkSharePrimaryHeaderFooter
{
public static void Main()
{
// Construct previous-day file name
string oldFile = (
@"\\serverName\1234_merchandis" +
DateTime.Now.Year +
DateTime.Now.Month +
(DateTime.Now.Day) +
".txt"
);
// Hose the previous LinkSharePrimary file
if (File.Exists(oldFile))
{
File.Delete(oldFile);
}
// construct current-day export file name, including LinkShare date format, where
// 1234 is (i.e. substitute) your LinkShare-assigned merchant id number.
string dateFileName = (
"1234_merchandis" +
DateTime.Now.Year +
DateTime.Now.Month +
DateTime.Now.Day +
".txt"
);
// Specify the target file to a FileInfo object (fi).
FileInfo fi = new FileInfo(@"\\serverName\" + dateFileName);
// Use the FileInfo.AppendText() method to
// write the three LinkShare components (header, records, footer) to target file (LinkSharePrimary.txt),
// starting with the LinkShare header
// Header processing ...
using (StreamWriter sw = fi.AppendText())
{
// Prepend LinkShare header to file.
sw.Write("HDR|1234|yourFirmName|");
sw.Write(DateTime.Now.Year);
sw.Write("-");
sw.Write(DateTime.Now.Month);
sw.Write("-");
sw.Write(DateTime.Now.Day);
sw.Write("/");
sw.Write(DateTime.Now.Hour);
sw.Write(":");
sw.Write(DateTime.Now.Minute);
sw.Write(":");
sw.WriteLine(DateTime.Now.Second);
// Records processing ...
// Assuming you've, previously, dumped your LinkShare records to a .txt file (e.g. from your favorite RDBMS ... in this case MS SQL Server)
// read the data.txt with a StreamReader & StreamWrite it to target file (LinkSharePrimary.txt).
StreamReader sr = File.OpenText(@"\\serverName\workingDirectory\LinkSharePrimary.txt");
String readfile;
while ((readfile = sr.ReadLine()) != null)
{
sw.Write(sr.ReadToEnd());
}
sr.Close(); // close the StreamReader
// Footer processing ...
// get LinkShare.LinkSharePrimary records count
SqlConnection LinkShareConn = new SqlConnection("Initial Catalog=LinkShare;Data Source=serverName;Integrated Security=SSPI;");
SqlCommand LSPrimaryCMD = LinkShareConn.CreateCommand();
LSPrimaryCMD.CommandText = "SELECT Count(*) FROM LinkSharePrimary";
LinkShareConn.Open();
SqlDataReader LSPrimaryReaderCount = LSPrimaryCMD.ExecuteReader();
while(LSPrimaryReaderCount.Read())
{
// Pass results to an integer variable
// Note: Without (int) "un-boxing", .Net thinks it's passing an object, even though the VALUE is int!
int LSPrimaryCount = (int)(LSPrimaryReaderCount.GetValue(0));
// StreamWrite the footer ...
sw.Write("TRL|");
sw.Write(LSPrimaryCount);
sw.Close(); // close StreamWriter
}
} // close ... using (StreamWriter) brace
} // close ... module brace
} // close ... class brace
.Net wildcard file searching
To find files on the file system, the Directory.GetFiles() method may be used, as follows:string[] dirs = Directory.GetFiles(@"c:\", "fileroot*");For example, a client wanted a maintenance program to delete old LinkShare .txt files - after 20 days. A C# program accomplished that, as follows:
using System;
using System.IO;
public class oldFilesDelete
{
public static void Main()
{
try
{
// assign LinkShare files into string array, oldFiles
string[] oldFiles = Directory.GetFiles(@"\\serverName\folderShare\", "1234*.txt");
// for each fileName in oldFiles array
foreach(string fileName in oldFiles)
// delete files, older than 20 days
if (DateTime.Compare(DateTime.Today.AddDays(-20), File.GetLastWriteTime(fileName)) > 0 )
{
File.Delete(fileName);
}
} // end TRY brace
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.ToString());
}
} // close Main() brace
} // close class brace
DTS errors from running the C# programs
error: Registered JIT debugger is not available.
This turned out to be a SQL Server DTS buglet. When using a DTS "Execute Process Task", the "Win32 process:" line requires an absolute path (e.g. C:\LinkShare\editFile.exe), rather than a URL-style path (e.g. \\serverName\shareName\fileName.exe). When configured with an absolute path, the job will only run from the SQL Server box i.e. not from a remote workstation.
Back