GNU readline whitespace quoting - c++

i´m working on an application which uses readline to read commands from stdin.
It accepts "cd", and other commands which require a path as an argument. I'm having troubles with paths that include whitespaces. My objective is to somehow make readline quote the whitespaces, and autocomplete the path name after this character appears(actually, when a space is encountered, it is just skipped, and autocompletion starts from the next word).
I've been trying to achieve this, but i keep trying things and none of them work. I've managed to quote a " " into a "\ ", which is what i want. But then readline doesn't interpret this as part of the path, it just skips it, and autocompletes the next word as if there was nothing before that. Basically, i'm expecting the same behaviour as bash's autocompletion.
Any help is appreciated.
Thanks in advance!
Edit:
Alright, so i've managed to somehow accomplish what i was looking for. What i did was:
During initialization:
rl_attempted_completion_function = completition;
rl_completer_quote_characters = "\"";
rl_filename_quote_characters = " ";
completition should return a char** containing every command that matches what "text" as so far. I've ommitted that part, since it doesn't have to do with what i was asking. The important part is the rl_filename_quoting_desired = 1; which tells readline that you want your filenames to be quoted.
char **completition(const char *text, int start, int end) {
rl_filename_quoting_desired = 1;
return 0;
}
Note that what i ended up doing is what BuHHu-nyx said, just adding double quotes(") to filenames.

Try to escape not spaces but the whole path. For example:
cd "/path/to/some where"

Related

Question marks in wildcard search in Windows

I am using wildcard characters (? and *) to search for files in Windows in a c++ program with _tfindfirst64 and _tfindnext64. I observed the following code
TCHAR root[1024] = L"C:/testData/?????_?????.jpg";
_tfinddata64_t c_file;
intptr_t hFile = _tfindfirst64(root, &c_file);
do
{
wcout << c_file.name << endl;
} while (_tfindnext64(hFile, &c_file) == 0);
_findclose(hFile);
picks up the following files:
12345_---.jpg
12345_12.jpg
12345_12345.jpg
But I was expected the filter would accept only the last one as it means: "5 arbitrary chars followed by '_' followed by another chars with '.jpg'" as described. What would be the correct way to do it?
As you've found, a ? in a file search doesn't require a character to be present, but matching will fail if a character is present that your search string doesn't account for. For example, foo?.txt will match foo.txt, foo1.txt, fooa.txt, and so on, but will not match foo10.txt or foo_abc.txt.
About the only way I know of to work around this is to re-check the result afterward to assure you eliminate any unwanted matches. In this case, it sounds like just comparing the length of the filename with what you expect will suffice.

JSONCPP is adding extra double quotes to string

I have a root in JSONcpp having string value like this.
Json::Value root;
std::string val = "{\"stringval\": \"mystring\"}";
Json::Reader reader;
bool parsingpassed = reader.parse(val, root, false);
Now when I am trying to retrieve this value using this piece of code.
Json::StreamWriterBuilder builder;
builder.settings_["indentation"] = "";
std::string out = Json::writeString(builder, root["stringval"]);
here out string ideally should be giving containing:
"mystring"
whereas it is giving output like this:
"\"mystring\"" \\you see this in debug mode if you check your string content
by the way if you print this value using stdout it will be printed something like this::
"mystring" \\ because \" is an escape sequence and prints " in stdout
it should be printing like this in stdout:
mystring \\Expected output
Any idea how to avoid this kind of output when converting json output to std::string ?
Please avoid suggesting fastwriter as it also adds newline character and it deprecated API as well.
Constraint: I do not want to modify the string by removing extra \" with string manipulation rather I am willing to know how I can I do that with JSONcpp directly.
This is StreamWriterBuilder Reference code which I have used
Also found this solution, which gives optimal solution to remove extra quotes from your current string , but I don't want it to be there in first place
I had this problem also until I realized you have to use the Json::Value class accessor functions, e.g. root["stringval"] will be "mystring", but root["stringval"].asString() will be mystring.
Okay so This question did not get answer after thorough explanation as well and I had to go through JSONCPP apis and documentation for a while.
I did not find any api as of now which takes care of this scenario of extra double quote addition.
Now from their wikibook I could figure out that some escape sequences might come in String. It is as designed and they haven't mentioned exact scenario.
\" - quote
\\ - backslash
\/ - slash
\n - newline
\t - tabulation
\r - carriage return
\b - backspace
\f - form feed
\uxxxx , where x is a hexadecimal digit - any 2-byte symbol
Link Explaining what all extra Escape Sequence might come in String
Anyone coming around this if finds out better explanation for the same issue , please feel free to post your answer.Till then I guess only string manipulation is the option to remove those extra escape sequence..

Regex to Split by comma and remove text delimiters [duplicate]

I have a text file that is in a comma separated format, delimited by " on most fields. I am trying to get that into something I can enumerate through (Generic Collection, for example). I don't have control over how the file is output nor the character it uses for the delimiter.
In this case, the fields are separated by a comma and text fields are enclosed in " marks. The problem I am running into is that some fields have quotation marks in them (i.e. 8" Tray) and are accidentally being picked up as the next field. In the case of numeric fields, they don't have quotes around them, but they do start with a + or a - sign (depicting a positive/negative number).
I was thinking of a RegEx, but my skills aren't that great so hopefully someone can come up with some ideas I can try. There are about 19,000 records in this file, so I am trying to do it as efficiently as possible. Here are a couple of example rows of data:
"00","000000112260 ","Pie Pumpkin ","RET","6.99 "," ","ea ",+0000000006.99000
"00","000000304078 ","Pie Apple caramel ","RET","9.99 "," ","ea ",+0000000009.99000
"00","StringValue here","8" Tray of Food ","RET","6.99 "," ","ea ",-00000000005.3200
There are a lot more fields, but you can get the picture....
I am using VB.NET and I have a generic List setup to accept the data. I have tried using CSVReader and it seems to work well until you hit a record like the 3rd one (with a quote in the text field). If I could somehow get it to handle the additional quotes, than the CSVReader option will work great.
Thanks!
I recommend looking at the TextFieldParserClass in .Net. You need to include
Imports Microsoft.VisualBasic.FileIO.TextFieldParser
Here's a quick sample:
Dim afile As FileIO.TextFieldParser = New FileIO.TextFieldParser(FileName)
Dim CurrentRecord As String() ' this array will hold each line of data
afile.TextFieldType = FileIO.FieldType.Delimited
afile.Delimiters = New String() {","}
afile.HasFieldsEnclosedInQuotes = True
' parse the actual file
Do While Not afile.EndOfData
Try
CurrentRecord = afile.ReadFields
Catch ex As FileIO.MalformedLineException
Stop
End Try
Loop
From here:
Encoding fileEncoding = GetFileEncoding(csvFile);
// get rid of all doublequotes except those used as field delimiters
string fileContents = File.ReadAllText(csvFile, fileEncoding);
string fixedContents = Regex.Replace(fileContents, #"([^\^,\r\n])""([^$,\r\n])", #"$1$2");
using (CsvReader csv =
new CsvReader(new StringReader(fixedContents), true))
{
// ... parse the CSV
As this link says... Don't roll your own CSV parser!
Use TextFieldParser as Avi suggested. Microsoft has already done this for you. If you ended up writing one, and you find a bug in it, consider replacing it instead of fixing the bug. I did just that recently and it saved me a lot of time.
Give a look to the FileHelpers library.
You could give CsvHelper (a library I maintain) a try and it's available via NuGet. It follows the RFC 4180 standard for CSV. It will be able to handle any content inside of a field including commas, quotes, and new lines.
CsvHelper is simple to use, but it's also easy to configure it to work with many different types of delimited files.
CsvReader csv = new CsvReader( streamToFile );
IEnumerable<MyObject> myObjects = csv.GetRecords<MyObject>();
If you want to read CSV files on a lower level, you can use the parser directly, which will return each row as a string array.
var parser = new CsvParser( myTextReader );
while( true )
{
string[] line = parser.ReadLine();
if( line == null )
{
break;
}
}
I am posting this as an answer so I can explain how I did it and why.... The answer from Mitch Wheat was the one that gave me the best solution for this case and I just had to modify it slightly due to the format this data was exported in.
Here is the VB Code:
Dim fixedContents As String = Regex.Replace(
File.ReadAllText(csvFile, fileEncoding),
"(?<!,)("")(?!,)",
AddressOf ReplaceQuotes)
The RegEx that was used is what I needed to change because certain fields had non-escaped quotes in them and the RegEx provided didn't seem to work on all examples. This one uses 'Look Ahead' and 'Look Behind' to see if the quote is just after a comma or just before. In this case, they are both negative (meaning show me where the double quote is not before or after a comma). This should mean that the quote is in the middle of a string.
In this case, instead of doing a direct replacement, I am using the function ReplaceQuotes to handle that for me. The reason I am using this is because I needed a little extra logic to detect whether it was at the beginning of a line. If I would have spent even more time on it, I am sure I could have tweaked the RegEx to take into consideration the beginning of the line (using MultiLine, etc) but when I tried it quickly, it didn't seem to work at all.
With this in place, using CSV reader on a 32MB CSV file (about 19000 rows), it takes about 2 seconds to read the file, perform the regex, load it into the CSV Reader, add all the data to my generic class and finish. Real quick!!
RegEx to exclude first and last quote would be (?<!^)(?<!,)("")(?!,)(?!$). Of course, you need to use RegexOptions.Multiline.
That way there is no need for evaluator function. My code replaces undesired double quotes with single quotes.
Complete C# code is as below.
string fixedCSV = Regex.Replace(
File.ReadAllText(fileName),
#"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);
There are at least ODBC drivers for CSV files. But there are different flavors of CSV.
What produced these files? It's not unlikely that there's a matching driver based on the requirements of the source application.
Your problem with CSVReader is that the quote in the third record isn't escaped with another quote (aka double quoting). If you don't escape them, then how would you expect to handle ", in the middle of a text field?
http://en.wikipedia.org/wiki/Comma-separated_values
(I did end up having to work with files (with different delimiters) but the quote characters inside a text value weren't escaped and I ended up writing my own custom parser. I do not know if this was absolutely necessary or not.)
The logic of this custom approach is: Read through file 1 line at a time, split each line on the comma, remove the first and last character (removing the outer quotes but not affecting any inside quotes), then adding the data to your generic list. It's short and very easy to read and work with.
Dim fr As StreamReader = Nothing
Dim FileString As String = ""
Dim LineItemsArr() as String
Dim FilePath As String = HttpContext.Current.Request.MapPath("YourFile.csv")
fr = New System.IO.StreamReader(FilePath)
While fr.Peek <> -1
FileString = fr.ReadLine.Trim
If String.IsNullOrEmpty(FileString) Then Continue While 'Empty Line
LineItemsArr = FileString.Split(",")
For Each Item as String In LineItemsArr
'If every item will have a beginning and closing " (quote) then you can just
'cut the first and last characters of the string here.
'i.e. UpdatedItems = Item. remove first and last character
'Then stick the data into your Generic List (Of String()?)
Next
End While
public static Encoding GetFileEncoding(String fileName)
{
Encoding Result = null;
FileInfo FI = new FileInfo(fileName);
FileStream FS = null;
try
{
FS = FI.OpenRead();
Encoding[] UnicodeEncodings = { Encoding.BigEndianUnicode, Encoding.Unicode, Encoding.UTF8 };
for (int i = 0; Result == null && i < UnicodeEncodings.Length; i++)
{
FS.Position = 0;
byte[] Preamble = UnicodeEncodings[i].GetPreamble();
bool PreamblesAreEqual = true;
for (int j = 0; PreamblesAreEqual && j < Preamble.Length; j++)
{
PreamblesAreEqual = Preamble[j] == FS.ReadByte();
}
if (PreamblesAreEqual)
{
Result = UnicodeEncodings[i];
}
}
}
catch (System.IO.IOException)
{
}
finally
{
if (FS != null)
{
FS.Close();
}
}
if (Result == null)
{
Result = Encoding.Default;
}
return Result;
}

_mkdir strange behaviour c++

I'm struggling with a strange issue.
I want to create a folder for the solution of my program and I've written the following code:
timer = time(NULL);
path.assign(".\\Solution_");
path.append(ctime(&timer));
ch1 = ':';
ch2 = '_';
for (i = 0; i < path.length(); ++i) {
if (path[i] == ch1 || path[i] == ' ')
path[i] = ch2;
}
mkdir(path.c_str());
it doesn't create anything even if path is equal to ".\Solution_Thu_Jun_12_10_10_31_2014" and if I type
mkdir(".\\test");
It works correctly.
I did many test and I ckeck all I could but I cannot figure out what's wrong.
Can anyone help me please?
Best,
Roberto
Detail:
Even if I use mkdir(path.c_str()); with path equal to "Solution_Thu_Jun_12_11_52_04_2014" (without any \) it doesn't work anyway and on the other side 'mkdir("test");' works.
Solution:
Ok there was a hidden newline.
Thanks everyone for the help :-)!!!
The issue you are having is that the backslash character is the start of an escape sequence. These were used to input a non-printable character into the stream (tabs, carriage returns, etc.).
So your program is seeing a backslash and expecting that the next character will be a command character. If it sees another backslash then it knows that you want a backslash and not an escape sequence, hence why \\ works and \ doesn't.
Since you are running a flavour of Windows you could use CreateDirectory function.
Also what you have put as your example is C, not C++. What source are you learning from?
Check if there is newline or any other illegal character for filename in windows. Your error looks like that.
windows doesn't allow these character <>?/\| or newline in filename

C++ - Replace spaces with %20

I am looking for a way to prepare a string for use as a URL.
The basics of the code is you type in what you are looking for and it opens a browser with what you typed in. I am learning C++, so this is a learning program. And please be as specific as possible for I am new to C++.
Here is what I am trying to do:
cin >> s_input;
transform(s_input.begin(), s_input.end(), s_input.begin(), tolower);
s_input = "start http://website.com/" + s_input + "/0/7/0";
system(s_input.c_str());
But I am trying to replace all the spaces the user enter with a '%20'. I have found one method this way but it only works with one letter at a time, and I am needing to do it with a full string and not an array of chars. Here is the method I have tried:
cin >> s_input;
transform(s_input.begin(), s_input.end(), s_input.begin(), tolower);
using std::string;
using std::cout;
using std::endl;
using std::replace;
replace(s_input.begin(), s_input.end(), ' ', '%20');
s_input = "start http://website.com/" + s_input + "/0/7/0";
system(s_input.c_str());
Thanks for your help!
If you have Visual Studio 2010 or later you should be able to use regular expressions to search/replace:
std::regex space("[[:space:]]");
s_input = std::regex_replace(s_input, space, "%20");
Edit: How to use the six-argument version of std::regex_replace:
std::regex space("[[:space:]]");
std::string s_output;
std::regex_replace(s_output.begin(), s_input.begin(), s_input.end(), space, "%20");
The string s_output now contains the changed string.
You might have to change the replacement string to std::string("%20").
As you see I have only five arguments, that's because the sixth should have a default value.
std::replace is only able to replace single elements (chars in this case) with single elements. You are trying to replace a single element with three. You will need a special function to do that. Boost has one, called replace_all, you can use it like this:
boost::replace_all(s_input, " ", "%20");
If you google: C++ UrlEncode, you will find many hits. Here's one:
http://www.zedwood.com/article/111/cpp-urlencode-function