.Net IO
Make ReadOnly/ReadWrite
{
if (File.Exists(path))
{
FileInfo fileInfo = new FileInfo(path);
// is file readonly
if ((fileInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
//bitwise XOR - remove the readonly
fileInfo.Attributes ^= FileAttributes.ReadOnly;
else
//bitwise OR - add the readonly attribute
fileInfo.Attributes |= FileAttributes.ReadOnly;
}
}
Processing CSVs
The Regex way. With a dirty hack for localized Excel csvs (which use semi-colon).
{
//do work with fields here
throw new Exception("The method or operation is not implemented.");
}
private static void OpenCSVFile(string filename)
{
//,(?=(?:[^"]*"[^"]*")*(?![^"]*"))
System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
char[] trim = { ' ', '"' }; //we'll trim spaces and quotes
//in vb you could use Microsoft.VisualBasic.FileIO.TextFieldParser
//for more full-featured reader, see http://www.codeproject.com/cs/database/CsvReader.asp
using (System.IO.StreamReader sr = new System.IO.StreamReader(filename))
{
bool readHeader = false; //set this to true if no header
while (sr.Peek() != -1)
{
//bug: in true csv, line breaks within fields are allowed.
string fileLine = sr.ReadLine();
if (fileLine.Length > 0) //ignore blank lines
{
if (!readHeader)
{ //header line
if (fileLine.Contains("\";\"")) //tweak this if rqd
{
//local versions of Excel make csv with semicolons not commas
regex = new System.Text.RegularExpressions.Regex(";(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
}
readHeader = true; //discard header
}
else
{
//split the line into fields and trim each field
string[] fields = regex.Split(fileLine);
for (int i = 0; i < fields.Length; i++)
{
fields[i] = fields[i].Trim(trim);
}
ProcessFields(fields);
}
}
}
}
}
A button_Click to invoke the above.
{
openFileDialog1.Title = "Select CSV file";
openFileDialog1.Filter = "Comma Separated Values (*.csv)|*.csv";
openFileDialog1.DefaultExt = "csv";
openFileDialog1.FileName = "";
openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
OpenCSVFile(openFileDialog1.FileName);
}
}
Streams
Writing streams wrap the destination in the ctor and the stream.Write("text") always take the origin. Easy to get it the wrong way round!
Compressing
Create the GZipStream (or DeflateStream, the same without the zip CRC) from the source stream and read-out the bytes into your destination stream. It doesn't cope with .zip files (actually it's unix-like .gz compatible)
private static void CompressFile(string srcPath, string zipPath)
{
using (FileStream zip = File.Open(zipPath, FileMode.Create)) //destination fs
{
using (FileStream src = File.OpenRead(srcPath))//origin fs
{
//wrap compression stream around destination stream
using (GZipStream zipStream = new GZipStream(zip, CompressionMode.Compress))
{
//read byte-by-byte from source stream into compression stream
int eachByte = src.ReadByte();
while (eachByte != -1)
{
zipStream.WriteByte((byte)eachByte);
eachByte = src.ReadByte();
}
}
}
} //all streams closed
}
private static void DecompressFile(string zipPath, string unzippedPath)
{
using (FileStream unzipped = File.Open(unzippedPath, FileMode.Create)) //dest fs
{
using (FileStream zip = File.OpenRead(zipPath))//origin fs
{
//wrap compression stream around zipped stream
using (GZipStream zipStream = new GZipStream(zip, CompressionMode.Decompress))
{
//read byte-by-byte from compression
int eachByte = zipStream.ReadByte();
while (eachByte != -1)
{
unzipped.WriteByte((byte)eachByte);
eachByte = zipStream.ReadByte();
}
}
}
} //all streams closed
}
Isolated Storage
WinForms 2.0 Application Settings user.config is not secure...
- IsolatedStorageScope
- User - Always have user scope!
- Assembly (url or strong name - NB: this assembly could be called by another and then use it's data)
- Application - calling assembly
- Domain - appdomain
- Machine - everything on machine can read it
- In .Net 2, create using IsolatedStorageFile.GetMachineStoreForAssembly or GetUserStoreForAssembly (== GetStore with Assembly + machine or user flags)
- There's no FileInfo.Exists so use Store.GetFileNames("MyFile.txt").Length > 0
- Demo
This gets the My Documents folder (cf ApplicationData)
Directory.Copy
There isn't one (just Move), so here's the recursive directory copy
/// Copies the files in fromPath to the toDirectory. Recursive
/// </summary>
/// <param name="fromPath">From path.</param>
/// <param name="toPath">To path.</param>
public static void CopyFiles(string fromPath, string toPath)
{
//directoryInfo.FullName unfortunately doesn't have ending /, so path combines don't work.
if (!toPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
toPath += Path.DirectorySeparatorChar;
DirectoryInfo srcDir = new DirectoryInfo(fromPath);
foreach (FileInfo file in srcDir.GetFiles())
{
string newname = Path.Combine(toPath, file.Name); //put the file name onto the root
file.CopyTo(newname, true);
}
foreach (DirectoryInfo subDir in srcDir.GetDirectories())
{
string subDirName = Path.Combine(toPath, subDir.Name); //put the directory name onto the root
DirectoryInfo newSubDir = Directory.CreateDirectory(subDirName); //create it
CopyFiles(subDir.FullName, newSubDir.FullName); //recurse
}
}
Relative Paths
This creates a windows relative path (../../file.txt). Change two lines (see comments) for a web relative path (..\..\page.html).
/// <summary>
/// Gets the relative path from one directory to another.
/// Can take web "/" but always returns windows path.
/// </summary>
/// <param name="fromPath">From path.
/// Should be a directory (a file on the end will be treated as an extra depth of directory)</param>
/// <param name="toPath">To path. Can be a directory or file path</param>
/// <returns>Relative path with windows directory separator (\)</returns>
public string GetRelativePath(string fromPath, string toPath)
{
//validate- both must be absolute
if (fromPath.StartsWith(".")) return toPath;
if (toPath.StartsWith(".")) return toPath;
//sanitize- web forwardslash changed to windows backslash
char winSlash = Path.DirectorySeparatorChar;
char webSlash = Path.AltDirectorySeparatorChar; //swap these around if web paths
if (fromPath.Contains(webSlash.ToString())) fromPath = fromPath.Replace(webSlash, winSlash);
if (toPath.Contains(webSlash.ToString())) toPath = toPath.Replace(webSlash, winSlash);
//find what's common
string[] fromPathFolders = fromPath.Split(winSlash);
string[] toPathFolders = toPath.Split(winSlash);
int length = Math.Min(fromPathFolders.Length, toPathFolders.Length);
int sameUntil = -1;
for (int i = 0; i < length; i++)
{
if (!string.Equals(fromPathFolders[i], toPathFolders[i],
StringComparison.InvariantCultureIgnoreCase))
break;
sameUntil = i;
}
if (sameUntil == -1) return toPath; //they are completely different
//walk back up fromPath
StringBuilder sb = new StringBuilder();
for (int i = sameUntil + 1; i < fromPathFolders.Length; i++)
{
sb.Append(".." + winSlash);
}
//walk down the toPath
for (int i = sameUntil + 1; i < toPathFolders.Length; i++)
{
sb.Append(toPathFolders[i]);
if (i < (toPathFolders.Length - 1)) //not on last one
sb.Append(winSlash);
}
return sb.ToString();
}
[Test]
public void TestRelativePath()
{
//web forward slashes converted to windows backslashes
string fromPath = "root/north";
string toPath = "root/south/file2.txt"; //to a file
string expected = @"..\south\file2.txt";
string actual = GetRelativePath(fromPath, toPath);
Assert.AreEqual(expected, actual);
fromPath = "root/north";
toPath = @"root\south"; //to a folder
expected = @"..\south";
actual = GetRelativePath(fromPath, toPath);
Assert.AreEqual(expected, actual);
fromPath = "root/north";
toPath = @"root\south\"; //slash on end
expected = @"..\south\"; //is preserved
actual = GetRelativePath(fromPath, toPath);
Assert.AreEqual(expected, actual);
fromPath = "mars/north";
toPath = @"venus\south\"; //not the same root
expected = toPath; //relative path not applicable
actual = GetRelativePath(fromPath, toPath);
Assert.AreEqual(expected, actual);
}
Security
Access rules= DACLs, audit rules= SACL, easy to make an intellisense typo.
private void GetFileOwner(FileInfo f1)
{
FileSecurity ac = f1.GetAccessControl();
string name = ac.GetOwner(typeof(NTAccount)).Value; //shows name of owner
Debug.WriteLine(name);
}
private void CopyAccessRulesPreventInheritance(FileInfo f1, FileInfo f2)
{
const bool protectFromInheriting = true;
const bool preserveExistingInheritance = true;
//to copy access rules but prevent inheritance
FileSecurity acl = f1.GetAccessControl();
acl.SetAccessRuleProtection(protectFromInheriting, preserveExistingInheritance); //eg true, true so same as old
f2.SetAccessControl(acl);
}
public void TestFileAccessControl()
{
string initDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filePath = Path.Combine(initDir, Guid.NewGuid() + ".tmp");
File.WriteAllText(filePath, "Temp");
FileInfo f1 = new FileInfo(filePath);
GetFileOwner(f1);
string filePath2 = @"c:\" + f1.Name;
f1.CopyTo(filePath2);
FileInfo f2 = new FileInfo(filePath2);
CopyAccessRulesPreventInheritance(f1, f2);
f1.Delete();
f2.Delete();
}