Getting ERROR 419 (unknown status) while uploading image Laravel 5.5 - laravel-5.5

I am new in laravel and i am trying to integrate pl uploader inside for image uploading.
I am getting unknown status error when i upload image.
Here is the script i am using in view:
var uploader = new plupload.Uploader({
runtimes : 'html5,flash,silverlight,html4',
browse_button : 'bg_upload', // you can pass an id...
container: document.getElementById('bg_container'), // ... or DOM Element itself
url : "{{ url('/') }}/image-upload?_token="+$('meta[name="csrf-token"]').attr('content'),
unique_names : false,
max_file_count: 1,
multi_selection: true,
flash_swf_url : "{{ URL::asset('admin/js/pluploader/Moxie.swf') }}",
silverlight_xap_url : "{{ URL::asset('admin/js/pluploader/Moxie.xap') }}",
filters : {
max_file_size : '60mb'
},
init: {
FileUploaded: function (up, file) {
var upload_path = "{{ url('/uploads') }}";
var img = jQuery('<img alt="click to change image" src="'+upload_path+file.name+'" style="width:auto; max-height:400px;">');
jQuery("#bg_upload").html(img);
},
FilesAdded: function(up, files) {
jQuery("#progress_file").css("display","block");
plupload.each(files, function(file) {
var img_type = file.name;
jQuery("#progress_file").append('<div id="' + file.id + '" >' + img_type + ' (' + plupload.formatSize(file.size) + ')<b></b></div>');
});
uploader.start();
},
UploadProgress: function(up, file) {
jQuery("#"+file.id).find("b").html('<span>' + file.percent + "%</span>");
},
UploadComplete: function () {
jQuery("#progress_file").html("");
jQuery("#progress_file").css("display","none");
},
Error: function(up, err) {
console.log("\nError #" + err.code + ": " + err.message);
//document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));
}
}
});
uploader.init();
Here is the route i defined:
Route::post('/image-upload', 'ProductsController#upload_image');
Here is the upload_image function of controller:
public function upload_image()
{
if( ! ini_get('date.timezone') )
{
date_default_timezone_set('GMT');
}
// Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
/*
// Support CORS
header("Access-Control-Allow-Origin: *");
// other CORS headers if any...
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit; // finish preflight CORS requests here
}
*/
// 5 minutes execution time
#set_time_limit(5 * 60);
// Uncomment this one to fake upload time
// usleep(5000);
// Settings
$targetDir = base_path().'/uploads';
$cleanupTargetDir = true; // Remove old files
$maxFileAge = 5 * 3600; // Temp file age in seconds
// Create target dir
if (!file_exists($targetDir)) {
#mkdir($targetDir);
}
//echo $targetDir;
// Get a file name
if (isset($_REQUEST["name"])) {
$fileName = $_REQUEST["name"];
} elseif (!empty($_FILES)) {
$fileName = $_FILES["file"]["name"];
} else {
$fileName = uniqid("file_");
}
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
// Chunking might be enabled
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
// Remove old temp files
if ($cleanupTargetDir) {
if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}';
}
while (($file = readdir($dir)) !== false) {
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
// If temp file is current file proceed to the next
if ($tmpfilePath == "{$filePath}.part") {
continue;
}
// Remove temp file if it is older than the max age and is not the current file
if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
#unlink($tmpfilePath);
}
}
closedir($dir);
}
// Open temp file
if (!$out = #fopen("{$filePath}.part", $chunks ? "ab" : "wb")) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}';
}
if (!empty($_FILES)) {
if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}';
}
// Read binary input stream and append it to temp file
if (!$in = #fopen($_FILES["file"]["tmp_name"], "rb")) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}';
}
} else {
if (!$in = #fopen("php://input", "rb")) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}';
}
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
#fclose($out);
#fclose($in);
// Check if file has been uploaded
if (!$chunks || $chunk == $chunks - 1) {
// Strip the temp .part suffix off
rename("{$filePath}.part", $filePath);
}
$data = '{"jsonrpc" : "2.0", "result" : null, "id" : "id"}';
return response()->json($data);
//return '{"jsonrpc" : "2.0", "result" : null, "id" : "id"}';
}
Can anyone tell me where i am wrong.
Thanks in advance.

The code starts working. May be it was a cache issue. I don't know what happens and the code starts uploading.
Here is my final code:
Uploader script i am using in view:
var uploader = new plupload.Uploader({
runtimes : 'html5,flash,silverlight,html4',
browse_button : 'bg_upload', // you can pass an id...
container: document.getElementById('bg_container'), // ... or DOM Element itself
url : "{{ url('/') }}/image-upload?_token="+$('meta[name="csrf-token"]').attr('content'),
unique_names : false,
max_file_count: 1,
multi_selection: false,
flash_swf_url : "{{ URL::asset('admin/js/pluploader/Moxie.swf') }}",
silverlight_xap_url : "{{ URL::asset('admin/js/pluploader/Moxie.xap') }}",
filters : {
max_file_size : '60mb'
},
init: {
FileUploaded: function (up, file) {
var upload_path = "{{ URL::asset('uploads') }}";
var img = jQuery('<img alt="click to change image" src="'+upload_path+"/"+file.name+'" style="width:100%;">');
jQuery("#bg_container").addClass("no-border");
jQuery("#bg_upload").html(img);
},
FilesAdded: function(up, files) {
jQuery("#progress_file").css("display","block");
plupload.each(files, function(file) {
var img_type = file.name;
jQuery("#progress_file").append('<div id="' + file.id + '" >' + img_type + ' (' + plupload.formatSize(file.size) + ')<b></b></div>');
});
uploader.start();
},
UploadProgress: function(up, file) {
jQuery("#"+file.id).find("b").html('<span>' + file.percent + "%</span>");
},
UploadComplete: function () {
jQuery("#progress_file").html("");
jQuery("#progress_file").css("display","none");
},
Error: function(up, err) {
console.log("\nError #" + err.code + ": " + err.message);
//document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));
}
}
});
uploader.init();
Here is the route code:
Route::post('/image-upload', 'ProductsController#upload_image');
Image upload code in controller:
public function upload_image(Request $request)
{
if( ! ini_get('date.timezone') )
{
date_default_timezone_set('GMT');
}
// Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// 5 minutes execution time
#set_time_limit(5 * 60);
// Uncomment this one to fake upload time
// usleep(5000);
// Settings
$targetDir = public_path().'/uploads';
$cleanupTargetDir = true; // Remove old files
$maxFileAge = 5 * 3600; // Temp file age in seconds
// Create target dir
if (!file_exists($targetDir)) {
#mkdir($targetDir);
}
// Get a file name
if ($request->input('name')) {
$fileName = $request->input('name');
} elseif (!empty($_FILES)) {
$fileName = $request->image->getClientOriginalName();
} else {
$fileName = uniqid("file_");
}
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
// Chunking might be enabled
$chunk = $request->input("chunk") ? intval($request->input("chunk")) : 0;
$chunks = $request->input("chunks") ? intval($request->input("chunks")) : 0;
// Remove old temp files
if ($cleanupTargetDir) {
if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}';
}
while (($file = readdir($dir)) !== false) {
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
// If temp file is current file proceed to the next
if ($tmpfilePath == "{$filePath}.part") {
continue;
}
// Remove temp file if it is older than the max age and is not the current file
if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
#unlink($tmpfilePath);
}
}
closedir($dir);
}
// Open temp file
if (!$out = #fopen("{$filePath}.part", $chunks ? "ab" : "wb")) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}';
}
if (!empty($_FILES)) {
if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}';
}
// Read binary input stream and append it to temp file
if (!$in = #fopen($_FILES["file"]["tmp_name"], "rb")) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}';
}
} else {
if (!$in = #fopen("php://input", "rb")) {
$data = '{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}';
}
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
#fclose($out);
#fclose($in);
// Check if file has been uploaded
if (!$chunks || $chunk == $chunks - 1) {
// Strip the temp .part suffix off
rename("{$filePath}.part", $filePath);
}
$data = '{"jsonrpc" : "2.0", "result" : null, "id" : "id"}';
return response()->json($data);
}

Related

Determine velocity template request body property type?

I have an API Gateway that uses velocity templates as a thin wrapper to allow users to do CRUD operations on a DynamoDB table.
I'm trying to write the update operation as dynamically as possible, but where I'm stuck is with determining type from the request body's properties from within the velocity template. This is what I'm working with:
#set($body = $input.path('$'))
#set($updateExpression = "set")
#set($expressionAttributeNames = "")
#set($expressionAttributeValues = "")
#foreach($attrName in $body.keySet())
#set($updateExpression = "${updateExpression} #$attrName = :${attrName},")
#set($expressionAttributeNames = "${expressionAttributeNames}""#${attrName}"":""${attrName}""")
#set($attrValue = $input.json("$.${attrName}"))
#if($attrValue.matches("^-?\\d+$"))
#set($attrValue = """:${attrName}"": { ""N"": ${attrValue}, ")
#else
#set($attrValue = """:${attrName}"": { ""S"": """ + $util.escapeJavaScript($attrValue) + """ },")
#end
#set($expressionAttributeValues = "${expressionAttributeValues} ${attrValue}")
#if($foreach.hasNext)
#set($expressionAttributeNames = "${expressionAttributeNames}, ")
#end
#end
{
"TableName": "TABLE",
"Key": { "id": { "S": "$input.params('id')" } },
"UpdateExpression": "${updateExpression} updatedOn = :updatedOn",
"ExpressionAttributeNames": {$expressionAttributeNames},
"ExpressionAttributeValues": {
$expressionAttributeValues
":updatedOn": { "N": "$context.requestTimeEpoch" }
}
}
Edit: This would be a sample request body:
https://api/v1/endpoint/123
{
"location": {
"lat": 42,
"lon": -71
},
"rating": 4
}
This is the current transformation I get:
{
"TableName": "users",
"Key": { "gcn": { "S": "123" } },
"UpdateExpression": "set #number = :number, #location = :location, updatedOn = :updatedOn",
"ExpressionAttributeNames": {"#number":"number", "#location":"location"},
"ExpressionAttributeValues": {
":number": { "S": "1" }, ":location": { "S": "{\"lat\":26.89199858375187,\"lon\":75.77141155196833}" },
":updatedOn": { "N": "" }
}
}
I currently just have a test for checking if a value is a number...and it isn't working.
After doing some more digging I reached what I set out for. I have a dynamic Velocity Template mapping for AWS API Gateway for the purpose of updating DynamoDB items.
So far it supports strings, numbers, booleans, and string-escaped objects, as that's how my project stores them (they are not query-able). ExpressionAttributeNames exists in case you use a reserved keyword for an attribute name...like I did for 'location'.
If anyone has any improvements/enhancements please let me know, it's a beast of a script.
#set($body = $input.path('$'))
#set($updateExpression = "set")
#set($expressionAttributeNames = "")
#set($expressionAttributeValues = "")
#foreach($attrName in $body.keySet())
#set($updateExpression = "${updateExpression} #$attrName = :${attrName},")
#set($expressionAttributeNames = "${expressionAttributeNames}""#${attrName}"":""${attrName}""")
#set($attrValue = $input.json("$.${attrName}"))
#if($attrValue.toString().matches("[+-]?\d+"))
#set($attrValue = """:${attrName}"": { ""N"": ""${attrValue}"" }, ")
#elseif($attrValue.toString() == "true" || $attrValue.toString() == "false")
#set($attrValue = """:${attrName}"": { ""BOOL"": ${attrValue} }, ")
#elseif(($attrValue.toString().startsWith("{") && $attrValue.toString().endsWith("}")) ||
($attrValue.toString().startsWith("[") && $attrValue.toString().endsWith("]")) )
#set($attrValue = """:${attrName}"": { ""S"": """ + $util.escapeJavaScript($attrValue) + """ },")
#else
#set($attrValue = """:${attrName}"": { ""S"": " + $attrValue + " },")
#end
#set($expressionAttributeValues = "${expressionAttributeValues} ${attrValue}")
#if($foreach.hasNext)
#set($expressionAttributeNames = "${expressionAttributeNames}, ")
#end
#end
{
"TableName": "", ## Insert your table here.
"Key": { "gcn": { "S": "$input.params('')" } }, ## Insert your key expression here.
## Update below if `updatedOn` is not your audit attribute.
"UpdateExpression": "${updateExpression} updatedOn = :updatedOn",
"ExpressionAttributeNames": {$expressionAttributeNames},
"ExpressionAttributeValues": {
$expressionAttributeValues
":updatedOn": { "N": "$context.requestTimeEpoch.toString()" }
}
}
Sample Request Body:
{
"firstName": "John",
"isActive": true,
"_status": 1
}
Sample Transformation:
{
"TableName": "users",
"Key": {
"id": {
"S": "1"
}
},
"UpdateExpression": "set #firstName = :firstName, #isActive = :isActive, #_status = :_status, updatedOn = :updatedOn",
"ExpressionAttributeNames": {
"#firstName": "firstName",
"#isActive": "isActive",
"#_status": "_status"
},
"ExpressionAttributeValues": {
":firstName": {
"S": "John"
},
":isActive": {
"BOOL": true
},
":_status": {
"N": "1"
},
":updatedOn": {
"N": "123456789"
}
}
}

laravel-filemanager, Sort by time default

I need load files order by "time DESC" when the iframe of laravel-filemanager is called.
Is posible? I read the code and see that we cant order by time DESC and the code dont have options to configure a default "sort_type"
https://github.com/UniSharp/laravel-filemanager
this is not good idea but it's work for me
i am change the code in vendor/unisharp/laravel-filemanager/public/js/script.js
var sort_type = 'alphabetic';
to
var sort_type = 'time';
if you want to sort date in desc order. change the code in
vendor/unisharp/laravel-filemanager/src/Controllers/ItemsController.php
public function getItems()
{
$currentPage = self::getCurrentPageFromRequest();
$perPage = $this->helper->getPaginationPerPage();
$items = array_merge($this->lfm->folders(), $this->lfm->files());
return [
'items' => array_map(function ($item) {
return $item->fill()->attributes;
}, array_slice($items, ($currentPage - 1) * $perPage, $perPage)),
'paginator' => [
'current_page' => $currentPage,
'total' => count($items),
'per_page' => $perPage,
],
'display' => $this->helper->getDisplayMode(),
'working_dir' => $this->lfm->path('working_dir'),
];
}
with
use Illuminate\Http\Request;
public function getItems(Request $request)
{
$currentPage = self::getCurrentPageFromRequest();
$perPage = $this->helper->getPaginationPerPage();
$files = $this->lfm->files();
if($request->sort_type=='time'){
$files = array_reverse($files);
}
$items = array_merge($this->lfm->folders(), $files);
return [
'items' => array_map(function ($item) {
return $item->fill()->attributes;
}, array_slice($items, ($currentPage - 1) * $perPage, $perPage)),
'paginator' => [
'current_page' => $currentPage,
'total' => count($items),
'per_page' => $perPage,
],
'display' => $this->helper->getDisplayMode(),
'working_dir' => $this->lfm->path('working_dir'),
];
}
i'm change the code in vendor/unisharp/laravel-filemanager/src/traits/LfmHelpers.php
and it's worked
public function sortFilesAndDirectories($arr_items, $sort_type)
{
if ($sort_type == 'time') {
$key_to_sort = 'updated';
} elseif ($sort_type == 'alphabetic') {
$key_to_sort = 'name';
} else {
$key_to_sort = 'updated';
}
return strcmp($a->{$key_to_sort}, $b->{$key_to_sort});
});
return $arr_items;
}
with
public function sortFilesAndDirectories($arr_items, $sort_type)
{
if ($sort_type == 'time') {
$key_to_sort = 'updated';
} elseif ($sort_type == 'alphabetic') {
$key_to_sort = 'name';
} else {
$key_to_sort = 'updated';
}
uasort($arr_items, function ($a, $b) use ($key_to_sort) {
if ( $a->$key_to_sort == $a->$key_to_sort )
return 0;
else if ( $a->$key_to_sort > $a->$key_to_sort)
return -1;
else
return 1;
});
return $arr_items;
}
LFM 1.8:
Also, you can use this method, if you don't want to change the LFM Src code.
First use this command to generate views :
php artisan vendor:publish --tag=lfm_view
Find this file:
ROOT/resources/views/vendor/laravel-filemanager/grid-view.blade.php
and change the cod according the follow:
#if((sizeof($files) > 0) || (sizeof($directories) > 0))
<div class="row">
<!-- -----------------------------------Begin of added block -->
<?php
$file_temp = [];
if($files != null){
foreach ($files as $key => $value) {
$file_temp[$value['updated']] = $value;
}
krsort($file_temp);
$file_temp1 = [];
$i = 0;
foreach ($file_temp as $key => $value) {
$file_temp1[$i] = $value;
$i+=1;
}
$files = $file_temp1;
}
?>
<!-- ---------------------------------------End of added block -->
#foreach($items as $item)
....
...
As you can see, the <?php ?> code block was added.You can use krsort() or ksort() as you want for descending or ascending.
In 2.3 I did next steps
php artisan vendor:publish --tag=lfm_view
Then you can find file
ROOT/resources/views/vendor/laravel-filemanager/grid-view.blade.php
And after incuding
<script>{!! \File::get(base_path('vendor/unisharp/laravel-filemanager/public/js/script.js')) !!}</script>
I added one line of js
sort_type = 'time';
But files sorts from the oldest to the newest. Thast's why I redefined routes and ItemsController

How to find and replace unique field name on mongodb

I have 1.6 million documents in mongodb like this:
{
"_id" : ObjectId("57580c3f7e1a1469e772345b"),
"https://www.....com/vr/s1812227" : {
"suitability" : "children welcome",
"details" : {
"lookingCount" : 0,
"photoUrl" : "https://www.....com/vr/s1812227/....",
"partner" : null,
.........
}
.........
}
}
{
"_id" : ObjectId("57580c3f7e1a1469e772346d"),
"https://www.....com/vr/s1812358" : {
"suitability" : "children welcome",
"details" : {
"lookingCount" : 0,
"photoUrl" : "https://www.....com/vr/s1812358/....",
"partner" : null,
.........
}
.........
}
}
{
"_id" : ObjectId("57580c3f7e1a1469e772346d"),
"https://www.....com/vr/s1812358/unite/125" : {
"suitability" : "children welcome",
"details" : {
"lookingCount" : 0,
"photoUrl" : "https://www.....com/vr/s1812358/....",
"partner" : null,
.........
}
.........
}
}
I want like this:
{
"_id" : ObjectId("57580c3f7e1a1469e772345b"),
"products" : {
"suitability" : "children welcome",
"details" : {
"lookingCount" : 0,
"photoUrl" : "https://www.....com/vr/s1812227/....",
"partner" : null,
.........
}
.........
}
}
Edit content.... Thanks for your answer and interest in advance.
UPDATE
I'm trying this code but maximum 1200 documents insert to new collection. I have 1.5 million documents.
db.sourceColl.find().forEach(function(doc) {
for (var k in doc) {
if (k.match(/^https.*/) ) {
db.sourceColl.find({ "_id": doc._id });
db.getSiblingDB('targetdb')['targetColl'].insert({products: doc[k]});
}
}
});
After I'm try this and insert 20 documents to new collection. I'm so confused. how to rename and copy new collection all documents. UPDATE2: I use robomongo and I think there are limits in robomongo. This code works without problem in mongo shell. search, replace and copy new document.
var bulk = db.sourceColl.initializeOrderedBulkOp();
var counter = 0;
db.sourceColl.find().forEach(function(doc) {
for (var k in doc) {
if (k.match(/^https.*/) ) {
print(k)
bulk.find({ "_id": doc._id });
db.getSiblingDB('targetDB')['targetColl'].insert({products: doc[k]});
counter++;
}
}
if ( counter % 1000 == 0 ) {
bulk.execute();
bulk = db.sourceColl.initializeOrderedBulkOp();
}
});
if ( counter % 1000 != 0 )
bulk.execute();
I think there are limits in robomongo. This code works fine in mongo shell. search, replace and copy new collection.
var bulk = db.sourceColl.initializeOrderedBulkOp();
var counter = 0;
db.sourceColl.find().forEach(function(doc) {
for (var k in doc) {
if (k.match(/^https.*/) ) {
print(k)
bulk.find({ "_id": doc._id });
db.getSiblingDB('targetDB')['targetColl'].insert({products: doc[k]});
counter++;
}
}
if ( counter % 1000 == 0 ) {
bulk.execute();
bulk = db.sourceColl.initializeOrderedBulkOp();
}
});
if ( counter % 1000 != 0 )
bulk.execute();
I have modified this answer https://stackoverflow.com/a/25204168/6446251

RapidJSON library getting value without name field

We got json reply similar to following from certain partner's api.
[
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName": "Jones" }
]
when we tried to parse through rapidjson lib, parse didn't return any error, but we are not sure how to get value from document without the name field on the array. Anyone has any idea to deal with it?
std::string s("[ { \"firstName\":\"John\" , \"lastName\":\"Doe\" }, "
" { \"firstName\":\"Anna\" , \"lastName\":\"Smith\" }, "
" { \"firstName\":\"Peter\" , \"lastName\": \"Jones\" } ]");
rapidjson::Document doc;
doc.Parse<0>(s.c_str());
for (rapidjson::SizeType i = 0; i < doc.Size(); i++)
printf("\n%u: %s %s", i + 1, doc[i]["firstName"].GetString(), doc[i]["lastName"].GetString());

Insert commas into number string

Hey there, I'm trying to perform a backwards regular expression search on a string to divide it into groups of 3 digits. As far as i can see from the AS3 documentation, searching backwards is not possible in the reg ex engine.
The point of this exercise is to insert triplet commas into a number like so:
10000000 => 10,000,000
I'm thinking of doing it like so:
string.replace(/(\d{3})/g, ",$1")
But this is not correct due to the search not happening from the back and the replace $1 will only work for the first match.
I'm getting the feeling I would be better off performing this task using a loop.
UPDATE:
Due to AS3 not supporting lookahead this is how I have solved it.
public static function formatNumber(number:Number):String
{
var numString:String = number.toString()
var result:String = ''
while (numString.length > 3)
{
var chunk:String = numString.substr(-3)
numString = numString.substr(0, numString.length - 3)
result = ',' + chunk + result
}
if (numString.length > 0)
{
result = numString + result
}
return result
}
If your language supports postive lookahead assertions, then I think the following regex will work:
(\d)(?=(\d{3})+$)
Demonstrated in Java:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CommifyTest {
#Test
public void testCommify() {
String num0 = "1";
String num1 = "123456";
String num2 = "1234567";
String num3 = "12345678";
String num4 = "123456789";
String regex = "(\\d)(?=(\\d{3})+$)";
assertEquals("1", num0.replaceAll(regex, "$1,"));
assertEquals("123,456", num1.replaceAll(regex, "$1,"));
assertEquals("1,234,567", num2.replaceAll(regex, "$1,"));
assertEquals("12,345,678", num3.replaceAll(regex, "$1,"));
assertEquals("123,456,789", num4.replaceAll(regex, "$1,"));
}
}
Found on http://gskinner.com/RegExr/
Community > Thousands separator
Pattern: /\d{1,3}(?=(\d{3})+(?!\d))/g
Replace: $&,
trace ( String("1000000000").replace( /\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,") );
It done the job!
If your regex engine has positive lookaheads, you could do something like this:
string.replace(/(\d)(?=(\d\d\d)+$)/, "$1,")
Where the positive lookahead (?=...) means that the regex only matches when the lookahead expression ... matches.
(Note that lookaround-expressions are not always very efficient.)
While many of these answers work fine with positive integers, many of their argument inputs are cast as Numbers, which implies that they can handle negative values or contain decimals, and here all of the solutions fail. Though the currently selected answer does not assume a Number I was curious to find a solution that could and was also more performant than RegExp (which AS3 does not do well).
I put together many of the answers here in a testing class (and included a solution from this blog and an answer of my own called commaify) and formatted them in a consistent way for easy comparison:
package
{
public class CommaNumberSolutions
{
public static function commaify( input:Number ):String
{
var split:Array = input.toString().split( '.' ),
front:String = split[0],
back:String = ( split.length > 1 ) ? "." + split[1] : null,
n:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - n) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
n = front.length - (3 * i + i - 1);
front = front.slice( 0, n ) + "," + front.slice( n );
}
if ( back )
return front + back;
else
return front;
}
public static function getCommaString( input:Number ):String
{
var s:String = input.toString();
if ( s.length <= 3 )
return s;
var i:int = s.length % 3;
if ( i == 0 )
i = 3;
for ( ; i < s.length; i += 4 )
{
var part1:String = s.substr(0, i);
var part2:String = s.substr(i, s.length);
s = part1.concat(",", part2);
}
return s;
}
public static function formatNumber( input:Number ):String
{
var s:String = input.toString()
var result:String = ''
while ( s.length > 3 )
{
var chunk:String = s.substr(-3)
s = s.substr(0, s.length - 3)
result = ',' + chunk + result
}
if ( s.length > 0 )
result = s + result
return result
}
public static function commaCoder( input:Number ):String
{
var s:String = "";
var len:Number = input.toString().length;
for ( var i:int = 0; i < len; i++ )
{
if ( (len-i) % 3 == 0 && i != 0)
s += ",";
s += input.toString().charAt(i);
}
return s;
}
public static function regex1( input:Number ):String
{
return input.toString().replace( /-{0,1}(\d)(?=(\d\d\d)+$)/g, "$1," );
}
public static function regex2( input:Number ):String
{
return input.toString().replace( /-{0,1}\d{1,3}(?=(\d{3})+(?!\d))/g , "$&,")
}
public static function addCommas( input:Number ):String
{
var negative:String = "";
if ( input < 0 )
{
negative = "-";
input = Math.abs(input);
}
var s:String = input.toString();
var results:Array = s.split(/\./);
s = results[0];
if ( s.length > 3 )
{
var mod:Number = s.length % 3;
var output:String = s.substr(0, mod);
for ( var i:Number = mod; i < s.length; i += 3 )
{
output += ((mod == 0 && i == 0) ? "" : ",") + s.substr(i, 3);
}
if ( results.length > 1 )
{
if ( results[1].length == 1 )
return negative + output + "." + results[1] + "0";
else
return negative + output + "." + results[1];
}
else
return negative + output;
}
if ( results.length > 1 )
{
if ( results[1].length == 1 )
return negative + s + "." + results[1] + "0";
else
return negative + s + "." + results[1];
}
else
return negative + s;
}
}
}
Then I tested each for accuracy and performance:
package
{
public class TestCommaNumberSolutions
{
private var functions:Array;
function TestCommaNumberSolutions()
{
functions = [
{ name: "commaify()", f: CommaNumberSolutions.commaify },
{ name: "addCommas()", f: CommaNumberSolutions.addCommas },
{ name: "getCommaString()", f: CommaNumberSolutions.getCommaString },
{ name: "formatNumber()", f: CommaNumberSolutions.formatNumber },
{ name: "regex1()", f: CommaNumberSolutions.regex1 },
{ name: "regex2()", f: CommaNumberSolutions.regex2 },
{ name: "commaCoder()", f: CommaNumberSolutions.commaCoder }
];
verify();
measure();
}
protected function verify():void
{
var assertions:Array = [
{ input: 1, output: "1" },
{ input: 21, output: "21" },
{ input: 321, output: "321" },
{ input: 4321, output: "4,321" },
{ input: 54321, output: "54,321" },
{ input: 654321, output: "654,321" },
{ input: 7654321, output: "7,654,321" },
{ input: 987654321, output: "987,654,321" },
{ input: 1987654321, output: "1,987,654,321" },
{ input: 21987654321, output: "21,987,654,321" },
{ input: 321987654321, output: "321,987,654,321" },
{ input: 4321987654321, output: "4,321,987,654,321" },
{ input: 54321987654321, output: "54,321,987,654,321" },
{ input: 654321987654321, output: "654,321,987,654,321" },
{ input: 7654321987654321, output: "7,654,321,987,654,321" },
{ input: 87654321987654321, output: "87,654,321,987,654,321" },
{ input: -1, output: "-1" },
{ input: -21, output: "-21" },
{ input: -321, output: "-321" },
{ input: -4321, output: "-4,321" },
{ input: -54321, output: "-54,321" },
{ input: -654321, output: "-654,321" },
{ input: -7654321, output: "-7,654,321" },
{ input: -987654321, output: "-987,654,321" },
{ input: -1987654321, output: "-1,987,654,321" },
{ input: -21987654321, output: "-21,987,654,321" },
{ input: -321987654321, output: "-321,987,654,321" },
{ input: -4321987654321, output: "-4,321,987,654,321" },
{ input: -54321987654321, output: "-54,321,987,654,321" },
{ input: -654321987654321, output: "-654,321,987,654,321" },
{ input: -7654321987654321, output: "-7,654,321,987,654,321" },
{ input: -87654321987654321, output: "-87,654,321,987,654,321" },
{ input: .012345, output: "0.012345" },
{ input: 1.012345, output: "1.012345" },
{ input: 21.012345, output: "21.012345" },
{ input: 321.012345, output: "321.012345" },
{ input: 4321.012345, output: "4,321.012345" },
{ input: 54321.012345, output: "54,321.012345" },
{ input: 654321.012345, output: "654,321.012345" },
{ input: 7654321.012345, output: "7,654,321.012345" },
{ input: 987654321.012345, output: "987,654,321.012345" },
{ input: 1987654321.012345, output: "1,987,654,321.012345" },
{ input: 21987654321.012345, output: "21,987,654,321.012345" },
{ input: -.012345, output: "-0.012345" },
{ input: -1.012345, output: "-1.012345" },
{ input: -21.012345, output: "-21.012345" },
{ input: -321.012345, output: "-321.012345" },
{ input: -4321.012345, output: "-4,321.012345" },
{ input: -54321.012345, output: "-54,321.012345" },
{ input: -654321.012345, output: "-654,321.012345" },
{ input: -7654321.012345, output: "-7,654,321.012345" },
{ input: -987654321.012345, output: "-987,654,321.012345" },
{ input: -1987654321.012345, output: "-1,987,654,321.012345" },
{ input: -21987654321.012345, output: "-21,987,654,321.012345" }
];
var i:int;
var len:int = assertions.length;
var assertion:Object;
var f:Function;
var s1:String;
var s2:String;
for each ( var o:Object in functions )
{
i = 0;
f = o.f;
trace( '\rVerify: ' + o.name );
for ( ; i < len; i++ )
{
assertion = assertions[ i ];
s1 = f.apply( null, [ assertion.input ] );
s2 = assertion.output;
if ( s1 !== s2 )
trace( 'Test #' + i + ' Failed: ' + s1 + ' !== ' + s2 );
}
}
}
protected function measure():void
{
// Generate random inputs
var values:Array = [];
for ( var i:int = 0; i < 999999; i++ ) {
values.push( Math.random() * int.MAX_VALUE * ( Math.random() > .5 ? -1 : 1) );
}
var len:int = values.length;
var stopwatch:Stopwatch = new Stopwatch;
var s:String;
var f:Function;
trace( '\rTesting ' + len + ' random values' );
// Test each function
for each ( var o:Object in functions )
{
i = 0;
s = "";
f = o.f;
stopwatch.start();
for ( ; i < len; i++ ) {
s += f.apply( null, [ values[i] ] ) + " ";
}
stopwatch.stop();
trace( o.name + '\t\ttook ' + (stopwatch.elapsed/1000) + 's' ); //(stopwatch.elapsed/len) + 'ms'
}
}
}
}
import flash.utils.getTimer;
class Stopwatch
{
protected var startStamp:int;
protected var stopStamp:int;
protected var _started:Boolean;
protected var _stopped:Boolean;
function Stopwatch( startNow:Boolean = true ):void
{
if ( startNow )
start();
}
public function start():void
{
startStamp = getTimer();
_started = true;
_stopped = false;
}
public function stop():void
{
stopStamp = getTimer();
_stopped = true;
_started = false;
}
public function get elapsed():int
{
return ( _stopped ) ? stopStamp - startStamp : ( _started ) ? getTimer() - startStamp : 0;
}
public function get started():Boolean
{
return _started;
}
public function get stopped():Boolean
{
return _stopped;
}
}
Because of AS3's lack of precision with larger Numbers every class failed these tests:
Test #15 Failed: 87,654,321,987,654,320 !== 87,654,321,987,654,321
Test #31 Failed: -87,654,321,987,654,320 !== -87,654,321,987,654,321
Test #42 Failed: 21,987,654,321.012344 !== 21,987,654,321.012345
Test #53 Failed: -21,987,654,321.012344 !== -21,987,654,321.012345
But only two functions passed all of the other tests: commaify() and addCommas().
The performance tests show that commaify() is the most preformant of all the solutions:
Testing 999999 random values
commaify() took 12.411s
addCommas() took 17.863s
getCommaString() took 18.519s
formatNumber() took 14.409s
regex1() took 40.654s
regex2() took 36.985s
Additionally commaify() can be extended to including arguments for decimal length and zero-padding on the decimal portion — it also outperforms the others at 13.128s:
public static function cappedDecimal( input:Number, decimalPlaces:int = 2 ):Number
{
if ( decimalPlaces == 0 )
return Math.floor( input );
var decimalFactor:Number = Math.pow( 10, decimalPlaces );
return Math.floor( input * decimalFactor ) / decimalFactor;
}
public static function cappedDecimalString( input:Number, decimalPlaces:int = 2, padZeros:Boolean = true ):String
{
if ( padZeros )
return cappedDecimal( input, decimalPlaces ).toFixed( decimalPlaces );
else
return cappedDecimal( input, decimalPlaces ).toString();
}
public static function commaifyExtended( input:Number, decimalPlaces:int = 2, padZeros:Boolean = true ):String
{
var split:Array = cappedDecimalString( input, decimalPlaces, padZeros ).split( '.' ),
front:String = split[0],
back:String = ( split.length > 1 ) ? "." + split[1] : null,
n:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - n) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
n = front.length - (3 * i + i - 1);
front = front.slice( 0, n ) + "," + front.slice( n );
}
if ( back )
return front + back;
else
return front;
}
So, I'd offer that commaify() meets the demands of versatility and performance though certainly not the most compact or elegant.
This really isn't the best use of RegEx... I'm not aware of a number formatting function, but this thread seems to provide a solution.
function commaCoder(yourNum):String {
//var yourNum:Number = new Number();
var numtoString:String = new String();
var numLength:Number = yourNum.toString().length;
numtoString = "";
for (i=0; i<numLength; i++) {
if ((numLength-i)%3 == 0 && i != 0) {
numtoString += ",";
}
numtoString += yourNum.toString().charAt(i);
trace(numtoString);
}
return numtoString;
}
If you really are insistent on using RegEx, you could just reverse the string, apply the RegEx replace function, then reverse it back.
A sexeger is good for this. In brief, a sexeger is a reversed regex run against a reversed string that you reverse the output of. It is generally more efficient than the alternative. Here is some pseudocode for what you want to do:
string = reverse string
string.replace(/(\d{3})(?!$)/g, "$1,")
string = reverse string
Here is is a Perl implemntation
#!/usr/bin/perl
use strict;
use warnings;
my $s = 13_456_789;
for my $n (1, 12, 123, 1234, 12345, 123456, 1234567) {
my $s = reverse $n;
$s =~ s/([0-9]{3})(?!$)/$1,/g;
$s = reverse $s;
print "$s\n";
}
You may want to consider NumberFormatter
I'll take the downvotes for not being the requested language, but this non-regex technique should apply (and I arrived here via searching for "C# regex to add commas into number")
var raw = "104241824 15202656 KB 13498560 KB 1612672KB already 1,000,000 or 99.999 or 9999.99";
int i = 0;
bool isnum = false;
var formatted = raw.Reverse().Aggregate(new StringBuilder(), (sb, c) => {
//$"{i}: [{c}] {isnum}".Dump();
if (char.IsDigit(c) && c != ' ' && c!= '.' && c != ',') {
if (isnum) {
if (i == 3) {
//$"ins ,".Dump();
sb.Insert(0, ',');
i = 0;
}
}
else isnum = true;
i++;
}
else {
isnum = false;
i = 0;
}
sb.Insert(0, c);
return sb;
});
results in:
104,241,824 15,202,656 KB 13,498,560 KB 1,612,672KB already 1,000,000 or 99.999 or 9,999.99
// This is a simple code and it works fine...:)
import java.util.Scanner;
public class NumberWithCommas {
public static void main(String a[]) {
Scanner sc = new Scanner(System.in);
String num;
System.out.println("\n enter the number:");
num = sc.next();
printNumber(num);
}
public static void printNumber(String ar) {
int len, i = 0, temp = 0;
len = ar.length();
temp = len / 3;
if (len % 3 == 0)
temp = temp - 1;
len = len + temp;
char[] ch = ar.toCharArray();
char[] ch1 = new char[len];
for (int j = 0, k = (ar.length() - 1); j < len; j++)
{
if (i < 3)
{
ch1[j] = ch[k];
i++;
k--;
}
else
{
ch1[j] = ',';
i = 0;
}
}
for (int j = len - 1; j >= 0; j--)
System.out.print(ch1[j]);
System.out.println("");
}
}
If you can't use lookahead on regular expressions, you can use this:
string.replace(/^(.*?,)?(\d{1,3})((?:\d{3})+)$/, "$1$2,$3")
inside a loop until there's nothing to replace.
For example, a perlish solution would look like this:
my $num = '1234567890';
while ($num =~ s/^(.*?,)?(\d{1,3})((?:\d{3})+)$/$1$2,$3/) {}
Perl RegExp 1 liner:
1 while $VAR{total} =~ s/(.*\d)(\d\d\d)/$1,$2/g;
Try this code. it's simple and best performance.
var reg:RegExp=/\d{1,3}(?=(\d{3})+(?!\d))/g;
var my_num:Number = 48712694;
var my_num_str:String = String(my_num).replace(reg,"$&,");
trace(my_num_str);
::output::
48,712,694