I am having trouble with opening up a StreamReader object in C#. I always receive the exception Illegal characters in path. The only way I can get it to work is if I use the entire path name using the # symbol to not escape any \ characters in the file path. However, this doesn't really help me because I start with the two separate variables for the file's path and the file's name as output from another method (which can not be changed).
I've gone through eight permutations that all failed, which are commented out below for reference. For the sake of readability here, pretend I'm declaring dirIni and fileIni instead of receiving their values as output from another method. With that said, both declaration style 1 and 2 failed using all four concatenation methods. What is going on here? I've pretty much seen all four concatenation methods work in other examples.
EDIT:
I've simplified the code to show 1 version of what breaks for me:
string dirIni = #"C:\Users\Dan\AppData\Local\MyApp 4.0\INI\";
string fileIni = #"PWTRANSACTION.INI";
try
{
string transIniFullFileName = Path.Combine(dirIni, fileIni);
using (StreamReader file = new StreamReader(transIniFullFileName))
{
// do StreamReader stuff...
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Here is the exception's stack trace:
at System.IO.Path.CheckInvalidPathChars(String path)
at System.IO.Path.GetFileName(String path)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)
at System.IO.StreamReader..ctor(String path)
at TestApp.Form1.btnTestRead_Click(Object sender, EventArgs e) in C:\Users\Dan\TestApp\Form1.cs:line 4977
The issue was the method was returning a string which had a Field Separator character at the end, and that was the illegal character. I was also using these strings in my test code. I pasted my code into notepad++ and toggled on "Show Hidden Characters" and I could see the FS character then. After removing the FS, everything ran properly. Thank you all for helping me test the code, especially #Dynguss.
Any time VS gives the Illegal Character exception, check for potential hidden characters!
The following should work:
string dirIni = #"C:\Users\Dan\AppData\Local\MyApp 4.0\INI";
string fileIni = "PWTRANSACTION.INI";
string transIniFullFileName = Path.Combine(dirIni, fileIni);
and to avoid hardcoding the Local Application Data folder:
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string dirIni = #"MyApp 4.0\INI";
string fileIni = "PWTRANSACTION.INI";
string transIniFullFileName = Path.Combine(localAppData, dirIni, fileIni);
By the way the following two string declarations are perfectly identical:
string transIniFullFileName = "C:\\Users\\Dan\\AppData\\Local\\MyApp 4.0\\INI\\PWTRANSACTION.INI";
string transIniFullFileName = #"C:\Users\Dan\AppData\Local\MyApp 4.0\INI\PWTRANSACTION.INI";
So if you are saying that the first fails but the second succeeds, well, I guess there's something else that's failing and you are not showing us your real code.
I had this same error and for me it was caused by using a string instead of a path in StreamReader
string[] blocks = content.Split(';');
foreach(string block in blocks)
{
StreamReader strReader = new StreamReader(block);
Debug.WriteLine(block);
}
I removed the StreamReader line and got what I wanted.
string[] blocks = content.Split(';');
foreach(string block in blocks)
{
Debug.WriteLine(block);
}
Related
I am pretty new to C# and I am trying to get my program to copy a file from one location to another. The method I have is as below;
private void CopyInstallFiles(object sender, EventArgs e)
{
string sourceFile = "F:\\inetpub\ftproot\test.txt";
string copyPathone = directoryImput.Text;
System.IO.File.Copy(sourceFile, copyPathone);
}
As you can is there is a fixed source location however the destination is taken from user input (text box). The problem I have however, is that when I try to copy to a location for example C:\testfolder. I get an illegal character exception.
Look at your sourceFile string and be aware of using the \, which could be interpreted as escape character.
To prevent this start your string with #
string sourceFile = #"F:\inetpub\ftproot\test.txt";
or
string sourceFile = "F:\\inetpub\\ftproot\\test.txt";
File.Copy requires the full filename for the destination.
destFileName
Type: System.String
The name of the destination file. This cannot be a directory.
If your input is just the folder name then you need to add the filename of the source file.
private void CopyInstallFiles(object sender, EventArgs e)
{
// The correct syntax for a path name requires the verbatim # char
string sourceFile = #"F:\inetpub\ftproot\test.txt";
string file = Path.GetFileName(sourceFile);
string copyPathone = directoryImput.Text;
System.IO.File.Copy(sourceFile, Path.Combine(copyPathone, file), true);
}
Note the final parameter = true to overwrite a file in the destination folder.
As a side note, I suggest you to remove the textbox as input for a folder name but instead use the FolderBrowserDialog
Try this :
string path = #"C:\Program Files (x86)\your\path\main.txt";
This is because in C# (and C++ and C and some other languages) string can contain special characters. Those characters are followed by '\'. So for example string:
"\n"
Will not show you \n This is special character called - new line. So, when you create path like that:
"C:\Dir\file.txt"
C# expects that there are two special characters: \D and \f. But there is no special characters like that. Thus the error.
To put character '\' into string you have to double it, so:
"\\n"
would output \n
The same is with paths: "C:\Dir\file.txt"
C# has an alternative. You can have single '\' in path, but such a string must be followed by at sign (#):
string properPath = #"C:\dir\file.txt";
string properPath2 = "C:\\dir\\file.txt";
string error = "C:\dir\file.txt"
Either FIle.Copy
Move it to new location like below
new_file_path = file_path.Replace(".xls", " created on " + File.GetLastWriteTime(file_path).ToString("dd-MM-yyyy hh-mm-ss tt") + ".xls");
File.Move(file_path, new_file_path);
File.Delete(file_path);
I have a 'safe' config file saving routine that tries to be atomic when writing user config data to the disk, avoiding disk caching etc.
The code goes something like this:
public static void WriteAllTextSafe(string path, string contents)
{
// generate a temp filename
var tempPath = Path.GetTempFileName();
// create the backup name
var backup = path + ".backup";
// delete any existing backups
if (File.Exists(backup))
File.Delete(backup);
// get the bytes
var data = Encoding.UTF8.GetBytes(contents);
if (File.Exists(path))
{
// write the data to a temp file
using (var tempFile = File.Create(tempPath, 4096, FileOptions.WriteThrough))
tempFile.Write(data, 0, data.Length);
// replace the contents
File.Replace(tempPath, path, backup, );
}
else
{
// if the file doesn't exist we can't replace so just write it
using (var tempFile = File.Create(path, 4096, FileOptions.WriteThrough))
tempFile.Write(data, 0, data.Length);
}
}
On most systems this works perfectly and I have not had any reports of issues, but for some users each time my program calls this function they get the following error:
System.IO.IOException: 置換するファイルを置換されるファイルに移動できません。置換されるファイルの名前は、元のままです。
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalReplace(String sourceFileName, String destinationFileName, String destinationBackupFileName, Boolean ignoreMetadataErrors)
at download.ninja.BO.FileExtensions.WriteAllTextSafe(String path, String contents)
at download.ninja.BO.FileExtensions.SaveConfig(String path, Object toSave)
After a bit of investigation and with the help of Google Translate I've found that the actual error is thrown from the wine32 ReplaceFile function:
ERROR_UNABLE_TO_MOVE_REPLACEMENT 1176 (0x498)
The replacement file could not be renamed. If lpBackupFileName was specified, the replaced and
replacement files retain their original file names. Otherwise, the replaced file no longer exists
and the replacement file exists under its original name.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365512(v=vs.85).aspx
The problem is that I have no idea why Windows is throwing this error.. I have tried setting the file to readonly locally but that throws an Unauthorized exception rather than a IOException so I don't believe that is causing the problem.
My second guess is that the the is somehow locked, but I only have one read function that is used for reading all config files and that should be closing off all file handles when it finsihes
public static T LoadJson<T>(string path)
{
try
{
// load the values from the file
using (var r = new StreamReader(path))
{
string json = r.ReadToEnd();
T result = JsonConvert.DeserializeObject<T>(json);
if (result == null)
return default(T);
return result;
}
}
catch (Exception)
{
}
return default(T);
}
I have also tried throwing fake exceptions in the LoadJson function to try and lock the file but I can't seem to do it.
Even then I have tried simulating a file lock by opening the file in a different process and running the save code while it is still open, and that generates a different (expected) error:
System.IO.IOException: The process cannot access the file because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalReplace(String sourceFileName, String destinationFileName, String destinationBackupFileName, Boolean ignoreMetadataErrors)
at download.ninja.BO.FileExtensions.WriteAllTextSafe(String path, String contents)
So the question is.. what is causing Windows to throw this ERROR_UNABLE_TO_MOVE_REPLACEMENT error on some systems
NOTE: This error is thrown EVERY TIME my program attempt to replace the file on an affected machine.. not just occasionally.
OK, so found the problem. It seems that files passed into ReplaceFile must be on the SAME DRIVE
In this case the user had changed their temp folder to d:\tmp\
So File.Replace looked something like this:
File.Replace(#"D:\tmp\sometempfile", #"c:\AppData\DN\app-config", #"c:\AppData\DN\app-config.backup");
This difference in drives between the source file and the desitnation file in File.Replace seems to be what caused the problem.
I have modified my WriteAllTextSafe function as follows and my user reports the problem has been resolved!
public static void WriteAllTextSafe(string path, string contents)
{
// DISABLED: User temp folder might be on different drive
// var tempPath = Path.GetTempFileName();
// use the same folder so that they are always on the same drive!
var tempPath = Path.Combine(Path.GetDirectoryName(path), Guid.NewGuid().ToString());
....
}
As far as I can find there is no documentation stating that the two files need to be on the same drive but I can reproduce the problem by manually entering different drives (yet valid paths) into File.Replace
Are you by any chance, hardcoding drives in your file name, e.g. c:\ etc?
If you are, don't, they might not not work in runtimes where the locale is 1041 (japanese).
Instead use the framework to get the drive part and dynamically build your path.
string drivePath = System.IO.Path.GetPathRoot(System.Environment.SystemDirectory);
string somePath = string.Concat(drivePath, "someFolder\SomeFileName.Txt");
I've had the same problem working with source files on OneDrive copying to local files under %AppData%. A solution that worked well for me was to delete the destination file then perform a copy from the source.
Code that does not work:
file.replace(source,destination,backup,false)
Code that does work:
File.Delete(destination)
File.Copy(source, destination)
I have the following logics below that get called once every 20 minutes via a Timer, it serializes the content of the object into a file path, The filePath i see is \hard disk\logs\applicationstate.xml , please note I confirm this is a valid path..
It works most of the time but every now and then I get the System.IO.IOeException on the line this.StreamWriter = new StreamWriter(filePath); with the following stack stack trace:
at System.IO.__Error.WinIOError(Int32 errorCode, String str)\r\n at
System.IO.FileStream..ctor(String path, FileMode mode, FileAccess
access, FileShare share, Int32 bufferSize, Boolean useAsync, String
msgPath)\r\n at System.IO.FileStream..ctor(String path, FileMode
mode, FileAccess access, FileShare share, Int32 bufferSize)\r\n at
System.IO.StreamWriter.CreateFile(String path, Boolean append)\r\n
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding
encoding, Int32 bufferSize)\r\n at
System.IO.StreamWriter..ctor(String path)\r\n at
Shs.ScanPanel.CA.DataManager.DataManagercr.CopyData(Object data)\r\n
at System.Threading.Timer.ring()\r\n"
When it happens I see \hard disk\logs\applicationstate.xml exists but it has 0 byte.
So my question is, could the StreamWriter cause this 0 byte file to be generated in the first place? I read up on the IOException under StreamWriter on MSDN and it says the following
IOException
path includes an incorrect or invalid syntax for file name, directory name, or volume label syntax.
This confused me, is it because it tries to open a stream writer to a 0 byte file? could this 0 byte generated in the last time this code runs where a null object was being serialized into the file? if so why didn't I see that exception in Visual Studio?
if (filePath != string.Empty)
{
if (this.StateObject == null)
{
this.StateObject = new State();
}
//Do something to my StateObject object
this.StreamWriter = new StreamWriter(filePath);
this.Serializer = new XmlSerializer(typeof(State));
this.Serializer.Serialize(this.StreamWriter, this.StateObject);
}
else
{
if (this.log != null)
{
this.log.Write(LogLevel.Error, this.componentName, "CopyData : Unable to initilize State Object");
}
}
}
else
{
if (this.log != null)
{
this.log.Write(LogLevel.Error, this.componentName, "CopyData : Error while retrieving Current working directory");
}
}
}
catch (Exception ex)
{
if (this.log != null)
{
this.log.Write(ex, this.componentName);
}
}
finally
{
if (this.StreamWriter != null)
{
this.StreamWriter.Close();
}
}
I recommend using this.StreamWriter.Flush() to make sure all the contents are written.
However you exception appears to be complaining that the path is incorrect.
Edit: Opps I missed the WinCE tag
So I wrote up a little program and I confirmed that the line that makes the file to have 0 byte is right at the this.StreamWriter = new StreamWriter(filePath);
But what really boggle my mind is that it successfully wipe out the file so that new data can be serialized into it but yet at the same time it throws exception. I'm thinking this is a lower layer of the StreamWriter api or it could be something with the flash drive.... after all I'm running this program on WINDOW CE
At the moment I am using to store the zip files with the file name like this...
backup-20111010092345.Zip
but i want to change the file name to this ..backup-2011-10-10_09:23:45.Zip
i have got this code ...
string zipName = Path.Combine(filepath, string.Format("backup-{0}.zip", DateTime.Now.ToString("yyyyMMddhhmmss")));
string backupFilePath = Path.Combine(filepath, backupName);
using (ZipFile zip = new ZipFile())
{
zip.AddFile(backupFilePath, "");
zip.Save(zipName);
}
string backupName = "backup.sql";
string filepath = #"C:\Folder\Back\";
would any one pls help on this...
many thanks In advance...
Modified Code:
string zipName = Path.Combine(filepath, string.Format("backup-{0:yyyy-MM-dd_HH:mm:ss}.zip", DateTime.Now));
string backupFilePath = Path.Combine(filepath, backupName);
using (ZipFile zip = new ZipFile())
{
zip.AddFile(backupFilePath, "");
zip.Save(zipName);
}
Error :Notsupported Exception was unhandled
this is stack trace .
at System.Security.Util.StringExpressionSet.CanonicalizePath(String path, Boolean needFullPath)
at System.Security.Util.StringExpressionSet.CreateListFromExpressions(String[] str, Boolean needFullPath)
at System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList)
at System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String[] pathList, Boolean checkForDuplicates, Boolean needFullPath)
at System.IO.File.Move(String sourceFileName, String destFileName)
at Ionic.Zip.ZipFile.Save()
at Ionic.Zip.ZipFile.Save(String fileName)
error: The given path's format is not supported.
Sounds like you've nearly got it (in terms of building the name that you specified) - you just need to change the format string
string zipName = Path.Combine(filepath,
string.Format("backup-{0}.zip",
DateTime.Now.ToString("yyyy-MM-dd_HH:mm:ss"));
You could specify that as:
string zipName = Path.Combine(filepath,
string.Format("backup-{0:yyyy-MM-dd_HH:mm:ss}.zip",
DateTime.Now));
It's up to you which you find more readable.
Note that this will use the time separator for the current culture. If you always want it to be "colon" then you should quote it. On the other hand, is colon even a valid character in Windows filenames? Consider using dash again, or something similar. For example:
string zipName = Path.Combine(filepath,
string.Format("backup-{0:yyyy-MM-dd_HH-mm-ss}.zip",
DateTime.Now));
You'll need to use something other than : as it is reserved. I suggest something like:
DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss");
As well as the build in formats, you can get the individual components of a DateTime object using its properties like myDate.Year etc. These are detailed on MSDN here:
http://msdn.microsoft.com/en-us/library/991wfdee(v=VS.90).aspx
So if you wanted some really odd formatting you could put together a composite string from each component part in whatever pattern you want.
I have an app that "cleans" "dirty" filenames. "Dirty" filenames have #%&~+{} in their filenames. What my app does is see if they are a match for a RegEx pattern i have defined and then send it to a method called FileCleanUp where it "cleans" the file and replaces invalid chars with a "". However, i noticed while i was running this, that my FileCleanup method only works on SOME files and not others!
Here is my code:
public class SanitizeFileNames
{
public void FileCleanup(List<string>paths)
{
string regPattern = (#"[~#&!%+{}]+");
string replacement = "";
Regex regExPattern = new Regex(regPattern);
foreach (string files2 in paths)
try
{
string filenameOnly = Path.GetFileName(files2);
string pathOnly = Path.GetDirectoryName(files2);
string sanitizedFileName = regExPattern.Replace(filenameOnly, replacement);
string sanitized = Path.Combine(pathOnly, sanitizedFileName);
//write to streamwriter
System.IO.File.Move(files2, sanitized);
}
catch (Exception e)
{
//write to streamwriter
}
}
I tested on a few files with the names like: ~Test.txt, #Test.txt, +Text.txt, Test&Test.txt, T{e}st.txt, Test%.txt.
The ones i could not get to be renamed were: ~Test.txt, +Test.txt, T{e}st.txt
I debugged this and weirdly enough, it shows that these files that did not get renamed for some reason as correct on the debugger. Instead of showing ~Test.txt as a "sanitized" file name, it was Text.txt. So on the app side, it DOES read my foreach loop correctly.
However, i'm really stumped as to why it's not actually renaming these files. Anybody have a clue as to why this might be? Does it have to do with the File.Move() ?
EDIT: On further testing, i realize that it also doesn't rename files that are like this: ~~test.txt or ##test.txt
You have an empty catch block. Why don't you print out the exception message to see what's happening?
My guess is that the file names are matching the regex, but you're unable to rename the files because a file named Test.txt already exists after #Test.txt is renamed to that. Try renaming the other files to ~Test2.txt, +Test3.txt, T{e}st4.txt before running the program again.