Skip to content

Instantly share code, notes, and snippets.

@amankkg
Last active August 29, 2015 14:15
Show Gist options
  • Save amankkg/5ff4c961a1585da7c2ff to your computer and use it in GitHub Desktop.
Save amankkg/5ff4c961a1585da7c2ff to your computer and use it in GitHub Desktop.
Fix for bad Cyrillic encoding in the name of files
// <copyright file="FileNamesEncoder.cs" company="none">
// All is OK, guys! It's just a StyleCop-ism
// </copyright>
// <author>code4aman</author>
namespace FileNamesEncoder
{
using System;
using System.IO;
using System.Linq;
using System.Text;
/// <summary>
/// encode file names and folder names, root path to start from must be defined, source and final encoding numbers are optional
/// </summary>
public class FileNamesEncoder
{
#region Fields
/// <summary>
/// root directory to start from
/// </summary>
private string rootPath;
/// <summary>
/// bad names encoding number
/// </summary>
private int initialCodingNumber;
/// <summary>
/// names encoding number to-be
/// </summary>
private int finalCodingNumber;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the FileNamesEncoder class, default values assume cyrillic encoding problem for Windows (8.1) SL (english) edition (from CP437 to IBM866)
/// </summary>
/// <param name="root">folder to start from, required parameter</param>
/// <param name="sourceEncodingCode">initial encoding code/number, default is 437</param>
/// <param name="resultEncodingCode">final encoding code/number, default is 866</param>
public FileNamesEncoder(string root, int sourceEncodingCode = 437, int resultEncodingCode = 866)
{
this.rootPath = root;
this.initialCodingNumber = sourceEncodingCode;
this.finalCodingNumber = resultEncodingCode;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets root directory to start from
/// </summary>
public string RootPath
{
get { return this.rootPath; }
set { this.rootPath = value; }
}
/// <summary>
/// Gets or sets bad names encoding number, (i.e. 437 for CP437)
/// </summary>
public int InitialCodingNumber
{
get { return this.initialCodingNumber; }
set { this.initialCodingNumber = value; }
}
/// <summary>
/// Gets or sets names encoding number to-be (i.e. 866 for IBM866)
/// </summary>
public int FinalCodingNumber
{
get { return this.finalCodingNumber; }
set { this.finalCodingNumber = value; }
}
#endregion
#region Public methods
/// <summary>
/// start processing folders, root = RootPath property
/// </summary>
public void StartProcessingDirectories()
{
if (!Directory.Exists(this.rootPath))
{
this.ProcessDirectories(this.rootPath);
}
}
#endregion
#region Private methods
/// <summary>
/// renames folders (and subfolders, recursively) and files of each of them
/// </summary>
/// <param name="folderPath">start/root folder</param>
private void ProcessDirectories(string folderPath)
{
var dirs = Directory.GetDirectories(folderPath).ToList();
for (int i = 0; i < dirs.Count; i++)
{
var oldName = Path.GetFileName(dirs[i]);
if (!this.ContainsCyrillicChar(oldName))
{
var newName = this.GetCorrectString(oldName);
// different newName means encoding was valuable and string has been modified
// if folder was renamed we should save it for RenameFolderFiles() use
if (oldName != newName)
{
dirs[i] = this.RenameFolder(dirs[i], oldName, newName);
}
}
// processing each subfolder
this.ProcessDirectories(dirs[i]);
}
this.RenameFolderFiles(folderPath);
}
/// <summary>
/// renames folder
/// </summary>
/// <param name="oldPathWithName">full path with name included</param>
/// <param name="oldName">old folder name</param>
/// <param name="newName">new folder name</param>
/// <returns>name of resulting folder, if some errors occur then old name will be returned</returns>
private string RenameFolder(string oldPathWithName, string oldName, string newName)
{
var resultPathWithName = oldPathWithName;
var newPathWithName = oldPathWithName.Replace(oldName, newName);
try
{
Directory.Move(oldPathWithName, newPathWithName);
resultPathWithName = newPathWithName;
}
catch (IOException ex)
{
this.WriteLog("Renaming folder " + newPathWithName + " __issued:__ " + ex.Message);
if (Directory.Exists(newPathWithName))
{
newPathWithName += "__MERGE_ME_";
Directory.Move(oldPathWithName, newPathWithName);
resultPathWithName = newPathWithName;
this.WriteLog("Renaming folder " + newPathWithName + "successful");
}
}
return resultPathWithName;
}
/// <summary>
/// renames all files in directory
/// </summary>
/// <param name="folderPath">path of directory</param>
private void RenameFolderFiles(string folderPath)
{
var fileNames = Directory.GetFiles(folderPath).Select(p => Path.GetFileName(p)).ToList();
for (int i = 0; i < fileNames.Count; i++)
{
var oldName = fileNames[i];
if (!this.ContainsCyrillicChar(oldName))
{
var newName = this.GetCorrectString(oldName);
// different newName means encoding was valuable and string has been modified
if (oldName != newName)
{
this.RenameFile(folderPath, oldName, newName);
}
}
}
}
/// <summary>
/// renames folder
/// </summary>
/// <param name="folderPath">folder root path</param>
/// <param name="oldName">target folder name</param>
/// <param name="newName">new name</param>
private void RenameFile(string folderPath, string oldName, string newName)
{
var oldPath = folderPath + "\\" + oldName;
var newPath = folderPath + "\\" + newName;
try
{
File.Move(oldPath, newPath);
}
catch (Exception ex)
{
this.WriteLog("Renaming file " + newPath + " __issued:__ " + ex.Message);
}
}
#endregion
#region HelperMethods
/// <summary>
/// checks if string contains cyrillic alphabet characters
/// </summary>
/// <param name="name">string to check</param>
/// <returns>true if contains any cyrillic char</returns>
private bool ContainsCyrillicChar(string name)
{
// checking if file name contains any cyrillic chars, if true then file name is not 'bad encoded'
string alphabet = "абвгдеёжзийклмнопрстуфцхчшщъыьэюя";
return name.ToLower().Any(c => alphabet.Contains(c));
}
/// <summary>
/// converts source string from source encoding to result encoding, source and final encodings are taken from properties
/// </summary>
/// <param name="sourceString">string to convert</param>
/// <returns>encoded string</returns>
private string GetCorrectString(string sourceString)
{
string resultString = sourceString;
var sourceEncoding = Encoding.GetEncoding(this.initialCodingNumber);
var resultEncoding = Encoding.GetEncoding(this.finalCodingNumber);
if (sourceEncoding != null && resultEncoding != null)
{
resultString = resultEncoding.GetString(sourceEncoding.GetBytes(sourceString.ToCharArray()));
}
return resultString;
}
/// <summary>
/// prints messages log in console
/// </summary>
/// <param name="message">message text</param>
private void WriteLog(string message)
{
Console.WriteLine("\t*\t*\t*");
Console.WriteLine(DateTime.Now.ToString());
Console.WriteLine(message);
Console.WriteLine("\t*\t*\t*\n");
}
#endregion
}
}
// example usage of FileNamesEncoder class
using System;
namespace FileNamesEncoder
{
class Program
{
static void Main(string[] args)
{
var encoder = new FileNamesEncoder("C:\\Users\\code4aman");
encoder.StartProcessingDirectories();
Console.WriteLine("\nFinished");
Console.ReadLine();
}
}
}
@amankkg
Copy link
Author

amankkg commented Feb 8, 2015

added regions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment