I have this function:
var dupeFiles = Directory.EnumerateFiles(strPath, "*", SearchOption.AllDirectories)
.Select(fullPath => new
{
Name = Path.GetFileName(fullPath),
FullPath = fullPath
})
.GroupBy(file => file.Name)
.Where(fileGroup => fileGroup.Count() > 1);
foreach (var dupeGroup in dupeFiles)
{
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(#"DupeFiles.txt", true))
{
file.WriteLine(dupeGroup.Key);
}
foreach (var dupeFile in dupeGroup)
{
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(#"DupeFiles.txt", true))
{
file.WriteLine($" {dupeFile.FullPath}");
}
}
}
But im getting this result:
FILE.TXT
...\RootFolder\Folder1\Child1\AnotherChild\FILE.TXT
...\RootFolder\Folder1\Child2\AnotherChild\FILE.TXT
...\RootFolder\Folder1\Child3\FILE.TXT
...\RootFolder\Folder2\Child3\FILE.TXT
...\RootFolder\Folder2\Child2\AnotherChild\FILE.TXT
How limit the result for the same file path from folder1 and folder2 (or more, this same child level)? In that case, i just want the results:
FILE.TXT
...\RootFolder\Folder1\Child2\AnotherChild\FILE.TXT
...\RootFolder\Folder2\Child2\AnotherChild\FILE.TXT
Thank you!
Thank you all replys, but it stills don't helps. Maybe i can't tell you what exactly i need, so look those real pic:
As you can see, the first file: NMS_REALITY_GCTECHNOLOGYTABLE.MBIN are repeated on 3 folders:
LessOverHeat10 ...
Reduced Launch Cost...
_BettterFreighter...
Thats all right, because as you can see, the same file was on (root) folders different.
But another other files, PLANTINTERACTION.ENTITY.MBIN, repeats on a lot of others folders, but inside the same folder:
_FASTER_FARMING_X50.
This part is wrong. I would lik to ignore (not list) all those files, because its for the same folder. I just would like to list, or mark them as conflicted, if its at the same path, but for different root folder.
Its better now? Thank you so much, sorry if can't explain right my needs.
Count on you, thanks!!!
Make a ParentPath attribute and group by that then select what you want from every parentDirectory by skipping all you don't want.
var dupeFiles = Directory.EnumerateFiles(strPath, "*", SearchOption.AllDirectories)
.Select(fullPath => new
{
Name = Path.GetFileName(fullPath),
FullPath = fullPath,
ParentPath = GetParentPath(fullPath)
}).GroupBy(file => file.ParentPath)
.Select(parentDirectory => parentDirectory
.SkipWhile((childFile, index) => index != 1)
.First()
);
StreamWriter fileWriter = new StreamWriter(#"DupeFiles.txt", true);
foreach(var aFile in dupeFiles.ToList())
Console.WriteLine(aFile.FullPath);
...
private string GetParentPath(string fullPath)
{
string [] strArr = fullPath.Split('\\');
string parentPath = strArr[0] + '\\' + strArr[1] + '\\' + strArr[2] + '\\' + strArr[3] + '\\' + strArr[4] + '\\' + strArr[5];
//Console.WriteLine("parentPath: " + parentPath);
return parentPath;
}
...
Edited answer using double foreach and retaining IGrouping.Key
string strPath = "C:\\Users\\Luke\\Desktop\\RootFolder";
var dupeFiles = Directory.EnumerateFiles(strPath, "*", SearchOption.AllDirectories)
.Select(fullPath => new
{
Name = Path.GetFileName(fullPath),
FullPath = fullPath,
}).GroupBy(file => GetParentPath(file.FullPath))
.Select(parentDirectory =>
parentDirectory.SkipWhile((childFile, index) => index != 1).First()
).GroupBy(file => file.Name, file => file.FullPath);
foreach(var dupeGroup in dupeFiles)
{
Console.WriteLine("key is: " + dupeGroup.Key);
foreach (var dupeFile in dupeGroup)
{
Console.WriteLine(dupeFile);
}
}
/*example output
key is: FILE.txt
C:\Users\Luke\Desktop\RootFolder\Folder1\Child2\AnotherChild\FILE.txt
C:\Users\Luke\Desktop\RootFolder\Folder2\Child2\AnotherChild\FILE.txt
*/
Related
I have a directory of files that contain both Word and PDF files. Some of the Word files in the directory have the same filename (minus extension) as the PDF files in the same directory. I have setup a simple C# winforms application to loop through the files and move the Word documents that have same name as PDF documents. Here's what I have so far. I'm not sure why this isn't working:
string[] filesWORD = Directory.GetFiles(#"c:\test\", "*.docx");
List<string> resultFiles = new List<string>();
foreach (var file in filesWORD)
{
var finalfile = file.Substring(0, file.LastIndexOf(".")); // removes everything after period in name.
resultFiles.Add(finalfile);
listBox1.DataSource = resultFiles.Distinct().ToList(); // placing the Word files in listBox1
}
string[] filesPDF = Directory.GetFiles(#"c:\test\", "*.pdf");
List<string> resultFilesPDF = new List<string>();
foreach (var file in filesPDF)
{
var finalfile = file.Substring(0, file.LastIndexOf("."));
resultFilesPDF.Add(finalfile);
listBox2.DataSource = resultFilesPDF.Distinct().ToList(); // placing the PDF files in listBox2
}
for (int i = 0; i < listBox1.Items.Count; i++)
{
//IF the WORD files in listBox1 match the PDF files in listBox2 -- move them to a new folder.
foreach (string files in listBox1.Items)
{
if (listBox1.Items == listBox2.Items)
{
//Get Filename
var filename = Path.GetFileName(files + ".docx");
//Move Files
File.Move(files + ".docx", #"c:\test2\" + "\\" + filename);
}
}
}
The final for loop is where the problem is, you can try this (need to add listbox for your case), you are comparing the wrong thing, also the outer for loop is not required.
foreach (var pdfFile in resultFilesPDF)
{
foreach (var wordFile in resultFiles)
{
if (wordFile == pdfFile)
{
//Get Filename
var filename = System.IO.Path.GetFileName(wordFile + ".docx");
//Move Files
File.Move(wordFile + ".docx", #"c:\test2\" + "\\" + filename);
}
}
}
Using Linq you can do it like, be aware that if you try to move the same file multiple times the Move method might blow up.
var sameNames = resultFiles.SelectMany(w => resultFilesPDF.Where(p => p == w));
sameNames.ToList().ForEach(file =>
{
File.Move(file + ".docx", #"c:\test2\" + "\\" + System.IO.Path.GetFileName(file + ".docx"));
});
I want to create folders month wise from a list of files.
I tried below code.
var files = directory.GetFiles()
.Where(file => file.LastWriteTime.Month == date.Month -1);
//create folder for the files (With MonthName)
var year = files.Select(j => j.LastWriteTime.Year).FirstOrDefault();
var month = files.Select(j => j.LastWriteTime.Month).FirstOrDefault();
var newFolderPath = year.ToString() + month.ToString();
var destinationDirec = System.IO.Directory.CreateDirectory(directory + newFolderPath);
foreach (var f in files)
{
// var destFile = System.IO.Path.Combine(directory, destinationDirec);
var path = Path.Combine(destinationDirec.FullName, f.Name);
if (!File.Exists(path))
{
System.IO.File.Move(f.FullName, path);
}
}
The above code gave the list of files for the last month. But I want create folders for the files which are older than the current month.
Can anyone give me a solution?
You can try this code. Maybe with some changes.
//Group files by month. Later you can skip some groups if needed
var fileGroups = directory.GetFiles()
.GroupBy(file => file.LastWriteTime.Month);
foreach (var fileList in fileGroups)
{
var year = fileList.First().LastWriteTime.Year;
var month = fileList.First().LastWriteTime.Month;
var newFolderPath = year.ToString() + month.ToString();
var destinationDirec = System.IO.Directory.CreateDirectory(directory + newFolderPath);
//move files
foreach (var file in fileList)
{
var path = Path.Combine(destinationDirec.FullName, file.Name);
if (!File.Exists(path))
{
System.IO.File.Move(file.FullName, path);
}
}
}
Maybe it's worth to modify GroupBy condition if you have a lot of files from different years.
For example you can use this condition:
GroupBy(file => (397 * file.LastWriteTime.Year) ^ file.LastWriteTime.Month)
This should help, build the fullNewDir value as you need it.
String fullSourceDir = "G:\\Tmp\\Test";
foreach (var fullFileName in Directory.EnumerateFiles(fullSourceDir)){
DateTime lastWriteTime = File.GetLastWriteTime(Path.Combine(fullSourceDir, fullFileName));
String fullNewDir = Path.Combine(fullSourceDir, lastWriteTime.ToString("yyyy-MM-dd_HH-mm"));
if (!Directory.Exists(fullNewDir)){
Directory.CreateDirectory(fullNewDir);
}
String fileName = Path.GetFileName(fullFileName);
System.IO.File.Move(fullFileName, Path.Combine(fullNewDir, fileName));
}
I have this code:
HashSet<string> allowedFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"file1.dll",
"file2.dll",
"file3.exe"
};
DirectoryInfo directory = new DirectoryInfo(Directory.GetCurrentDirectory());
bool containsNonAllowedFiles = directory.EnumerateFiles().Any(file => !allowedFiles.Contains(file.Name));
if (containsNonAllowedFiles == true)
{
Message.Warning("Unallowed files found ! Please remove them.");
return;
}
But I want to show filename, which is unallowed, how can I do it?
For example I have folder with these files: file1.dll, file2.dll, file3.exe, file4.exe, When I launch the app it must print for me Unallowed files found ! Please remove file4.exe and start app again. Thanks in advance
Use the Except method from Linq to produce the list of unallowed file names.
var disallowedFiles = directory.EnumerateFiles()
.Select(fi => fi.Name)
.Except(allowedFiles, StringComparer.OrdinalIgnoreCase)
.ToList();
DirectoryInfo directory = new DirectoryInfo(Directory.GetCurrentDirectory());
List<FileInfo> files = directory.GetFiles().ToList();
List<FileInfo> unAllowed = files.FindAll(f => !allowedFiles.Contains(f.Name));
if (unAllowed.Count > 0)
{
string notAllowedFiles = "";
unAllowed.ForEach(f => notAllowedFiles += f.Name + ",");
Message.Warning("Unallowed files found ! Please remove " + notAllowedFiles);
return;
}
You can use Where instead of the early Any:
var nonAllowedFilesNames = directory.EnumerateFiles()
.Select(file => file.Name)
.Where(fileName => !allowedFiles.Contains(fileName));
if(nonAllowedFilesNames.Any())
{
string unallowedFiles = String.Join(",", nonAllowedFilesNames);
Message.Warning("Unallowed files found ! Please remove: " + unallowedFiles);
}
You must get the list of unallowed files. You can do it like this :
List<String> nonAllowedFiles = directory.EnumerateFiles().FindAll(file => !allowedFiles.Contains(file.Name));
and then you can process it like this :
foreach (String file in nonAllowedFiles ) {
Message.Warning("Unallowed files found ! Please remove " + file + " and start app again");
}
return;
I have a folder that is filled with dwg files so I just need to find the latest version of a File or if a File has no versions then copy it to a directory. For example here are three files:
ABBIE 08-10 #6-09H4 FINAL 06-12-2012.dwg
ABBIE 08-10 #6-09H4 FINAL 06-12-2012_1.dwg
ABBIE 08-10 #6-09H4 FINAL 06-12-2012_2.dwg
Notice the difference is one file has a _1 and another has a _2 so the latest file here is the _2. I need to keep the latest file and copy it to a directory. Some files will not have different versions so those can be copied. I cannot focus on the creation date of the file or the modified date because in many instances they are the same so all I have to go on is the file name itself. I'm sure there is a more efficient way to do this than what I will post below.
DirectoryInfo myDir = new DirectoryInfo(#"H:\Temp\Test");
var Files = myDir.GetFiles("*.dwg");
string[] fileList = Directory.GetFiles(#"H:\Temp\Test", "*FINAL*", SearchOption.AllDirectories);
ArrayList list = new ArrayList();
ArrayList WithUnderscores = new ArrayList();
string nameNOunderscores = "";
for (int i = 0; i < fileList.Length; i++)
{
//Try to get just the filename..
string filename = fileList[i].Split('.')[0];
int position = filename.LastIndexOf('\\');
filename = filename.Substring(position + 1);
filename = filename.Split('_')[0];
foreach (FileInfo allfiles in Files)
{
var withoutunderscore = allfiles.Name.Split('_')[0];
withoutunderscore = withoutunderscore.Split('.')[0];
if (withoutunderscore.Equals(filename))
{
nameNOunderscores = filename;
list.Add(allfiles.Name);
}
}
//If there is a number after the _ then capture it in an ArrayList
if (list.Count > 0)
{
foreach (string nam in list)
{
if (nam.Contains("_"))
{
//need regex to grab numeric value after _
var match = new Regex("_(?<number>[0-9]+)").Match(nam);
if (match.Success)
{
var value = match.Groups["number"].Value;
var number = Int32.Parse(value);
WithUnderscores.Add(number);
}
}
}
int removedcount = 0;
//Whats the max value?
if (WithUnderscores.Count > 0)
{
var maxval = GetMaxValue(WithUnderscores);
Int32 intmax = Convert.ToInt32(maxval);
foreach (FileInfo deletefile in Files)
{
string shorten = deletefile.Name.Split('.')[0];
shorten = shorten.Split('_')[0];
if (shorten == nameNOunderscores && deletefile.Name != nameNOunderscores + "_" + intmax + ".dwg")
{
//Keep track of count of Files that are no good to us so we can iterate to next set of files
removedcount = removedcount + 1;
}
else
{
//Copy the "Good" file to a seperate directory
File.Copy(#"H:\Temp\Test\" + deletefile.Name, #"H:\Temp\AllFinals\" + deletefile.Name, true);
}
}
WithUnderscores.Clear();
list.Clear();
}
i = i + removedcount;
}
else
{
//This File had no versions so it is good to be copied to the "Good" directory
File.Copy(#"H:\Temp\SH_Plats\" + filename, #"H:\Temp\AllFinals" + filename, true);
i = i + 1;
}
}
I've made a Regex based solution, and apparently come late to the party in the meantime.
(?<fileName>[A-Za-z0-9-# ]*)_?(?<version>[0-9]+)?\.dwg
this regex will recognise the fileName and version and split them into groups, a pretty simple foreach loop to get the most recent files in a dictionary (cos I'm lazy) and then you just need to put the fileNames back together again before you access them.
var fileName = file.Key + "_" + file.Value + ".dwg"
full code
var files = new[] {
"ABBIE 08-10 #6-09H4 FINAL 06-12-2012.dwg",
"ABBIE 08-10 #6-09H4 FINAL 06-12-2012_1.dwg",
"ABBIE 08-10 #6-09H4 FINAL 06-12-2012_2.dwg",
"Second File.dwg",
"Second File_1.dwg",
"Third File.dwg"
};
// regex to split fileName from version
var r = new Regex( #"(?<fileName>[A-Za-z0-9-# ]*)_?(?<version>[0-9]+)?\.dwg" );
var latestFiles = new Dictionary<string, int>();
foreach (var f in files)
{
var parsedFileName = r.Match( f );
var fileName = parsedFileName.Groups["fileName"].Value;
var version = parsedFileName.Groups["version"].Success ? int.Parse( parsedFileName.Groups["version"].Value ) : 0;
if( latestFiles.ContainsKey( fileName ) && version > latestFiles[fileName] )
{
// replace if this file has a newer version
latestFiles[fileName] = version;
}
else
{
// add all newly found filenames
latestFiles.Add( fileName, version );
}
}
// open all most recent files
foreach (var file in latestFiles)
{
var fileToCopy = File.Open( file.Key + "_" + file.Value + ".dwg" );
// ...
}
You can use this Linq query with Enumerable.GroupBy which should work(now tested):
var allFiles = Directory.EnumerateFiles(sourceDir, "*.dwg")
.Select(path => new
{
Path = path,
FileName = Path.GetFileName(path),
FileNameWithoutExtension = Path.GetFileNameWithoutExtension(path),
VersionStartIndex = Path.GetFileNameWithoutExtension(path).LastIndexOf('_')
})
.Select(x => new
{
x.Path,
x.FileName,
IsVersionFile = x.VersionStartIndex != -1,
Version = x.VersionStartIndex == -1 ? new Nullable<int>()
: x.FileNameWithoutExtension.Substring(x.VersionStartIndex + 1).TryGetInt(),
NameWithoutVersion = x.VersionStartIndex == -1 ? x.FileName
: x.FileName.Substring(0, x.VersionStartIndex)
})
.OrderByDescending(x => x.Version)
.GroupBy(x => x.NameWithoutVersion)
.Select(g => g.First());
foreach (var file in allFiles)
{
string oldPath = Path.Combine(sourceDir, file.FileName);
string newPath;
if (file.IsVersionFile && file.Version.HasValue)
newPath = Path.Combine(versionPath, file.FileName);
else
newPath = Path.Combine(noVersionPath, file.FileName);
File.Copy(oldPath, newPath, true);
}
Here's the extension method which i'm using to determine if a string is parsable to int:
public static int? TryGetInt(this string item)
{
int i;
bool success = int.TryParse(item, out i);
return success ? (int?)i : (int?)null;
}
Note that i'm not using regex but string methods only.
Try this
var files = new My.Computer().FileSystem.GetFiles(#"c:\to\the\sample\directory", Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, "*.dwg");
foreach (String f in files) {
Console.WriteLine(f);
};
NB: Add a reference to Microsoft.VisualBasic and use the following line at the beginning of the class:
using My = Microsoft.VisualBasic.Devices;
UPDATE
The working sample[tested]:
String dPath=#"C:\to\the\sample\directory";
var xfiles = new My.Computer().FileSystem.GetFiles(dPath, Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, "*.dwg").Where(c => Regex.IsMatch(c,#"\d{3,}\.dwg$"));
XElement filez = new XElement("filez");
foreach (String f in xfiles)
{
var yfiles = new My.Computer().FileSystem.GetFiles(dPath, Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, string.Format("{0}*.dwg",System.IO.Path.GetFileNameWithoutExtension(f))).Where(c => Regex.IsMatch(c, #"_\d+\.dwg$"));
if (yfiles.Count() > 0)
{
filez.Add(new XElement("file", yfiles.Last()));
}
else {
filez.Add(new XElement("file", f));
};
};
Console.Write(filez);
Can you do this by string sort? The only tricky part I see here is to convert the file name to a sortable format. Just do a string replace from dd-mm-yyyy to yyyymmdd. Then, sort the the list and get the last record out.
This is what you want considering fileList contain all file names
List<string> latestFiles=new List<string>();
foreach(var groups in fileList.GroupBy(x=>Regex.Replace(x,#"(_\d+\.dwg$|\.dwg$)","")))
{
latestFiles.Add(groups.OrderBy(s=>Regex.Match(s,#"\d+(?=\.dwg$)").Value==""?0:int.Parse(Regex.Match(s,#"\d+(?=\.dwg$)").Value)).Last());
}
latestFiles has the list of all new files..
If fileList is bigger,use Threading or PLinq
I have 4 text files in one folder and a pattern.txt to compare these text files..In pattern.txt i have
insert
update
delete
drop
I need to compare this text file with those four text files and if these patterns matches any line in that text files i have to write those lines in another log file...i had read those files using linq..i need to compare those files and write in a text file with line number..here is my code
var foldercontent = Directory.GetFiles(pathA)
.Select(filename => File.ReadAllText(filename))
.Aggregate(new StringBuilder(),
(sb, s) => sb.Append(s).Append(Environment.NewLine),
sb => sb.ToString());
var pattern = File.ReadAllLines(pathB).Aggregate(new StringBuilder(),
(sb, s) => sb.Append(s).Append(Environment.NewLine),
sb => sb.ToString());
using (var dest = File.AppendText(Path.Combine(_logFolderPath, "log.txt")))
{
//dest.WriteLine("LineNo : " + counter.ToString() + " : " + "" + line);
}
EDIT
I have already used c# to compare two text files but i need this in linq
while ((line = file.ReadLine()) != null)
{
if (line.IndexOf(line2, StringComparison.CurrentCultureIgnoreCase) != -1)
{
dest.WriteLine("LineNo : " + counter.ToString() + " : " + " " + line.TrimStart());
}
counter++;
}
file.BaseStream.Seek(0, SeekOrigin.Begin);
counter = 1;
There might be a simpler solution, but this is at least working if you really want to use LINQ:
var foldercontent = Directory.GetFiles(pathA)
.Select(filename => new
{
Filename = filename,
Lines = File.ReadAllLines(filename)
})
.SelectMany(file => file.Lines.Select((line, idx) => new
{
LineNumber = idx + 1,
Text = line,
FileName = file.Filename
}));
var pattern = File.ReadAllLines(pathB);
var result = from fileLine in foldercontent
where pattern.Any(p => fileLine.Text.IndexOf(p, StringComparison.CurrentCultureIgnoreCase) != -1)
select fileLine;
foreach (var match in result)
{
System.Diagnostics.Debug.WriteLine("File: {0} LineNo: {1}: Text: {2}", match.FileName, match.LineNumber, match.Text);
}
Or if you want, you can combine that into one LINQ query (but thats not very readable i think):
var result = from fileLine in (Directory.GetFiles(pathA)
.Select(filename => new
{
Filename = filename,
Lines = File.ReadAllLines(filename)
})
.SelectMany(file => file.Lines.Select((line, idx) => new
{
LineNumber = idx + 1,
Text = line,
FileName = file.Filename
})))
where File.ReadAllLines(pathB).Any(p => fileLine.Text.IndexOf(p, StringComparison.CurrentCultureIgnoreCase) != -1)
select fileLine;
Since I'm a linq enthusiast, and will sometimes use a tool when it's inappropriate (I agree with #juharr about using grep or something similar for this situation) here is a possible version for you.
static IEnumerable<string> CreateMatchesLog(string patternFilePath, string pathToSearch)
{
string logTemplate = "File {0}, Line: {1}, Pattern: {2}";
DirectoryInfo di = new DirectoryInfo(pathToSearch);
var patternlines = File.ReadAllLines(patternFilePath);
var fileslines = di.EnumerateFiles().Select(fi => File.ReadAllLines(fi.FullName).Select((line, i) => new {fi.FullName, line, i}));
return from filelines in fileslines
from pattern in patternlines
from fileline in filelines
where fileline.line.Contains(pattern)
select String.Format(logTemplate, fileline.FullName, fileline.i + 1, pattern);
}
Then you'd write the output of this function to a file.
using (StreamWriter sw = new StreamWriter("log.txt", true))
{
foreach (var log in CreateMatchesLog("pattern.txt", #"c:\test"))
{
sw.WriteLine(log);
}
}
I've set append to true in the StreamWriter, because I assume you don't want to lose the contents of the file each time you run the programme.
It looks pretty inefficient (not tested that aspect), but it uses linq and lambdas up the wazoo!