I am trying to make an app that will verify multiple file hashes at once. The way I've done it is like this: Hash source files from location 1 and output to a textbox filename, hash type and the hash itself.
Hash source files from location 2 and output to another text box the second filename, hash type and hash.
The problem is that that the paths are different, and there are several hashes to verify. I don't know how to split the string to make this work so that just the hashes get checked against the other hashes, without filesnames.
This is how I am getting the hash string:
string[] files = filePicketTextBox.Text.Split('\n');
if (files.Length > 0)
{
foreach (var file in files).. //do hash
Here is how the output of the hash gets done:
output = file + " MD5 Hash = " + sb.ToString() + "\n";
Here's what it looks like in the output textbox:
C:\Users\jj\Downloads\hasher.zip MD5 Hash = 8B0A222D30CA4962AFE30511695C8BB3
C:\Users\jj\Downloads\OfficeSetup.exe MD5 Hash = 2E210D07A9AE9B93FAEB0852A0DAFF83
And here's what I'm using to verify the hashes:
private void VerifyHashes(object sender, RoutedEventArgs e)
{
string output_one = outputTextBox.Text;
string output_two = output2TextBox.Text;
if (output_one == output_two)
{
Verification.Text = "HASHES VERIFIED";
}
else
{
Verification.Text = "NO MATCH";
}
Can anyone help me with how to split this to remove those filenames and take into account that multiple filenames will be getting verified at once?
Full code:
private void filePickerButton_Click(object sender, RoutedEventArgs e)
{
// Create the CommonOpenFIleDialog object
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = "C:\\Users";
dialog.IsFolderPicker = false;
dialog.Multiselect = true;
// Check to see if we have a result
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
filePicketTextBox.Text = string.Join("\n", dialog.FileNames);
}
else
{
outputTextBox.Text = "Operation cancelled." + "\n";
}
}
private async void runButton_Click(object sender, RoutedEventArgs e)
{
File.Delete(Path.Combine(Environment.CurrentDirectory, "log.txt"));
// Detect and handle the event of a void filename
if (filePicketTextBox.Text == "")
{
outputTextBox.Text = "No file selected." + "\n";
return;
}
string[] files = filePicketTextBox.Text.Split('\n');
if (files.Length > 0)
{
foreach (var file in files)
{
// Detect and handle the event of a non-valid filename
try
{
var stream = File.OpenRead(file);
stream.Close();
}
catch
{
outputTextBox.Text = "Invalid filename." + "\n";
return;
}
// Detect event of no options selected
if (!(bool)md5CheckBox.IsChecked && !(bool)sha1CheckBox.IsChecked && !(bool)sha256CheckBox.IsChecked && !(bool)sha512CheckBox.IsChecked)
{
outputTextBox.Text = "No hash algorithm selected.";
}
// MD5 Calculation and Display
if ((bool)md5CheckBox.IsChecked)
{
await Task.Run(() =>
{
var result = checkMD5(file).Result;
WriteIntoTxtBox(result);
});
await Task.Delay(1000);
progressbar.Value = 0;
}
MD5 Calc code:
public async Task<string> checkMD5(string file)
{
string output;
using (var md5 = MD5.Create())
{
AddValueLoadingBar(20, true);
using (var stream = File.OpenRead(file))
{
byte[] hash = md5.ComputeHash(stream);
AddValueLoadingBar(60, true);
await Task.Delay(1000);
StringBuilder sb = new StringBuilder(hash.Length);
int toAdd = 30 / hash.Length;
foreach (byte b in hash)
{
sb.AppendFormat("{0:X2}", b);
AddValueLoadingBar(toAdd);
}
output = file + " MD5 Hash = " + sb.ToString() + "\n";
AddValueLoadingBar(100, true);
}
}
return output;
}
this works fine:
string[] Files = strFileBuf.Split('\n');
string[] MyDelim = { " MD5 Hash = " };
foreach (string OneRow in Files)
{
string[] TwoParts = OneRow.Split(MyDelim,StringSplitOptions.None);
string FileNameOnly = Path.GetFileName(TwoParts[0]);
string MyHash = TwoParts[1];
TextBox1.Text += ("File = " + FileNameOnly + " ->Hash = " + MyHash + "\n");
}
Output:
So, you can split on the each line (\n)
And then you can assume that the delimiter is the string part inbetween.
So, we split out to file name, and hash.
and then i display ONLY file name (without path) and the hash code.
***EDIT **************************************
Ok, the user wants the output sorted. They should have posted SOME mark up.
Here what we will do:
We assume TWO input strings - the from files, the "to" files.
As noted, I REALLY have to assume that some kind of list or something MUCH better then two strings are the SOURCE of these two file sets.
However, the code that follows could WELL be adapted - and be run directly off the original two "lists" of files.
These two strings - from a text box, from the bottom of the ocean - don't care.
So, we assume this markup on the page:
<asp:Button ID="Button1" runat="server" Height="31px" Text="Button" Width="110px" />
<br />
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
<br />
A simple button, and a simple gridView.
We will shove/send the results out to the Grid view - sorted.
The code would/could behind the above button could/would look like this:
protected void Button1_Click(object sender, EventArgs e)
{
string strFileBuf1 = ""; ' set this from/input files string
string strFileBuf2 = ""; ' set this to files string
SortedDictionary<string, string> Loc1Files;
SortedDictionary<string, string> Loc2Files;
Loc1Files = GetFiles(strFileBuf1);
Loc2Files = GetFiles(strFileBuf2);
DataTable MyTable = new DataTable();
MyTable.Columns.Add("FromFile", typeof(string));
MyTable.Columns.Add("ToFile", typeof(string));
MyTable.Columns.Add("Match", typeof(string));
foreach (KeyValuePair<string, string> OneFile in Loc1Files)
{
DataRow OneRow;
OneRow = MyTable.Rows.Add;
OneRow("FromFile") = OneFile.Key;
if (Loc2Files.ContainsKey(OneFile.Key))
{
OneRow("ToFile") = OneFile.Key;
// compare hash codes
if (OneFile.Value == Loc2Files[OneFile.Key])
OneRow("Match") = "Yes";
else
OneRow("Match") = "No";
}
else
{
OneRow("ToFile") = "missing";
OneRow("Match") = "missing";
}
}
GridView1.DataSource = MyTable;
GridView1.DataBind();
}
Output:
The function/sub that splits out the data and returns that sorted dict list is this - we assume those "two" sets of files as those strings.
public SortedDictionary<string, string> GetFiles(string str)
{
string[] Files = str.Split('\n');
SortedDictionary<string, string> MyList = new SortedDictionary<string, string>();
foreach (string OneRow in Files)
{
string[] MyDelim = { " MD5 Hash = " };
string[] TwoParts = OneRow.Split(MyDelim,StringSplitOptions.None);
MyList.Add(Path.GetFileName(TwoParts[0]), TwoParts[1]);
}
return MyList;
}
I've done quite a bit of work to reduce your code down to the bare essentials of computing your hashes without relying on the UI. I've then got the code that requires the UI separated out so that it has nothing to do with computing hashes.
To start with I created a single hashing function that you can use to compute any hash.
public Task<string> ComputeHash(string file, Func<HashAlgorithm> create)
{
return Task.Run(() =>
{
using (var crypto = create())
{
using (var stream = File.OpenRead(file))
{
return String.Concat(
crypto.ComputeHash(stream).Select(x => x.ToString("X2")));
}
}
});
}
I've now created two fields to store the list of files and the resulting hashes.
private string[] _fileNames = null;
private string[] _hashes = null;
I'll show the code that sets the _fileNames array later, but for now this is the code that you can call to compute your hashes:
private async Task<String> Run(bool md5, bool sha1, bool sha256, bool sha512)
{
File.Delete(Path.Combine(Environment.CurrentDirectory, "log.txt"));
Func<HashAlgorithm> hashAlgorithm = null;
if (md5) hashAlgorithm = () => MD5.Create();
else if (sha1) hashAlgorithm = () => SHA1.Create();
else if (sha256) hashAlgorithm = () => SHA256.Create();
else if (sha512) hashAlgorithm = () => SHA512.Create();
if (hashAlgorithm == null)
{
return "No hash algorithm selected.";
}
else if (_fileNames == null || _fileNames.Length == 0)
{
return "No file selected." + "\n";
}
else if (_fileNames.Any(f => !File.Exists(f)))
{
return "Invalid filename." + "\n";
}
else
{
var tasks = _fileNames.Select(f => ComputeHash(f, hashAlgorithm)).ToArray();
_hashes = await Task.WhenAll(tasks);
return "Success";
}
}
The hardest part of this code, in my opinion, is the use of Func<HashAlgorithm> hashAlgorithm to store the hashing algorithm. If you're not familiar with a Func<> then it's worth looking up, but think of it as a way to store a function in a variable so that you can call the function later.
Now, finally, we have the two bits of code that interact with the UI:
private void filePickerButton_Click(object sender, RoutedEventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = "C:\\Users";
dialog.IsFolderPicker = false;
dialog.Multiselect = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
_fileNames = dialog.FileNames.ToArray();
filePicketTextBox.Text = string.Join("\n", dialog.FileNames);
}
else
{
outputTextBox.Text = "Operation cancelled." + "\n";
}
}
private async void runButton_Click(object sender, RoutedEventArgs e)
{
string result = await Run(
md5CheckBox.IsChecked,
sha1CheckBox.IsChecked,
sha256CheckBox.IsChecked,
sha512CheckBox.IsChecked);
outputTextBox.Text = result;
}
The end result is that you have two arrays, _fileNames & _hashes that you can use to get the hash for each file name. No need for splitting text.
You should format your output with some delimiter other than a space as file names can have space in them, otherwise you will need to account for that.
Your solution is quite awkward and could certainly be reengineered for a better solution.
But to directly answer your question you can join your output into a Dictionary<string,string>
where the key would be the file name and value would the hash
var d1 = outputTextBox.Text.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Split(' '))
.ToDictionary(
// use the file name without the directory as the key.
x => System.IO.Path.GetFileName(string.Join(" ", x.Take(x.Length - 4))),
// the has will be the last sequence of characters following the last space
x => x[x.Length - 1]
);
var d2 = output2TextBox.Text.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Split(' '))
.ToDictionary(
// use the file name without the directory as the key.
x => System.IO.Path.GetFileName(string.Join(" ", x.Take(x.Length - 4))),
// the has will be the last sequence of characters following the last space
x => x[x.Length - 1]
);
After you would need to compare the contents of the two dictionaries using the file names as a key.
This can be done as follows
///compare the the keys of the two dictionaries match.
var areEqual = d1.Keys
.OrderBy(x=> x) // sort the first dictionaries keys
// comare the sorted keys to the sorted keys of the second dictionary
.SequenceEqual(d2.Keys.OrderBy(x=>x));
if (areEqual)
{
// if the keys match, then compare the hashes
areEqual = d1.Select(x => x.Value == d2[x.Key])
.All(x => x == true);
}
Now if you are only concerned with if the files in two directories match, you could just collect the hashes and compare them.
var outputTextBox = new TextBox();
var output2TextBox = new TextBox();
var hashes_left = outputTextBox.Text
.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Substring(x.LastIndexOf(' ') + 1))
.OrderBy(x => x);
var hashes_right = output2TextBox.Text
.Split(Environment.NewLine.ToCharArray())
.Select(x => x.Substring(x.LastIndexOf(' ') + 1))
.OrderBy(x => x);
var areEqual = hashes_left.SequenceEqual(hashes_right);
However, even with these direct answers the proper thing would be to reengineer your code for a more efficient solution, such using a custom class in which you could use to compare strongly typed items in memory.
To clarify, reengineering the solution properly using custom classes would look something like this:
public class FileCollectionComparer
{
private HashAlgo algo;
private string[] x;
private string[] y;
public FileCollectionComparer(HashAlgo hashAlgo, string[] x, string[] y)
{
this.algo = hashAlgo;
this.x = x.OrderBy(z=> Path.GetFileName(z)).ToArray();
this.y = y.OrderBy(z => Path.GetFileName(z)).ToArray();
}
public bool AreEqual()
{
if (x.Length != y.Length)
return false;
else if (!x.Select(z=> Path.GetFileName(z))
.SequenceEqual(y.Select(z=> Path.GetFileName(z))))
return false;
else
{
var a = x.GetEnumerator();
var b = y.GetEnumerator();
while (a.MoveNext())
{
b.MoveNext()
var xHash = Hasher.GetHash((string)a.Current, algo);
var yHash = Hasher.GetHash((string)b.Current, algo);
if (xHash != yHash)
return false;
}
}
return true;
}
}
And we might define the HashAlgo enum and Hasher as:
public enum HashAlgo
{
MD5,
SHA1,
SHA256,
SHA512
}
public class Hasher
{
public static string GetHash(string filePath, HashAlgo algo)
{
HashAlgorithm hasher = null;
switch (algo)
{
case HashAlgo.MD5:
hasher = MD5.Create();
break;
case HashAlgo.SHA1:
hasher = SHA1.Create();
break;
case HashAlgo.SHA256:
hasher = SHA256.Create();
break;
case HashAlgo.SHA512:
hasher = SHA512.Create();
break;
default:
throw new InvalidEnumArgumentException(nameof(algo), (int)algo, typeof(HashAlgo));
}
using (var fs = File.OpenRead(filePath))
return string.Join("", hasher.ComputeHash(fs).Select(x => x.ToString("X2")));
}
}
Ok, it was as simple as adding this line in the hash method.
file = Path.GetFileName(file);
Here it is inside the method.
public async Task<string> checkMD5(string file)
{
string output;
using (var md5 = MD5.Create())
{
AddValueLoadingBar(20, true);
using (var stream = File.OpenRead(file))
{
byte[] hash = md5.ComputeHash(stream);
AddValueLoadingBar(60, true);
await Task.Delay(1000);
StringBuilder sb = new StringBuilder(hash.Length);
int toAdd = 30 / hash.Length;
foreach (byte b in hash)
{
sb.AppendFormat("{0:X2}", b);
AddValueLoadingBar(toAdd);
}
file = Path.GetFileName(file);
output = file + " MD5 Hash = " + sb.ToString() + "\n";
AddValueLoadingBar(100, true);
}
}
return output;
I added this right before the output, causing the output to not include the full path name which is the only thing that changes between the textboxes assuming the hashes are the same. The file name is the same, and if the hash is too, then it's verified.
This caused a lot of work but the answer was quite simple. My code definitely needs some re-writing though :) thanks everyone who answered this.
Related
What i am attempting to do is read a .txt file which is structured like:
#signup.php|register.php
URL_1
#...
URL_2
I am trying to search the source code of a page, if it finds a value from URL_1 in the source code it will return to me what that value found was.
To read the .txt file i have this so far:
public static Dictionary<string, string> ReadAiFields(string path)
{
var values = new Dictionary<string, string>();
try
{
if (!File.Exists(path)) return values;
var lines = File.ReadAllLines(path);
for (var i = 0; i < lines.Length - 1; i++)
{
string line = lines[i];
if (line.StartsWith("#"))
{
string[] keys = line.Remove(0, 1).Split('|');
string value = lines[i + 1];
foreach (string key in keys)
{
if (!values.ContainsKey(key))
{
values.Add(key, value);
}
}
}
}
}
catch (Exception ex)
{
DebugLogging("[" + DateTime.Now + "]-[" + ex.ToString() + "]");
}
return values;
}
So in the source code if signup.php is found it will return it.
I have started to write:
public static string GetUrlStructureFromFile(string inputHtml)
{
string returnValue = "";
inputValue = inputHtml.ToLower();
var values = ReadAiFields(#"LogicFiles\rj-ai-url.txt");
var foundKey = values.Keys.FirstOrDefault(key => inputHtml.Contains(key));
if (foundKey != null)
{
returnValue = values[foundKey];
}
return returnValue;
}
This return the key URL_1 but i really need the value, i never wrote the original code which is why i'm unsure, any tips or help would be appreciated.
I'm reading csv file:
string line;
StreamReader sr = new StreamReader(file.ToString());
while ((line = sr.ReadLine()) != null)
{
string col1 = line.Split(',')[10]; //old value
col1 = "my value"; //new value
}
sr.Close();
sr.Dispose();
I want to replace old value by the new.
Then I need to save the file with the changes.
How can I do that?
I suggest using File class instead of Streams and Readers. Linq is very convenient when querying data:
var modifiedData = File
.ReadLines(file.ToString())
.Select(line => line.Split(','))
.Select(items => {
//TODO: put relevant logic here: given items we should return csv line
items[10] = "my value";
return string.Join(",", items);
})
.ToList(); // <- we have to store modified data in memory
File.WriteAllLines(file.ToString(), modifiedData);
Another possibility (say, when initial file is too long to fit memory) is to save the modified data into a temporary file and then Move it:
var modifiedData = File
.ReadLines(file.ToString())
.Select(line => line.Split(','))
.Select(items => {
//TODO: put relevant logic here: given items we should return csv line
items[10] = "my value";
return string.Join(",", items);
});
string tempFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.tmp");
File.WriteAllLines(tempFile, modifiedData);
File.Delete(file.ToString());
File.Move(tempFile, file.ToString());
Reading an entire file at once is memory-expensive. Not to mention creating its parallel copy. Using streams can fix it. Try this:
void Modify()
{
using (var fs = new FileStream(file, FileMode.Open, FileAccess.ReadWrite))
{
string line;
long position;
while ((line = fs.ReadLine(out position)) != null)
{
var tmp = line.Split(',');
tmp[1] = "00"; // new value
var newLine = string.Join(",", tmp);
fs.WriteLine(position, newLine);
}
}
}
with extensions:
static class FileStreamExtensions
{
private static readonly char[] newLine = Environment.NewLine.ToCharArray();
private static readonly int length = Environment.NewLine.Length;
private static readonly char eof = '\uFFFF';
public static string ReadLine(this FileStream fs, out long position)
{
position = fs.Position;
var chars = new List<char>();
char c;
while ((c = (char)fs.ReadByte()) != eof && (chars.Count < length || !chars.Skip(chars.Count - 2).SequenceEqual(newLine)))
{
chars.Add(c);
}
fs.Position--;
if (chars.Count == 0)
return null;
return new string(chars.ToArray());
}
public static void WriteLine(this FileStream fs, long position, string line)
{
var bytes = line.ToCharArray().Concat(newLine).Select(c => (byte)c).ToArray();
fs.Position = position;
fs.Write(bytes, 0, bytes.Length);
}
}
The shortcoming is you must keep your values the same length. E.g. 999 and __9 are both of length 3. Fixing this makes things much more complicated, so I'd leave it this way.
Full working example
I have to MD5 hash files/folders on both a client(C#) and a server(PHP) file structure. (Server land is PHP and client land is c#.) The problem is while they work they do not match. Any ideas would be greatly appreciated
Here are my two algorithms
C#
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace nofolder
{
public class classHasher
{
/**********
* recursive folder MD5 hash of a dir
*/
MD5 hashAlgo = null;
StringBuilder sb;
public classHasher()
{
hashAlgo = new MD5CryptoServiceProvider();
}
public string getHash(String path)
{
// get the file attributes for file or directory
if (File.Exists(path)) return getHashOverFile(path);
if (Directory.Exists(path)) return getHashOverFolder(path);
return "";
}
public string getHashOverFolder(String path)
{
sb = new StringBuilder();
getFolderContents(path);
return sb.ToString().GetHashCode().ToString();
}
public string getHashOverFile(String filename)
{
sb = new StringBuilder();
getFileHash(filename);
return sb.ToString().GetHashCode().ToString();
}
private void getFolderContents(string fold)
{
foreach (var d in Directory.GetDirectories(fold))
{
getFolderContents(d);
}
foreach (var f in Directory.GetFiles(fold))
{
getFileHash(f);
}
}
private void getFileHash(String f)
{
using (FileStream file = new FileStream(f, FileMode.Open, FileAccess.Read))
{
byte[] retVal = hashAlgo.ComputeHash(file);
file.Close();
foreach (var y in retVal)
{
sb.Append(y.ToString());
}
}
}
}
}
PHP
function include__md5_dir($dir){
/**********
* recursive folder MD5 hash of a dir
*/
if (!is_dir($dir)){
return md5_file($dir);
}
$filemd5s = array();
$d = dir($dir);
while (false !== ($entry = $d->read())){
if ($entry != '.' && $entry != '..'){
if (is_dir($dir.'/'.$entry)){
$filemd5s[] = include__md5_dir($dir.'/'.$entry);
}
else{
$filemd5s[] = md5_file($dir.'/'.$entry);
}
}
}
$d->close();
return md5(implode('', $filemd5s));
}
EDIT.
I have decided the c# must change. the PHP is fine as it is. The first code that works 100% gets the bounty
Your PHP code is assembling hexadecimal numbers (as per the md5_file() documentation)
Your C# code is assembling non-0-padded decimal numbers.
You need to y.ToString("x2") to format as hexadecimal.
Also, return sb.ToString().GetHashCode().ToString(); is extremely wrong. Don't call GetHashCode(); it's not what you want.
I eventually fixed this myself and I include the answer for future posterity - the key to this solution was irradicating the different default dir ORDERING that linux and windows use. This was only tested on the linux server (Cent OS6.3) and Windows 7 Client.
C#
public class classHasher
{
/**********
* recursive folder MD5 hash of a dir
*/
MD5 hashAlgo = null;
StringBuilder sb;
public classHasher()
{
hashAlgo = new MD5CryptoServiceProvider();
}
public string UltraHasher(String path)
{
/**********
* recursive folder MD5 hash of a dir
*/
if (!Directory.Exists(path))
{
return getHashOverFile(path);
}
List<string> filemd5s = new List<string>();
List<string> dir = new List<string>();
if (Directory.GetDirectories(path) != null) foreach (var d in Directory.GetDirectories(path))
{
dir.Add(d);
}
if (Directory.GetFiles(path) != null) foreach (var f in Directory.GetFiles(path))
{
dir.Add(f);
}
dir.Sort();
foreach (string entry in dir)
{
if (Directory.Exists(entry))
{
string rtn = UltraHasher(entry.ToString());
//Debug.WriteLine(" ULTRRAAHASHER:! " + entry.ToString() + ":" + rtn);
filemd5s.Add(rtn);
}
if (File.Exists(entry))
{
string rtn = getHashOverFile(entry.ToString());
//Debug.WriteLine(" FILEEEEHASHER:! " + entry.ToString() + ":" + rtn);
filemd5s.Add(rtn);
}
}
//Debug.WriteLine(" ULTRRAASUMMMM:! " + String.Join("", filemd5s.ToArray()));
string tosend = CalculateMD5Hash(String.Join("", filemd5s.ToArray()));
//Debug.WriteLine(" YEAHHAHHAHHAH:! " + tosend);
return tosend;
}
public string getHashOverFile(String filename)
{
sb = new StringBuilder();
getFileHash(filename);
return sb.ToString();
}
private void getFileHash(String f)
{
using (FileStream file = new FileStream(f, FileMode.Open, FileAccess.Read))
{
byte[] retVal = hashAlgo.ComputeHash(file);
file.Close();
foreach (var y in retVal)
{
sb.Append(y.ToString("x2"));
}
}
}
public string CalculateMD5Hash(string input)
{
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hash = hashAlgo.ComputeHash(inputBytes);
StringBuilder sz = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sz.Append(hash[i].ToString("x2"));
}
return sz.ToString();
}
}
PHP
function md5_dir($dir){
/**********
* recursive folder MD5 hash of a dir
*/
if (!is_dir($dir)){
return md5_file($dir);
}
$filemd5s = array();
$bit = array();
$d = scandir($dir);
foreach($d as $entry){
if ($entry != '.' && $entry != '..'){
$bit[] = $entry;
}
}
asort($bit);
foreach($bit as $entry){
if (is_dir($dir.'/'.$entry)){
$sz = md5_dir($dir.'/'.$entry);
//echo "\n ULTRRAAHASHER:! ".$dir.'/'.$entry.":$sz";
$filemd5s[] = $sz;
}
else{
$sz = md5_file($dir.'/'.$entry);
$filemd5s[] = $sz;
//echo "\n FILEEEEHASHER:! ".$dir.'/'.$entry.":$sz";
}
}
//echo "\n ULTRRAASUMMMM:! ".implode('', $filemd5s)."";
//echo "\n YEAHHAHHAHHAH:! ".md5(implode('', $filemd5s))."";
return md5(implode('', $filemd5s));
}
these two will traverse either a C# Windows and or a PHP linux folder and return the SAME hashes for all dirs (recursive, so it includes sub dirs) inside Linuxland and all inside Windowsland.
For sort like C# asort() is case sensitive so you will need per example natcasesort()
I am trying to understand and probably reuse part of DevExpress Demo code to save simple settings into ini file. I know I can use .NET System.Configuration doing what I want. Just for a smallish project, simple save it as a text file seem more flexible and light-weighted, at least that is what it seems.
Anyway, while I am playing with it, I am trying to understand why the code I am reading trying to add key as "[]Server" and "[]DBFormat" as key name. They do that for a reason I can not understand yet, I could probably use some help here.
Here is the Code I think relevant:
public class IniFile {
SortedDictionary<string, string> data = new SortedDictionary<string, string>();
...
public void Load(string path) {
using(StreamReader sr = new StreamReader(path)) {
string folder = "[]";
while(!sr.EndOfStream) {
string s = sr.ReadLine().Trim();
if(s.Length == 0 || s[0] == ';') continue;
if(s[0] == '[') {
folder = s;
continue;
}
string key, value;
int delim = s.IndexOf('=');
if(delim < 0) {
key = folder + s.Replace("[", string.Empty).Replace("]", string.Empty);
value = string.Empty;
} else {
key = folder + s.Remove(delim).TrimEnd().Replace("[", string.Empty).Replace("]", string.Empty);
value = s.Substring(delim + 1).TrimStart();
}
if(!data.ContainsKey(key)) data.Add(key, value);
else data[key] = value;
}
}
}
...
public void Save(string path) {
using(StreamWriter sw = new StreamWriter(path)) {
string folder = "[]";
foreach(string key in data.Keys) {
int delim = key.IndexOf(']');
string keyFolder = key.Remove(delim + 1);
string keyName = key.Substring(delim + 1);
if(keyFolder != folder) {
folder = keyFolder;
sw.WriteLine(folder);
}
sw.WriteLine(keyName + " = " + data[key]);
}
}
}
void AddRawValue(string key, string value) {
key = key.Trim();
value = value.Trim();
int folderBegin = key.IndexOf('[');
int folderEnd = key.IndexOf(']');
if(folderBegin != 0 || folderEnd < 0) throw new ArgumentException("key");
data.Add(key, value);
}
And here is part of the ini file itself:
DBFormat = "Mdb"
Login = "admin"
Password = ""
Server = "(localhost)"
Obvious, they go though the trouble to add [] into keyname for some reason, but end up not using it in the demo data. I am think they are using string inside [] to group settings?
I only skimmed it, but I'd imagine the intent might have been "[section]setting" so "[]setting" would represent a setting not in a section. Look how easy it is to get values, just a single string will do! (There seems to be confusion between a "section" and a "folder" -- which might be an above-par variable name for that code...)
Then again, I could be way off as I only invested about 10 seconds of time on that tripe >:)
Happy coding.
all,
I started out with what i thought was going to be a pretty simple task. (convert a csv to "wiki" format) but im hitting a few snags that im having trouble working through
I have 3 main problems
1) some of the cells contain \r\n ( so when reading line by line this treats each new line as a new cell
2) some of the rows contain "," ( i tried switching to \t delemited files but im still running into a problem escaping when its between two "")
3) some rows are completely blank except for the delmiter ("," or "\t") others are incomplete (which is fine i just need to make sure that the cell goes in the correct place)
I've tried a few of the CSV reader classes but they would bump up agenst of teh problems listed above
I'm trying to keep this app as small as possible so i am also trying to avoid dlls and large classes that only a small portion do what i want.
so far i have two "attempts that are not working
Atempt 1 (doesn't handel \r\n in a cell)
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
openFileDialog1.Filter = "tab sep file (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog1.FilterIndex = 1;
openFileDialog1.RestoreDirectory = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
if (cb_sortable.Checked)
{
header = "{| class=\"wikitable sortable\" border=\"1\" \r\n|+ Sortable table";
}
StringBuilder sb = new StringBuilder();
string line;
bool firstline = true;
StreamReader sr = new StreamReader(openFileDialog1.FileName);
sb.AppendLine(header);
while ((line = sr.ReadLine()) != null)
{
if (line.Replace("\t", "").Length > 1)
{
string[] hold;
string lead = "| ";
if (firstline && cb_header.Checked == true)
{
lead = "| align=\"center\" style=\"background:#f0f0f0;\"| ";
}
hold = line.Split('\t');
sb.AppendLine(table);
foreach (string row in hold)
{
sb.AppendLine(lead + row.Replace("\"", ""));
}
firstline = false;
}
}
sb.AppendLine(footer);
Clipboard.SetText(sb.ToString());
MessageBox.Show("Done!");
}
}
string header = "{| class=\"wikitable\" border=\"1\" ";
string footer = "|}";
string table = "|-";
attempt 2 ( can handle \r\n but shifts cells over blank cells) (its not complete yet)
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
openFileDialog1.Filter = "txt file (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog1.FilterIndex = 1;
openFileDialog1.RestoreDirectory = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
if (cb_sortable.Checked)
{
header = "{| class=\"wikitable sortable\" border=\"1\" \r\n|+ Sortable table";
}
using (StreamReader sr = new StreamReader(openFileDialog1.FileName))
{
string text = sr.ReadToEnd();
string[] cells = text.Split('\t');
int columnCount = 0;
foreach (string cell in cells)
{
if (cell.Contains("\r\n"))
{
break;
}
columnCount++;
}
}
basically all I needs is a "split if not between \" " but im just at a loss right now
any tips or tricks would be greatly appreciated
Checkout this project instead of rolling your own CSV parser.
You might take a look at http://www.filehelpers.com/ as well...
Don't try to do it by yourself if you can use libraries!
Try taking a look here. Your code doesn't make web requests, but effectively this shows you how to parse a csv that is returned from a web service.
There's a decent implementation here...
A Fast CSV Reader by Sébastien Lorion
It makes much more sense in this case to use tried-and-tested code rather than trying to roll your own.
For a specification that's essentially two pages long, the CSV format is deceptive in its simplicity. The majority of short parser implementations that can be found on the internet are blatantly incorrect in one way or another. That notwithstanding, the format hardly seems to call for 1k+ SLOC implementations.
public static class CsvImport {
/// <summary>
/// Parse a Comma Separated Value (CSV) source into rows of strings. [1]
///
/// The header row (if present) is not treated specially. No checking is
/// performed to ensure uniform column lengths among rows. If no input
/// is available, a single row containing String.Empty is returned. No
/// support is provided for debugging invalid CSV files. Callers who
/// desire such assistance are encouraged to use a TextReader that can
/// report the current line and column position.
///
/// [1] https://www.rfc-editor.org/rfc/rfc4180
/// </summary>
public static IEnumerable<string[]> Deserialize(TextReader input) {
if (input.Peek() == Sentinel) yield return new [] { String.Empty };
while (input.Peek() != Sentinel) {
// must read in entire row *now* to see if we're at end of input
yield return DeserializeRow(input).ToArray();
}
}
const int Sentinel = -1;
const char Quote = '"';
const char Separator = (char)System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator;
static IEnumerable<string> DeserializeRow(TextReader input) {
var field = new StringBuilder();
while (true) {
var c = input.Read();
if (c == Separator) {
yield return field.ToString();
field = new StringBuilder();
} else if (c == '\r') {
if (input.Peek() == '\n') {
input.Read();
}
yield return field.ToString();
yield break;
} else if (new [] { '\n', Sentinel }.Contains(c)) {
yield return field.ToString();
yield break;
} else if (c == Quote) {
field.Append(DeserializeQuoted(input));
} else {
field.Append((char) c);
}
}
}
static string DeserializeQuoted(TextReader input) {
var quoted = new StringBuilder();
while (input.Peek() != Sentinel) {
var c = input.Read();
if (c == Quote) {
if (input.Peek() == Quote) {
quoted.Append(Quote);
input.Read();
} else {
return quoted.ToString();
}
} else {
quoted.Append((char) c);
}
}
throw new UnexpectedEof("End-of-file inside quoted section.");
}
public class UnexpectedEof : Exception {
public UnexpectedEof(string message) : base(message) { }
}
}