C++ question:
I have the query in txt like this:
CUSTOMER_ID=4155&ORDER_ITEM_TYPE_ID=1&ORDER_ITEM_TYPE_NAME=Product&ORDER_ITEM_SKU=&ORDER_CURRENCY_CODE=UAH
How can I get these fields to make it look nearly like:
Customer_ID: 4155
ORDER_ITEM_TYPE_ID: 1
// and etc...
I know there is a separator "&" between each field, but I don't know how to do it properly.
Use methods of std::string like find,substr. Example:
const std::string field = "CUSTOMER_ID=4155&ORDER_ITEM_TYPE_ID=1&ORDER_ITEM_TYPE_NAME=Product&ORDER_ITEM_SKU=&ORDER_CURRENCY_CODE=UAH";
const char separator = '&';
const char equal = '=';
std::string::size_type cur = 0;
while ( cur != std::string::npos )
{
std::string name;
std::string value;
std::string::size_type newPos = field.find(equal, cur);
if ( newPos != std::string::npos )
{
name = field.substr( cur, newPos - cur );
cur = newPos + 1;
newPos = field.find(separator, cur);
value = field.substr( cur, ( newPos == std::string::npos ) ? std::string::npos : newPos - cur );
cur = ( newPos == std::string::npos ) ? std::string::npos : newPos + 1;
std::cout << name << ": " << value << std::endl;
}
else
{
break;
}
}
try
`char str[10][20];
int len = 0;
int count = 0;
while((c = string[i])!= EOC )
/* EOC : End of Character */
{
if (c == '&') {
str[count][len] = '\0';
len = 0;
count++;
continue;
}
str[count][len] = c;
len++;
}
Replace all '&' with replace and then when you write the data after this back into a file it's formated as requested.
std::ifstream infile("thefile.txt");
while (std::getline(infile, line)) //Read File line by line
{
std::replace( line.begin(), line.end(), '&', '\0'); // replace all '&' to '\0'
cout << line << endl;
}
Related
I'm a beginner in c++ and I'm training on exercises.
I'm stuck on one part. I would like to insert the string "CH" in front of each vowel in a sentence.
I first tried using string::replace, but it was not the best idea.
I would like to use string::insert to do this.
However, I can't seem to use it properly in a loop to tell it that the [i] is the desired position
Do you have any advice for me?
string message = "you have a secret message to decrypt";
string newMessage = "";
string InsertMessage = "CH";
for (int i = 0; i < message.length(); i++) {
if (
message[i] == 'a' ||
message[i] == 'e' ||
message[i] == 'i' ||
message[i] == 'o' ||
message[i] == 'u' ||
message[i] == 'y')
{
//newMessage = message.replace(i, 1, InsertMessage);
newMessage = message.insert(i, InsertMessage);
}
}
cout << newMessage << endl;
return 0;
This statement
newMessage = message.insert(i, InsertMessage);
does not make a sense at least because it changes the original string message.
I can suggest the following approach shown in the demonstration program below.
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
std::string message = "you have a secret message to decrypt";
std::string newMessage;
std::string InsertMessage = "CH";
const char *vowels = "aeiouy";
auto n = std::count_if( std::begin( message ), std::end( message ),
[vowels]( const auto &c ) { return std::strchr( vowels, c ); } );
newMessage.resize( message.size() + n * InsertMessage.size() );
std::string::size_type pos = 0;
do
{
auto i = message.find_first_of( vowels, pos );
if (i == std::string::npos)
{
newMessage += message.substr( pos );
pos = i;
}
else
{
newMessage += message.substr( pos, i - pos );
newMessage += InsertMessage;
pos = i;
newMessage += message[pos++];
}
} while( pos != std::string::npos );
std::cout << newMessage << '\n';
}
The program output is
CHyCHoCHu hCHavCHe CHa sCHecrCHet mCHessCHagCHe tCHo dCHecrCHypt
Thanks for your answers, I'll work on it :)
I'm building this webcrawler here. This error occurs to me when I start debugging and sends me to memcpy.asm or xstring or dbgdel.cpp files showing me different lines of these files every time.
I was wondering if the code is wrong somehow. I started thinking I am accessing memory blocks that I shouldn't. Here is some code. I hope you can help.
The idea is to iterate through httpContent and get all the URLs from the <a> tags. I am looking for href=" in the beginning and then for the next ". What is in between I am trying to put in temp, then pass the content of temp to an array of strings.
struct Url
{
string host;
string path;
};
int main(){
struct Url website;
string href[100];
website.host = "crawlertest.cs.tu-varna.bg";
website.path = "";
string httpContent = downloadHTTP(website);
for(unsigned int i = 0; i <= httpContent.length()-7; i++){
char c = httpContent[i];
if(c == 'h'){
c = httpContent[i+1];
if(c == 'r'){
c = httpContent[i+2];
if(c == 'e'){
c = httpContent[i+3];
if(c == 'f'){
c = httpContent[i+4];
if(c == '='){
c = httpContent[i+5];
if(c == '\"'){
i+=6;
c = httpContent[i];
string temp = "";
while(c!='\"'){
i++;
c = httpContent[i];
temp+= c;
}
href[i] = temp;
temp = "";
cout<<href[i]<<endl;
}}}}}}
}
system("pause");
return 0;
}
UPDATE
I edited the =, now ==
I am also stopping the iterations 7 positions earlier so the 'if's should not be problem.
I am getting the same errors though.
Use std::vector< std::string > href; to store your result.
With string::find you can find sequence in strings and with string::substr you can extract them from string.
#include <vetor>
#include <string>
struct Url
{
string host;
string path;
};
int main(){
struct Url website;
website.host = "crawlertest.cs.tu-varna.bg";
website.path = "";
std::string httpContent = downloadHTTP(website);
std::vector< std::string > href;
std::size_t pos = httpContent.find("href="); // serach for first "href="
while ( pos != string::npos )
{
pos = httpContent.find( '"', pos+5 ); // serch for '"' at start
if ( pos != string::npos )
{
std::size_t posSt = pos + 1;
pos = httpContent.find( '"', posSt ); // search for '"' at end
if ( pos != string::npos )
{
href.push_back( httpContent.substr( posSt, pos - posSt ) ); // extract ref and append to result
pos = httpContent.find( "href=", pos+1 ); // search for next "href="
}
}
}
system("pause");
return 0;
}
I have one array like this:
static WCHAR FilesToShow[][100] = { { L"start.cmd" },{ L"image.xml" }, { L"xyz" }};
as you see that there is "xyz" which I have to replace with some unique name. For this I have to read image.xml file.
Please can you tell me how can I do this.
I wrote a method like this:
PRIVATE WCHAR GetSystemName(WCHAR *pName)
{
WCHAR line;
wfstream in("image.xml");
WCHAR tmp;
bool begin_tag = false;
while (getline(in,line))
{
// strip whitespaces from the beginning
for (int i = 0; i < line.length(); i++)
{
if (line[i] == ' ' && tmp.size() == 0)
{
}
else
{
tmp += line[i];
}
}
if (wcswcs(tmp,"<SystemPath>") != NULL)
{
???????? how to get "vikash" from here <SystemPath>C:\Users\rs_user\Documents\RobotStudio\Systems\vikash</SystemPath>
}
else
{
continue;
}
}
return tmp;
}
I'm getting exception for wfstream, getline and line.length() method.
I have included fstream.h header file but I think It's not supported in COM.
Please help me how to solve this issue without parsing xml file.
If your xml-file is simple enough so that there is only a single tag with given name, you could do it like this:
#include <string>
#include <sstream>
#include <iostream>
std::wstring get_value(std::wistream & in, std::wstring const & tagname)
{
std::wstring text = std::wstring(std::istreambuf_iterator<std::wstring::value_type>(in),
std::istreambuf_iterator<std::wstring::value_type>());
std::wstring start_tag = L"<" + tagname + L">";
std::wstring end_tag = L"</" + tagname + L">";
std::wstring::size_type start = text.find(start_tag);
if (start == std::wstring::npos)
{
throw 123;
}
start += start_tag.length();
std::wstring::size_type end = text.find(end_tag);
if (end == std::wstring::npos)
{
throw 123;
}
return text.substr(start, end - start);
}
std::wstring get_substr_after(std::wstring const & str, wchar_t delim)
{
std::wstring::size_type pos = str.rfind(delim);
if (pos == std::wstring::npos)
{
throw 123;
}
return str.substr(pos + 1);
}
void stackoverflow()
{
std::wstring text(L"<foo>\n<bar>abc/def/ghi</bar>\n<baz>123/456/789</baz>\n</foo>\n");
std::wistringstream wiss(text);
std::wcout << text << std::endl;
std::wcout << get_substr_after(get_value(wiss, std::wstring(L"bar")), L'/') << std::endl;
}
The output of this program is:
<foo>
<bar>abc/def/ghi</bar>
<baz>123/456/789</baz>
</foo>
ghi
I hope that answered your question.
you have several issues here.
what you are getting are compiler errors and not exceptions
the header file to include is 'fstream' not 'fstream.h'.
make sure you have a line saying using namespace std;
You are declaring line as a variable of type WCHAR, so it is a single wide character, which surely is not a wstring object. Therefore line.length() is incorrect.
Why are you mixing C (wcswcs()) and C++ (STL) ? maybe you should re-design your function signature.
However, try the below function. I have modified the signature to return a pointer to WCHAR, and place the requested string in the buffer space provided by pName. I added a check to verify that the buffer is large enough to fit the name and the terminating NULL character.
WCHAR* GetSystemName(WCHAR *pName, size_t buflen)
{
wstring line;
wifstream in("image.xml");
WCHAR* tmp = NULL;
while (getline(in,line))
{
// strip whitespaces from the beginning
size_t beg_non_whitespace = line.find_first_not_of(L" \t");
if (beg_non_whitespace != wstring::npos)
{
line = line.substr( beg_non_whitespace );
}
size_t beg_system_path = line.find( L"<SystemPath>" );
if ( beg_system_path != wstring::npos )
{
// strip the tags (assuming closing tag is present)
size_t beg_data = beg_system_path + wstring( L"<SystemPath>" ).length();
size_t range = line.find( L"</SystemPath>" ) - beg_data;
line = line.substr( beg_data, range );
// get file name
size_t pos_last_backslash = line.find_last_of( L'\\' );
if ( pos_last_backslash != wstring::npos )
{
line = line.substr( pos_last_backslash + 1 );
if ( buflen <= line.length() )
{
// ERROR: pName buffer is not large enough to fit the string + terminating NULL character.
return NULL;
}
wcscpy( pName, line.c_str() );
tmp = pName;
break;
}
}
}
return tmp;
}
EDIT: Moreover, if you are using and/or parsing XML in other areas of your program, I strongly suggest using an XML parsing library such as Xerces-C or libXml2.
Thank you all for your answer. Here I got solution of my question.
PRIVATE WCHAR* GetNewSystemName()
{
WCHAR line[756];
WCHAR tempBuffer[100];
CComBSTR path = CurrentFolder.Path();
CComBSTR imagePath1 = L"rimageinfo.xml";
path.AppendBSTR(imagePath1);
std::wfstream in(path);
WCHAR tmp[756];
in.getline(line, 756);
WCHAR* buffer;
buffer = wcswcs(line, L"<SystemPath>");
WCHAR *dest = wcsstr(buffer, L"</SystemPath>");
int pos;
pos = dest - buffer;
unsigned int i = 0;
if (wcswcs(buffer,L"<SystemPath>") != NULL && wcswcs(buffer,L"</SystemPath>") != NULL)
{
for (; i < pos; i++)
{
if (buffer[i] == ' ' && sizeof(tmp) == 0)
{
}
else
{
tmp[i] = buffer[i];
}
}
tmp[i] = NULL;
//break;
}
int j = i;
for (; j > 0; j--)
{
if (tmp[j] == '\\')
{
break;
}
}
j++;
int k = 0;
for (; j < i ; j++)
{
System_Name[k] = tmp[j];
k++;
}
System_Name[k] = NULL;
return System_Name;
In C/C++, how can I extract from c:\Blabla - dsf\blup\AAA - BBB\blabla.bmp the substrings AAA and BBB ?
i.e. extract the parts before and after - in the last folder of a filename.
Thanks in advance.
(PS: if possible, with no Framework .net or such things, in which I could easily get lost)
#include <iostream>
using namespace std;
#include <windows.h>
#include <Shlwapi.h> // link with shlwapi.lib
int main()
{
char buffer_1[ ] = "c:\\Blabla - dsf\\blup\\AAA - BBB\\blabla.bmp";
char *lpStr1 = buffer_1;
// Remove the file name from the string
PathRemoveFileSpec(lpStr1);
string s(lpStr1);
// Find the last directory name
stringstream ss(s.substr(s.rfind('\\') + 1));
// Split the last directory name into tokens separated by '-'
while (getline(ss, s, '-'))
cout << s << endl;
}
Explanation in comments.
This doesn't trim leading spaces - in the output - if you also want to do that - check this.
This can relatively easily be done with regular expressions:
std::regex if you have C++11; boost::regex if you don't:
static std::regex( R"(.*\\(\w+)\s*-\s*(\w+)\\[^\\]*$" );
smatch results;
if ( std::regex_match( path, results, regex ) ) {
std::string firstMatch = results[1];
std::string secondMatch = results[2];
// ...
}
Also, you definitely should have the functions split and
trim in toolkit:
template <std::ctype_base::mask test>
class IsNot
{
std::locale ensureLifetime;
std::ctype<char> const* ctype; // Pointer to allow assignment
public:
Is( std::locale const& loc = std::locale() )
: ensureLifetime( loc )
, ctype( &std::use_facet<std::ctype<char>>( loc ) )
{
}
bool operator()( char ch ) const
{
return !ctype->is( test, ch );
}
};
typedef IsNot<std::ctype_base::space> IsNotSpace;
std::vector<std::string>
split( std::string const& original, char separator )
{
std::vector<std::string> results;
std::string::const_iterator current = original.begin();
std::string::const_iterator end = original.end();
std::string::const_iterator next = std::find( current, end, separator );
while ( next != end ) {
results.push_back( std::string( current, next ) );
current = next + 1;
next = std::find( current, end, separator );
}
results.push_back( std::string( current, next ) );
return results;
}
std::string
trim( std::string const& original )
{
std::string::const_iterator end
= std::find_if( original.rbegin(), original.rend(), IsNotSpace() ).base();
std::string::const_iterator begin
= std::find_if( original.begin(), end, IsNotSpace() );
return std::string( begin, end );
}
(These are just the ones you need here. You'll obviously want
the full complement of IsXxx and IsNotXxx predicates, a split
which can split according to a regular expression, a trim which
can be passed a predicate object specifying what is to be
trimmed, etc.)
Anyway, the application of split and trim should be obvious
to give you what you want.
This does all the work and validations in plain C:
int FindParts(const char* source, char** firstOut, char** secondOut)
{
const char* last = NULL;
const char* previous = NULL;
const char* middle = NULL;
const char* middle1 = NULL;
const char* middle2 = NULL;
char* first;
char* second;
last = strrchr(source, '\\');
if (!last || (last == source))
return -1;
--last;
if (last == source)
return -1;
previous = last;
for (; (previous != source) && (*previous != '\\'); --previous);
++previous;
{
middle = strchr(previous, '-');
if (!middle || (middle > last))
return -1;
middle1 = middle-1;
middle2 = middle+1;
}
// now skip spaces
for (; (previous != middle1) && (*previous == ' '); ++previous);
if (previous == middle1)
return -1;
for (; (middle1 != previous) && (*middle1 == ' '); --middle1);
if (middle1 == previous)
return -1;
for (; (middle2 != last) && (*middle2 == ' '); ++middle2);
if (middle2 == last)
return -1;
for (; (middle2 != last) && (*last == ' '); --last);
if (middle2 == last)
return -1;
first = (char*)malloc(middle1-previous+1 + 1);
second = (char*)malloc(last-middle2+1 + 1);
if (!first || !second)
{
free(first);
free(second);
return -1;
}
strncpy(first, previous, middle1-previous+1);
first[middle1-previous+1] = '\0';
strncpy(second, middle2, last-middle2+1);
second[last-middle2+1] = '\0';
*firstOut = first;
*secondOut = second;
return 1;
}
The plain C++ solution (without boost, nor C++11), still the regex solution of James Kanze (https://stackoverflow.com/a/16605408/1032277) is the most generic and elegant:
inline void Trim(std::string& source)
{
size_t position = source.find_first_not_of(" ");
if (std::string::npos != position)
source = source.substr(position);
position = source.find_last_not_of(" ");
if (std::string::npos != position)
source = source.substr(0, position+1);
}
inline bool FindParts(const std::string& source, std::string& first, std::string& second)
{
size_t last = source.find_last_of('\\');
if ((std::string::npos == last) || !last)
return false;
size_t previous = source.find_last_of('\\', last-1);
if (std::string::npos == last)
previous = -1;
size_t middle = source.find_first_of('-',1+previous);
if ((std::string::npos == middle) || (middle > last))
return false;
first = source.substr(1+previous, (middle-1)-(1+previous)+1);
second = source.substr(1+middle, (last-1)-(1+middle)+1);
Trim(first);
Trim(second);
return true;
}
Use std::string rfind rfind (char c, size_t pos = npos)
Find character '\' from the end using rfind (pos1)
Find next character '\' using rfind (pos2)
Get the substring between the positions pos2 and pos1. Use substring function for that.
Find character '-' (pos3)
Extract 2 substrings between pos3 and pos1, pos3 and pos2
Remove the spaces in the substrings.
Resulting substrings will be AAA and BBB
I have a problem rewriting a loop:
else if( "d" == option || "debug" == option )
{
debug(debug::always) << "commandline::set_internal_option::setting debug options: "
<< value << ".\n";
string::size_type index = 0;
do
{
const string::size_type previous_index = index+1;
index=value.find( ',', index );
const string item = value.substr(previous_index, index);
debug::type item_enum;
if( !map_value(lib::debug_map, item, item_enum) )
throw lib::commandline_error( "Unknown debug type: " + item, argument_number );
debug(debug::always) << "commandline::set_internal_option::enabling " << item
<< " debug output.\n";
debug(debug::always) << "\n-->s_level=" << debug::s_level << "\n";
debug::s_level = static_cast<debug::type>(debug::s_level ^ item_enum);
debug(debug::always) << "\n-->s_level=" << debug::s_level << "\n";
} while( index != string::npos );
}
value is something like string("commandline,parser") and the problem is that in the first run, I need substr(previous_index, index), but in every subsequent iteration I need substr(previous_index+1, index) to skip over the comma. Is there some easy way I'm overlooking or will I have to repeat the call to find outside the loop for the initial iteration?
Since your goal is to prevent code duplication:
std::vector<std::string> v;
boost::split(v, value, [](char c) { c == ','; });
If you want to create your own split function, you can do something like this:
template<typename PredicateT>
std::vector<std::string> Split(const std::string & in, PredicateT p)
{
std::vector<std::string> v;
auto b = in.begin();
auto e = b;
do {
e = std::find_if(b, in.end(), p);
v.emplace_back(b,e);
b = e + 1;
} while (e != in.end());
return v;
}
Why not update previous_index after taking the substr?
string::size_type index = 0;
string::size_type previous_index = 0;
do {
index=value.find( ',', previous_index );
const string item = value.substr(previous_index, index);
previous_index = index+1;
} while( index != string::npos );
Unchecked, but this should do the trick (with only one more word of memory).
Start at -1?
string::size_type index = -1;
do
{
const string::size_type previous_index = index + 1;
index=value.find(',', previous_index);
const string item = value.substr(previous_index, index - previous_index);
} while( index != string::npos );
A stupid (and somewhat unreadable) solution would be something like this:
string::size_type once = 0;
/* ... */
const string::size_type previous_index = index+1 + (once++ != 0); // or !!once
First, I think there's a small error:
In your code, the expression index=value.find( ',', index ); doesn't change the value of index if it already is the index of a comma character within the string (which is always the case except for the first loop iteration).
So you might want to replace while( index != string::npos ); with while( index++ != string::npos ); and previous_index = index+1 with previous_index = index.
This should also solve your original problem.
To clarify:
string::size_type index = 0;
do
{
const string::size_type previous_index = index;
index = value.find( ',', index );
const string item = value.substr(previous_index, index - previous_index);
} while( index++ != string::npos );