Is there any function in c# to shink a file path ?
Input: "c:\users\Windows\Downloaded Program Files\Folder\Inside\example\file.txt"
Output: "c:\users\...\example\file.txt"
Is there any function in c# to shink a file path ?
Input: "c:\users\Windows\Downloaded Program Files\Folder\Inside\example\file.txt"
Output: "c:\users\...\example\file.txt"
Jeff Atwood posted a solution to this on his blog and here it is :
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
static extern bool PathCompactPathEx([Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);
static string PathShortener(string path, int length)
{
StringBuilder sb = new StringBuilder();
PathCompactPathEx(sb, path, length, 0);
return sb.ToString();
}
It uses the unmanaged function PathCompactPathEx
to achieve what you want.
CharSet.Ansi
"solved" (?) the problem. –
Deterioration sb = new StringBuilder(length + 1)
. –
Ammon StringBuilder
by default allocates 16 characters, which is often not enough to store the result. You need to use length or MAX_PATH = 260 to avoid the possible memory corruption. –
Brill Nasreddine answer was nearly correct. Just specify StringBuilder size, in your case:
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
static extern bool PathCompactPathEx(
[Out] StringBuilder pszOut,
string szPath,
int cchMax,
int dwFlags);
static string PathShortener(string path, int length)
{
StringBuilder sb = new StringBuilder(length + 1);
PathCompactPathEx(sb, path, length, 0);
return sb.ToString();
}
That looks less human readable to me. Anyway, I don't think there is such a function. split it on the \ character and just keep the first two slots and the last two slots and you have it.
Something like this, although that code is not very elegant
string[] splits = path.Split('\\');
Console.WriteLine( splits[0] + "\\" + splits[1] + "\\...\\" + splits[splits.Length - 2] + "\\" + splits[splits.Length - 1]);
if (splits.Length > 4)
test in there too. –
Ease C:\veryveryveryveryverylong\supersupersuperlong\a\b\c\d.txt
... What exactly will your solution solve? –
Bedrabble Jeff Atwood posted a solution to this on his blog and here it is :
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
static extern bool PathCompactPathEx([Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);
static string PathShortener(string path, int length)
{
StringBuilder sb = new StringBuilder();
PathCompactPathEx(sb, path, length, 0);
return sb.ToString();
}
It uses the unmanaged function PathCompactPathEx
to achieve what you want.
CharSet.Ansi
"solved" (?) the problem. –
Deterioration sb = new StringBuilder(length + 1)
. –
Ammon StringBuilder
by default allocates 16 characters, which is often not enough to store the result. You need to use length or MAX_PATH = 260 to avoid the possible memory corruption. –
Brill If you want, do insert ellipsis dependent on the length of the path string, then use this code:
TextRenderer.MeasureText(path, Font,
new System.Drawing.Size(Width, 0),
TextFormatFlags.PathEllipsis | TextFormatFlags.ModifyString);
It will modify path
in-place.
EDIT: Be careful with this method. It breaks the rule, saying that strings in .NET are immutable. In fact, the first parameter of the MeasureText
method is not a ref
parameter, which means that no new string can be returned. Instead, the existing string is altered. It would be careful to work on a copy created with
string temp = String.Copy(path);
You could use something like:
public string ShrinkPath(string path, int maxLength)
{
List<string> parts = new List<string>(path.Split('\\'));
string start = parts[0] + @"\" + parts[1];
parts.RemoveAt(1);
parts.RemoveAt(0);
string end = parts[parts.Count-1];
parts.RemoveAt(parts.Count-1);
parts.Insert(0, "...");
while(parts.Count > 1 &&
start.Length + end.Length + parts.Sum(p=>p.Length) + parts.Count > maxLength)
parts.RemoveAt(parts.Count-1);
string mid = "";
parts.ForEach(p => mid += p + @"\");
return start+mid+end;
}
Or just use Olivers solution, which is much easier ;-).
I was just faced with this issue as long paths were becoming a complete eye sore. Here is what I tossed together real quick (mind the sloppiness) but it gets the job done.
private string ShortenPath(string path, int maxLength)
{
int pathLength = path.Length;
string[] parts;
parts = label1.Text.Split('\\');
int startIndex = (parts.Length - 1) / 2;
int index = startIndex;
string output = "";
output = String.Join("\\", parts, 0, parts.Length);
decimal step = 0;
int lean = 1;
do
{
parts[index] = "...";
output = String.Join("\\", parts, 0, parts.Length);
step = step + 0.5M;
lean = lean * -1;
index = startIndex + ((int)step * lean);
}
while (output.Length >= maxLength && index != -1);
return output;
}
EDIT
Below is an update with Merlin2001's corrections.
private string ShortenPath(string path, int maxLength)
{
int pathLength = path.Length;
string[] parts;
parts = path.Split('\\');
int startIndex = (parts.Length - 1) / 2;
int index = startIndex;
String output = "";
output = String.Join("\\", parts, 0, parts.Length);
decimal step = 0;
int lean = 1;
while (output.Length >= maxLength && index != 0 && index != -1)
{
parts[index] = "...";
output = String.Join("\\", parts, 0, parts.Length);
step = step + 0.5M;
lean = lean * -1;
index = startIndex + ((int)step * lean);
}
// result can be longer than maxLength
return output.Substring(0, Math.Min(maxLength, output.Length));
}
IndexOutOfRangeException
if the path doesn't fit even with all parts replaced by ...
. To fix this (and keep at least the drive letter and the last directory) I would suggest changing the do..while
to a while
and check for index != 0 && index < parts.Length - 1
instead of just index != -1
. This way you don't run into exceptions and don't add superfluous ellipses. –
Kilter int
instead of Int32
–
Boothman ShortenPath(@"C:\Temp\A folder\B folder\C folder\D folder\E folder\", 20)
without the Substring the result is 35 characters C:\...\...\...\...\...\...\F folder
–
Cortese private string ShrinkPath(string path, int maxLength)
{
var parts = path.Split('\\');
var output = String.Join("\\", parts, 0, parts.Length);
var endIndex = (parts.Length - 1);
var startIndex = endIndex / 2;
var index = startIndex;
var step = 0;
while (output.Length >= maxLength && index != 0 && index != endIndex)
{
parts[index] = "...";
output = String.Join("\\", parts, 0, parts.Length);
if (step >= 0) step++;
step = (step * -1);
index = startIndex + step;
}
return output;
}
MeasureString
. Thanks. –
Butlery Nearly all answers here shorten the path string by counting characters. But this approach ignores the width of each character.
These are 30 'W' characters:
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
These are 30 'i' characters:
iiiiiiiiiiiiiiiiiiiiiiiiiiiiii
As you see, counting characters is not really useful.
And there is no need to write your own code because the Windows API has this functionaly since Windows 95.
The name of this functionality is "Path Ellipsis".
The Windows API DrawTextW()
has a flag DT_PATH_ELLIPSIS
which does exactly this.
In the .NET framwork this is available (without the need to use PInvoke) in the TextRenderer
class.
There are 2 ways how this can be used:
1.) Drawing the path directly into a Label:
public class PathLabel : Label
{
protected override void OnPaint(PaintEventArgs e)
{
if (AutoSize)
throw new Exception("You must set "+Name+".AutoSize = false in VS "
+ "Designer and assign a fix width to the PathLabel.");
Color c_Fore = Enabled ? ForeColor : SystemColors.GrayText;
TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, c_Fore,
BackColor, TextFormatFlags.PathEllipsis);
}
}
This label requires you to turn AutoEllipsis
off in Visual Studio Designer and assign a fix width to the Label (the maximum width that your path should occupy).
You even see the truncated path in Visual Studio Designer.
I entered a long path which does not fit into the label:
C:\WINDOWS\Installer{40BF1E83-20EB-11D8-97C5-0009C5020658}\ARPPRODUCTICON.exe
Even in Visual Studio Designer it is displayed like this:
2.) Shorten the path without drawing it on the screen:
public static String ShortenPath(String s_Path, Font i_Font, int s32_Width)
{
TextRenderer.MeasureText(s_Path, i_Font, new Size(s32_Width, 100),
TextFormatFlags.PathEllipsis | TextFormatFlags.ModifyString);
// Windows inserts a '\0' character into the string instead of shortening the string
int s32_Nul = s_Path.IndexOf((Char)0);
if (s32_Nul > 0)
s_Path = s_Path.Substring(0, s32_Nul);
return s_Path;
}
The flag TextFormatFlags.ModifyString
inserts a '\0' character into the string. It is very unusual that a string is modified in C#.
Normally strings are unmutable.
This is because the underlying API DrawTextW()
works this way.
But as the string is only shortened and never will become longer there is no risk of a buffer overflow.
The following code
String s_Text = @"C:\WINDOWS\Installer{40BF1E83-20EB-11D8-97C5-0009C5020658}\ARPPRODUCTICON.exe";
s_Text = ShortenPath(s_Text, new Font("Arial", 12), 500);
will result in "C:\WINDOWS\Installer{40BF1E83-20EB-1...\ARPPRODUCTICON.exe"
If you want to write you own solution to this problem, use build in classes like: FileInfo, Directory, etc... which makes it less error prone.
The following code produces "VS style" shortened path like: "C:\...\Folder\File.ext".
public static class PathFormatter
{
public static string ShrinkPath(string absolutePath, int limit, string spacer = "…")
{
if (string.IsNullOrWhiteSpace(absolutePath))
{
return string.Empty;
}
if (absolutePath.Length <= limit)
{
return absolutePath;
}
var parts = new List<string>();
var fi = new FileInfo(absolutePath);
string drive = Path.GetPathRoot(fi.FullName);
parts.Add(drive.TrimEnd('\\'));
parts.Add(spacer);
parts.Add(fi.Name);
var ret = string.Join("\\", parts);
var dir = fi.Directory;
while (ret.Length < limit && dir != null)
{
if (ret.Length + dir.Name.Length > limit)
{
break;
}
parts.Insert(2, dir.Name);
dir = dir.Parent;
ret = string.Join("\\", parts);
}
return ret;
}
}
© 2022 - 2024 — McMap. All rights reserved.