I am trying to write a struct in php, i know there is no such thing in php, but at least get it working somehow...
C++:
// The struct
typedef struct data
{
char numbers[20];
char numbers2[50];
char number3[6];
char sometext[100];
}data_t;
data_t config;
char numbers[20] = "12345.12345";
char numbers3[6] = "12345";
char sometext[100] = "asdsadsad";
// Storing into struct
strcpy_s(config.numbers, numbers);
strcpy_s(config.numbers3, numbers3);
strcpy_s(config.sometext, sometext);
// Serializing struct to test.dat
ofstream output_file("test.dat", ios::binary);
output_file.write((char*)&config, sizeof(config));
output_file.close();
// Reading from it
ifstream input_file("test.dat", ios::binary);
input_file.read((char*)&master, sizeof(master));
cout << "NUMBERS : " << master.numbers << endl;
cout << "NUMBERS3 : " << master.numbers3 << endl;
cout << "SOMETEXT : " << master.sometext << endl;
cout << endl << endl;
Now storing with c++ in the struct, then reading it works just fine, but i want to store in that file trough php, then read it from c++, so i have:
PHP:
$data = Array();
$data['numbers'] = "12345.12345";
$data['numbers3'] = "12345";
$data['sometext'] = "abcdfghs";
$fp=fopen("test.dat","wb") or die("Stop! i kill you...");
foreach($data as $key => $value){
echo 'written:'.$value;
fwrite($fp,$value."\t");
}
Now what is happening is:
NUMBERS : 12345.12345 12345 abcdfghs
NUMBERS3 :
SOMETEXT :
So as you can see, it`s not good, also i noticed a difference when writing to file from c++ (contains binary data), while writing to file from php is just plain text.
Some help would be apreciated, many thanks!
Your C++ struct allocates 20 bytes for the numbers member. That means when you write it to the file, all 20 bytes are written, the writing doesn't just stop after writing 12345.12345. Your PHP code, on the other hand, writes exactly what is in $data['numbers'] and stops immediately (well, after adding a useless "\t"). The "binary data" you noticed in the file is just the garbage which happened to be in memory in those leftover bytes after 12345.12345. Same goes for the other fields.
Your PHP code does not write the string's terminating NULL to the file.
Your PHP code does not write the numbers2 member to the file.
You need to ensure the PHP code writes the terminating NULL, pads the output to the same size as the field has in the C++ struct, and outputs the fields in the same order as the C++ struct. You can use pack() for this:
<?php
$data = array();
$data['numbers'] = "12345.12345";
$data['numbers2'] = '';
$data['numbers3'] = "12345";
$data['sometext'] = "abcdfghs";
$packed = pack('a20a50a6a100', $data['numbers'], $data['numbers2'], $data['numbers3'], $data['sometext']);
$written = file_put_contents("test.dat", $packed);
if($written === false) {
throw new RuntimeException("Failed to write data to file!");
} else if($written !== strlen($packed)) {
throw new RuntimeException("Writing to file was not complete!");
}
Note: For maximum compatibility, you should read/write each struct member to the file individually in a consistent order on both sides. Otherwise you can have problems due to C++ field padding/alignment.
Related
I'm absolutely dumbfounded by this.
I was trying to demonstrate to myself how much faster c++ is than even modern PHP.
I ran a simple csv parsing program in both that have the same output.
The csv file is 40,194,684 parsed down to 1,537,194 lines.
PHP Code (runtime 42.750s):
<?php
$i_fp = fopen("inFile.csv","r");
$o_fp = fopen("outFile.csv","w");
while(!feof($i_fp))
{
$line = fgets($i_fp);
$split = explode(';',$line);
if($split[3] == 'E' || $split[3] == 'T')
{
fwrite($o_fp,join(',',[ $split[0], $split[1], $split[3], $split[4], $split[5], $split[6],
$split[10], $split[9],$split[11],$split[7],$split[32]])."\n");
}
}
fclose($i_fp);
fclose($o_fp);
C++ Code (runtime 3m59.074s)
(compiled using g++ parse.cpp -o parse -O2 -std=c++1y)
#include <fstream>
#include <stdlib.h>
#include <string>
#include <vector>
using std::string;
using std::vector;
vector<string> splitStr(string line, const char delimiter = ',')
{
vector<string> splitLine;
string buf;
for(size_t i=0; i<line.length(); i++)
{
if(line[i] == delimiter)
{
splitLine.push_back(buf);
buf.clear();
}else{
buf += line[i];
}
}
return splitLine;
}
string makeCSVLine(vector<string> splitLine)
{
string line =
splitLine[0] + ',' +
splitLine[1] + ',' +
splitLine[3] + ',' +
splitLine[4] + ',' +
splitLine[5] + ',' +
splitLine[6] + ',' +
splitLine[10] + ',' +
splitLine[9] + ',' +
splitLine[11] + ',' +
splitLine[7] + ',' +
splitLine[32] + '\n';
return line;
}
int main(int argc, char* argv[])
{
if(argc < 3)
{
exit(EXIT_SUCCESS);
}
string inPath = argv[1];
string outPath = argv[2];
std::ifstream inFile;
std::ofstream outFile;
inFile.open(inPath.c_str());
outFile.open(outPath.c_str());
string line;
while(std::getline(inFile,line))
{
vector<string> split = splitStr(line, ';');
if(split[3][0] == 'E' || split[3][0] == 'T')
{
outFile << makeCSVLine(split);
}
}
inFile.close();
outFile.close();
}
Both running on redhat 8.
I'm sure that its some mistake I'm making in terms of c++ efficiency (possibly somewhere in how im utilizing strings and vectors and whether they need to be re-sized repeatedly per loop)but I'm not sure what it could be. If anyone could help shed some light that would be great.
I/O is often a huge bottleneck in C++, with a fairly large overhead per function call. I've seen instances of unoptimized C++ I/O being slower than python I/O. While the rest of your code isn't heavily optimized (e.g. adding characters one at a time to buf is probably somewhat slower than using .substr()), it doesn't look terrible.
To speed stuff up, try reading and writing the file in one go each. That is, create a char* buffer the size of the input file and use one call of ifstream::read, then parse the buffer. When writing, write to a char* buffer, and at the end, write that buffer to a file with one call of ofstream::write.
I have requirement to develop a PHP server socket and a C client socket , visa versa.
I am thorough with TCP sockets in C and its concept.
I am stuck in on last thing.
I am able to send a whole structure from C client socket as follows
typedef struct _test {
char str[32];
char c;
int i;
float f;
}test;
//Some Coding ...
memset(&t,'\0',sizeof(test));
strcpy(t.str,"Sunny"); //String
t.c = 'M'; //Char
t.i = 26; //Integer
t.f = 98.8; //Float
//Send test STRUCT to server
if(send(sockfd,(void *)&t,sizeof(t),0) < 0)
{
perror("Send failed ");
exit(0);
}
//Some Coding ...
I am receiving this structure at PHP server socket as follows
...
$client = socket_accept($socket);
$input = socket_read($client, 1024);
$arr = unpack("Z32Str/a1Chr/iInt/fFlt", $input);
echo $arr['Str']; //Print String
echo $arr['Chr']; //Print Char
echo $arr['Int']; //Print Int
echo $arr['Flt']; //Print Float
...
I am getting string and char properly but am not getting Integer and Float properly , i am sure its network to host byte order (little endian,big endian) problem.
i.e. am getting integer value as 436207616
Can any one please tell me how to make equivalent fucntions to ntohl and ntohs in PHP.
P.S. :- Am quite new at PHP ... Please help
i have disabled the structure padding in C as below and it worked .....
How to disable structure padding ? As Follows ....
Following is the way to disable structure padding using pragma in C.
#pragma pack(push, 1)
//Define your structure here
#pragma pack(pop)
//Structure padding is re enabled.
#pragma pack(push,1)
typedef struct _test {
char str[32];
char c;
int i;
float f;
}test;
#pragma pack(pop)
Or:
I have kept padding on in C and do following at php side , and it worked ....
$arr = unpack("Z32Str/z4Chr/iInt/fFlt", $input);
I am trying to pass over from php a string into C++, i managed to figure out how to pass numbers, but it doesn't work for letters. Here's what i have that works for PHP
<?php
$r = 5;
$s = 12;
$x= 3;
$y= 4;
$q= "Hello World";
$c_output=`project1.exe $r $s $x $y $q`; // pass in the value to the c++ prog
echo "<pre>$c_output</pre>"; //received the sum
//modify the value in php and output
echo "output from C++ programm is" . ($c_output + 1);
?>
This sends the variables r,s,x,y, and q to the C++ programm project1.exe and IT WORKS, but the problem is that it doesn't work for the string variable $q.
Here's the code that I have in my C++ programm, it's simple:
#include<iostream>
#include<cstdlib>
#include<string>
using namespace std;
int main(int in, char* argv[]) {
int val[2];
for(int i = 1; i < in; i++) { // retrieve the value from php
val[i-1] = atoi(argv[i]);
}
double r = val[0];
double s = val[1];
double x = val[2];
double y = val[3];
double q = val[4]; // here's the problem, as soon as i try to define val[4] as a string or char, it screws up
cout << r;
cout <<s;
cout << x;
cout << y;
cout << q;
// will output to php
return 0;
}
It works, but for the string "Hello world" which i pass through $q from PHP doesn't give me the string back (i know it's defined as a double, but as soon as i try to change it to a string or a char variable the code just doesn't compile).
Please explain to me how i have to go around this problem so that $q can be processed as a string. FYI, I am a newbie to programming (6 months in).
Try not converting the final argument using atoi(argv[i]). Just keep it as argv[i].
for(int i = 1; i < in-1; i++)
{
val[i-1] = atoi(argv[i]);
}
q = argv[i];
It doesn't work for letters because you are doing atoi(..)(which converts char-string to integer) in the C++ program.
Have some means of letting the program know what to expect -- whether a number or a string. May be the first argument can help the program differentiate, like may be the following:
$c_output = `project1.exe nnsnns 1 2 string1 3 4 string2`
Then you could do:
for(int i = 0/*NOTE*/,len=strlen(argv[1]); i < len; i++) { // retrieve the value from php
if (argv[1][i] == 'n'){
//argv[2+i] must be an integer
}else if (argv[1][i] == 's'){
//argv[2+i] is a string
}
}
Of course you should check if (strlen(argv[1]) == in-2).
BTW, in the C++ code above, val is a array holding 2 ints; and you are trying to access much beyond index 1.
To pass one single string to the C++ you would do something like the following:
$output = `project1.exe $q`; //Read below.
NOTE: $q must be a single word. No spaces, no extra characters like '|', '&', or any other character which the shell might interpret differently. $q must be clean before you pass that on to C++ Program. If $q is more than one word, use quotes.
C++ Part (Just try the following, then you can modify as you go along)
cout<<argv[1]<<endl;
I can't understand the following and I'm hoping someone can shed some light on it for me:
In C++ if I create a vector of test data containing 2M different bits of text (testdata) then create a map using these strings as the index values, then look up all the values, like this:
//Create test data
for(int f=0; f<loopvalue; f++)
{
stringstream convertToString;
convertToString << f;
string strf = convertToString.str();
testdata[f] = "test" + strf;
}
time_t startTimeSeconds = time(NULL);
for(int f=0; f<2000000; f++) testmap[ testdata[f] ] = f; //Write to map
for(int f=0; f<2000000; f++) result = testmap[ testdata[f] ]; //Lookup
time_t endTimeSeconds = time(NULL);
cout << "Time taken " << endTimeSeconds - startTimeSeconds << "seconds." << endl;
It takes 10 seconds.
If I do seemingly at least the same in PHP:
<?php
$starttime = time();
$loopvalue = 2000000;
//fill array
for($f=0; $f<$loopvalue; $f++)
{
$filler = "test" . $f;
$testarray[$filler] = $f;
}
//look up array
for($f=0; $f<$loopvalue; $f++)
{
$filler = "test" . $f;
$result = $testarray[$filler];
}
$endtime = time();
echo "Time taken ".($endtime-$starttime)." seconds.";
?>
...it takes only 3 seconds.
Given that PHP is written in C does anyone know how PHP achieves this much faster text index lookup?
Thanks
C
Your loops are not absolutely equivalent algorithms.
Note that in the C++ version you have
testmap[ testdata[f] ] - this is actually a lookup + insert
testmap[ testdata[f] ] - 2 lookups
In the PHP versions you just have insert in the first loop and lookup in the second one.
PHP is interpreted - generally if you code is faster in PHP, check the code first ! ;-)
I suspect you benchmark the wrong things.
Anyway, I used your code (had to make some assumptions on your data types) and here are the results from my machine:
PHP:
Time taken 2 seconds.
C++ (using std::map):
Time taken 3 seconds.
C++ (using std::tr1::unordered_map):
Time taken 1 seconds.
C++ compiled with
g++ -03
Here is my test C++ code:
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include <tr1/unordered_map>
int main(){
const int loopvalue=2000000;
std::vector<std::string> testdata(loopvalue);
std::tr1::unordered_map<std::string, int> testmap;
std::string result;
for(int f=0; f<loopvalue; f++)
{
std::stringstream convertToString;
convertToString << f;
std::string strf = convertToString.str();
testdata[f] = "test" + strf;
}
time_t startTimeSeconds = time(NULL);
for(int f=0; f<loopvalue; f++) testmap[ testdata[f] ] = f; //Write to map
for(int f=0; f<loopvalue; f++) result = testmap[ testdata[f] ]; //Lookup
time_t endTimeSeconds = time(NULL);
std::cout << "Time taken " << endTimeSeconds - startTimeSeconds << "seconds." << std::endl;
}
Conclusion:
You tested unoptimized C++ code, probably even compiled with VC++, which by default has a bounds check in std::vector::operator[] when compiled in debug mode.
There still is a difference of PHP to the optimised C++ code, when we use std::map, because of the difference in lookup complexity (see n0rd's answer), but C++ is faster when you use a Hashmap.
According to another question, associative arrays in PHP are implemented as hash tables, which have search complexity of O(1) on average, while std::map in C++ is a binary tree with search complexity of O(log n), which is slower.
I have this code in C++, which returns outputs the following number
int main(int argn, char** argv)
{
cout << (*((unsigned long*)"P3TF")) << endl;
cin.get();
return 0;
}
How can I achieve the above in PHP (i.e. the string "P3TF" in unsigned long int). I tried using the pack method:
<?php
$lol = pack('N', 'P3TF');
var_dump( $lol, // returns jumbled up characters
ord($lol[0]), // returns int 0
ord($lol[1]), // returns int 0
ord($lol[2]), // returns int 0
ord($lol[3]), // returns int 0
ord($lol[0]).ord($lol[1]).ord($lol[2]).ord($lol[3]) // returns 4 zeros as a string.
);
?>
I need it in bigendian byte order so I haven't used pack('V') or pack('L').
Anyone know how to achieve this?
Thanks!
If it's literally "P3TF" in the real code, why not convert it once, and define a constant in the PHP code?
Failing that, you need unpack, not pack. e.g. running
<?php
$in = 'P3TF';
$arr = unpack('N', $in);
printf("%08x\n", $arr[1]);
?>
Gives 50335446, which is the ASCII codes for 'P' '3' 'T' 'F' in hex (concatenated)