How to convert large UTF-8 encoded char* string to CStringW (UTF-16)? - html

I have a problem with converting a UTF-8 encoded string to a UTF-16 encoded CStringW.
Here is my source code:
CStringW ConvertUTF8ToUTF16( __in const CHAR * pszTextUTF8 )
{
_wsetlocale( LC_ALL, L"Korean" );
if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == '\0') )
{
return L"";
}
const size_t cchUTF8Max = INT_MAX - 1;
size_t cchUTF8;
HRESULT hr = ::StringCbLengthA( pszTextUTF8, cchUTF8Max, &cchUTF8 );
if ( FAILED( hr ) )
{
AtlThrow( hr );
}
++cchUTF8;
int cbUTF8 = static_cast<int>( cchUTF8 );
int cchUTF16 = ::MultiByteToWideChar(
CP_UTF8,
MB_ERR_INVALID_CHARS,
pszTextUTF8,
-1,
NULL,
0
);
CString strUTF16;
strUTF16.GetBufferSetLength(cbUTF8);
WCHAR * pszUTF16 = new WCHAR[cchUTF16];
int result = ::MultiByteToWideChar(
CP_UTF8,
0,
pszTextUTF8,
cbUTF8,
pszUTF16,
cchUTF16
);
ATLASSERT( result != 0 );
if ( result == 0 )
{
AtlThrowLastWin32();
}
strUTF16.Format(_T("%s"), pszUTF16);
return strUTF16;
}
pszTextUTF8 is htm file's content in UTF-8.
When htm file's volume is less than 500kb, this code works well.
but, when converting over 500kb htm file, (ex 648KB htm file that I have.)
pszUTF16 has all content of file, but strUTF16 is not. (about half)
I guess File open is not wrong.
In strUTF16 m_pszData has all content how to I get that?
strUTF16.Getbuffer(); dosen't work.

The code in the question is stock full of bugs, somewhere in the order of 1 bug per 1-2 lines of code.
Here is a short summary:
_wsetlocale( LC_ALL, L"Korean" );
Changing a global setting in a conversion function is unexpected, and will break code calling that. It's not even necessary either; you aren't using the locale for the encoding conversion.
HRESULT hr = ::StringCbLengthA( pszTextUTF8, cchUTF8Max, &cchUTF8 );
This is passing the wrong cchUTF8Max value (according to the documentation), and counts the number of bytes (vs. the number of characters, i.e. code units). Besides all that, you do not even need to know the number of code units, as you never use it (well, you are, but that is just another bug).
int cbUTF8 = static_cast<int>( cchUTF8 );
While that fixes the prefix (count of bytes as opposed to count of characters), it won't save you from using it later on for something that has an unrelated value.
strUTF16.GetBufferSetLength(cbUTF8);
This resizes the string object that should eventually hold the UTF-16 encoded characters. But it doesn't use the correct number of characters (the previous call to MultiByteToWideChar would have provided that value), but rather chooses a completely unrelated value: The number of bytes in the UTF-8 encoded source string.
But it doesn't just stop there, that line of code also throws away the pointer to the internal buffer, that was ready to be written to. Failure to call ReleaseBuffer is only a natural consequence, since you decided against reading the documentation.
WCHAR * pszUTF16 = new WCHAR[cchUTF16];
While not a bug in itself, it needlessly allocates another buffer (this time passing the correct size). You already allocated a buffer of the required size (albeit wrong) in the previous call to GetBufferSetLength. Just use that, that's what the member function is for.
strUTF16.Format(_T("%s"), pszUTF16);
That is probably the anti-pattern associated with the printf family of functions. It is the convoluted way to write CopyChars (or Append).
Now that that's cleared up, here is the correct way to write that function (or at least one way to do it):
CStringW ConvertUTF8ToUTF16( __in const CHAR * pszTextUTF8 ) {
// Allocate return value immediately, so that (N)RVO can be applied
CStringW strUTF16;
if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == '\0') ) {
return strUTF16;
}
// Calculate the required destination buffer size
int cchUTF16 = ::MultiByteToWideChar( CP_UTF8,
MB_ERR_INVALID_CHARS,
pszTextUTF8,
-1,
nullptr,
0 );
// Perform error checking
if ( cchUTF16 == 0 ) {
throw std::runtime_error( "MultiByteToWideChar failed." );
}
// Resize the output string size and use the pointer to the internal buffer
wchar_t* const pszUTF16 = strUTF16.GetBufferSetLength( cchUTF16 );
// Perform conversion (return value ignored, since we just checked for success)
::MultiByteToWideChar( CP_UTF8,
MB_ERR_INVALID_CHARS, // Use identical flags
pszTextUTF8,
-1,
pszUTF16,
cchUTF16 );
// Perform required cleanup
strUTF16.ReleaseBuffer();
// Return converted string
return strUTF16;
}

Related

recv() loop adding last "chunk" twice to buffer

Im new to C and very new to network programming. I wrote some code that would read an HTML file chunk by chunk. Everything works great until the last chunk which gets read in twice and Im not inertly sure why.
char request[] = "GET /~kkredo/file.html HTTP/1.0\r\n\r\n";
char response[10000];
int bytes, p_count = 0;
int res_code = 1;
send(sock, request, sizeof(request), 0);
while(res_code != 0) {
res_code = recv(sock, &response, chunk, 0);
printf("%s",response);
bytes += res_code;
}
That's the code for the loop, it prints out each chunk until the last chunk which prints twice. Ive put an If statement to double check what the loop is checking, and even though res_code = 0, it still prints. I think it has something to do with the char buffer but Im not sure.
<p> that's it for this page </p>
</body>
</html>
</body>
</html>
That would be an example of the duplication. The second html and body tags are duplicated and dependent on if the chunk size is larger, then it would duplicate more of the document. Thanks for the help.
you call
printf("%s",response);
without regarding res_code, thus if recv() doesn't receive anymore data and returns 0, you print whatever still is in response. Furthermore: response isn't necessarily NUL terminated, so your printf() invokes UB. And, as a third point, your code doesn't handle that recv() might return -1.
So you should change yout loop to:
while(res_code > 0) {
res_code = recv(sock, response, chunk, 0); // '&' isn't necessary here
if(res_code > 0) {
printf("%.*s",res_code,response);
bytes += res_code;
}
}

Reading YUY2 data from an IMFSample that appears to have improper data on Windows 10

I am developing an application that using IMFSourceReader to read data from video files. I am using DXVA for improved performance. I am having trouble with one specific full-HD H.264 encoded AVI file. Based on my investigation this far, I believe that the IMFSample contains incorrect data. My workflow is below:
Create a source reader with a D3D manager to enable hardware acceleration.
Set the current media type to YUY2 as DXVA does not
decode to any RGB colorspace.
Call ReadSample to get an IMFSample. Works fine.
Use the VideoProcessorBlt to perform YUY2 to BGRA32
conversion. For this specific file it errors out with an
E_INVALIDARGS error code. Decided to do the conversion myself.
Used IMFSample::ConvertToContiguousBuffer to receive an IMFMediaBuffer. When locking this buffer, the pitch is reported as 1280 bytes. This I believe is incorrect, because for a full HD video, the pitch should be (1920 + 960 + 960 = 3840 bytes).
I dumped the raw memory and extracted the Y, U and V components based on my understanding of the YUY2 layout. You can find it below. So, the data is there but I do not believe it is laid out as YUY2. Need some help in interpreting the data.
My code for reading is below:
// Direct3D surface that stores the result of the YUV2RGB conversion
CComPtr<IDirect3DSurface9> _pTargetSurface;
IDirectXVideoAccelerationService* vidAccelService;
initVidAccelerator(&vidAccelService); // Omitting the code for this.
// Create a new surface for doing the color conversion, set it up to store X8R8G8B8 data.
hr = vidAccelService->CreateSurface( static_cast<UINT>( 1920 ),
static_cast<UINT>( 1080 ),
0, // no back buffers
D3DFMT_X8R8G8B8, // data format
D3DPOOL_DEFAULT, // default memory pool
0, // reserved
DXVA2_VideoProcessorRenderTarget, // to use with the Blit operation
&_pTargetSurface, // surface used to store frame
NULL);
GUID processorGUID;
DXVA2_VideoDesc videoDescriptor;
D3DFORMAT processorFmt;
UINT numSubStreams;
IDirectXVideoProcessor* _vpd;
initVideoProcessor(&vpd); // Omitting the code for this
// We get the videoProcessor parameters on creation, and fill up the videoProcessBltParams accordingly.
_vpd->GetCreationParameters(&processorGUID, &videoDescriptor, &processorFmt, &numSubStreams);
RECT targetRECT; // { 0, 0, width, height } as left, top, right, bottom
targetRECT.left = 0;
targetRECT.right = videoDescriptor.SampleWidth;
targetRECT.top = 0;
targetRECT.bottom = videoDescriptor.SampleHeight;
SIZE targetSIZE; // { width, height }
targetSIZE.cx = videoDescriptor.SampleWidth;
targetSIZE.cy = videoDescriptor.SampleHeight;
// Parameters that are required to use the video processor to perform
// YUV2RGB and other video processing operations
DXVA2_VideoProcessBltParams _frameBltParams;
_frameBltParams.TargetRect = targetRECT;
_frameBltParams.ConstrictionSize = targetSIZE;
_frameBltParams.StreamingFlags = 0; // reserved.
_frameBltParams.BackgroundColor.Y = 0x0000;
_frameBltParams.BackgroundColor.Cb = 0x0000;
_frameBltParams.BackgroundColor.Cr = 0x0000;
_frameBltParams.BackgroundColor.Alpha = 0xFFFF;
// copy attributes from videoDescriptor obtained above.
_frameBltParams.DestFormat.VideoChromaSubsampling = videoDescriptor.SampleFormat.VideoChromaSubsampling;
_frameBltParams.DestFormat.NominalRange = videoDescriptor.SampleFormat.NominalRange;
_frameBltParams.DestFormat.VideoTransferMatrix = videoDescriptor.SampleFormat.VideoTransferMatrix;
_frameBltParams.DestFormat.VideoLighting = videoDescriptor.SampleFormat.VideoLighting;
_frameBltParams.DestFormat.VideoPrimaries = videoDescriptor.SampleFormat.VideoPrimaries;
_frameBltParams.DestFormat.VideoTransferFunction = videoDescriptor.SampleFormat.VideoTransferFunction;
_frameBltParams.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
// The default values are used for all these parameters.
DXVA2_ValueRange pRangePABrightness;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Brightness, &pRangePABrightness);
DXVA2_ValueRange pRangePAContrast;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Contrast, &pRangePAContrast);
DXVA2_ValueRange pRangePAHue;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Hue, &pRangePAHue);
DXVA2_ValueRange pRangePASaturation;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Saturation, &pRangePASaturation);
_frameBltParams.ProcAmpValues = { pRangePABrightness.DefaultValue, pRangePAContrast.DefaultValue,
pRangePAHue.DefaultValue, pRangePASaturation.DefaultValue };
_frameBltParams.Alpha = DXVA2_Fixed32OpaqueAlpha();
_frameBltParams.DestData = DXVA2_SampleData_TFF;
// Input video sample for the Blt operation
DXVA2_VideoSample _frameVideoSample;
_frameVideoSample.SampleFormat.VideoChromaSubsampling = videoDescriptor.SampleFormat.VideoChromaSubsampling;
_frameVideoSample.SampleFormat.NominalRange = videoDescriptor.SampleFormat.NominalRange;
_frameVideoSample.SampleFormat.VideoTransferMatrix = videoDescriptor.SampleFormat.VideoTransferMatrix;
_frameVideoSample.SampleFormat.VideoLighting = videoDescriptor.SampleFormat.VideoLighting;
_frameVideoSample.SampleFormat.VideoPrimaries = videoDescriptor.SampleFormat.VideoPrimaries;
_frameVideoSample.SampleFormat.VideoTransferFunction = videoDescriptor.SampleFormat.VideoTransferFunction;
_frameVideoSample.SrcRect = targetRECT;
_frameVideoSample.DstRect = targetRECT;
_frameVideoSample.PlanarAlpha = DXVA2_Fixed32OpaqueAlpha();
_frameVideoSample.SampleData = DXVA2_SampleData_TFF;
CComPtr<IMFSample> sample; // Assume that this was read in from a call to ReadSample
CComPtr<IMFMediaBuffer> buffer;
HRESULT hr = sample->GetBufferByIndex(0, &buffer);
CComPtr<IDirect3DSurface9> pSrcSurface;
// From the MediaBuffer, we get the Source Surface using MFGetService
hr = MFGetService( buffer, MR_BUFFER_SERVICE, __uuidof(IDirect3DSurface9), (void**)&pSrcSurface );
// Update the videoProcessBltParams with frame specific values.
LONGLONG sampleStartTime;
sample->GetSampleTime(&sampleStartTime);
_frameBltParams.TargetFrame = sampleStartTime;
LONGLONG sampleDuration;
sample->GetSampleDuration(&sampleDuration);
_frameVideoSample.Start = sampleStartTime;
_frameVideoSample.End = sampleStartTime + sampleDuration;
_frameVideoSample.SrcSurface = pSrcSurface;
// Run videoProcessBlt using the parameters setup (this is used for color conversion)
// The returned code is E_INVALIDARGS
hr = _vpd->VideoProcessBlt( _pTargetSurface, // target surface
&_frameBltParams, // parameters
&_frameVideoSample, // video sample structure
1, // one sample
NULL); // reserved
After a call to ReadSample of IMFSourceReader or inside OnReadSample callback function of the IMFSourceReaderCallback implementation, you might receive the MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED flag. It means that the current media type has changed for one or more streams. To get the current media type call the IMFSourceReader::GetCurrentMediaType method.
In your case you need to query (again) the IMFMediaType's MF_MT_FRAME_SIZE attribute for the video stream, in order to obtain the new correct video resolution. You should use the new video resolution to set the "width" and "height" values of the VideoProcessorBlt parameters' source and destination rectangles.

Is there a standard way to add textual information in a TIFF file with FreeImage?

I have a program that generates its results as TIFF files. I would like to add some textual information in theses files to keep a trace of the program parameters.
I know that a tag named "ImageDescription" can be added in a tiff file (according to the specification file p34), if I could put the program parameters in that field, it will be ok for me.
But is it possible to set this tag with FreeImage?
If it's not possible, can I add EXIF information to my tiff file with FreeImage?
I answer my own question.
With FreeImage, one simple way to add metadata is to use IPTC tags:
void addTag(FIBITMAP *bitmap, const char *key, const char *value)
{
FITAG *tag = FreeImage_CreateTag();
size_t len = strlen(value)+1;
FreeImage_SetTagKey(tag, len);
FreeImage_SetTagCount(tag, len);
FreeImage_SetTagType(tag, FIDT_ASCII);
FreeImage_SetTagValue(tag, value);
FreeImage_SetMetadata(FIMD_IPTC, bitmap, FreeImage_GetTagKey(tag), tag);
FreeImage_DeleteTag(tag);
}
And, use these function with valid IPTC tags:
// set creator's name, limited to 32 bytes
addTag(bitmap, "By-line", "Creator's name");
// set keyword, limited to 64 bytes
addTag(bitmap, "Keywords", "Param1=foo;Param2=bar");
I would like to modify Mathieve's answer little bit. Otherwise it will not work.
void addTag(FIBITMAP *bitmap, const char *key, const char *value)
{
FITAG *tag = FreeImage_CreateTag();
if(tag)
{
size_t len = strlen(value)+1;
FreeImage_SetTagKey(tag, len);
//Tag length, tag count and tag type should be set before setting tag value
FreeImage_SetTagLength(tag, len);
FreeImage_SetTagCount(tag, len);
FreeImage_SetTagType(tag, FIDT_ASCII);
FreeImage_SetTagValue(tag, value);
FreeImage_SetMetadata(FIMD_IPTC, bitmap, FreeImage_GetTagKey(tag), tag);
//Delete tag after setting the metadata
FreeImage_DeleteTag(tag);
}
}
And, use these function with valid IPTC tags in FreeImage documentation:
// set creator's name, limited to 32 bytes
addTag(bitmap, "By-line", "Creator's name");
// set keyword, limited to 64 bytes
addTag(bitmap, "Keywords", "Param1=foo;Param2=bar");

CGI wont display variables through HTML in c (Eclipse)

I have used a fifo pipe to read in some data (weather data) into a char variable. The console will display this variable correctly. However, when I try to display it through HTML on the CGI page, it simply does not display. Code below -
int main(void) {
int fd;
char *myfifo = "pressure.txt";
char buff[BUFFER];
long fTemp;
//open and read message
fd = open(myfifo, O_RDONLY);
read(fd, buff, BUFFER);
printf("Received: %s\n", buff);
close(fd);
printf("Content-type: text/html\n\n");
puts("<HTML>");
puts("<BODY>");
printf("Data is: %s", buff);
puts("</BODY>");
puts("</HTML>");
return EXIT_SUCCESS;
}
As you can see in the console is displays correctly -
Received: 2014-08-13 16:54:57
25.0 DegC, 1018.7 mBar
Content-type: text/html
<HTML>
<BODY>
Data is 2014-08-13 16:54:57
25.0 DegC, 1018.7 mBar
</BODY>
</HTML>
logout
But on the CGI webpage it does not display the weather data, but it does display "data is".
Two important things when writing a CGI program:
the program will be run by the webserver, which is normally
started as a different user (the 'www' user for example).
it's possible that the program is started from within another
directory, which can cause different behaviour if you don't
specify the full path of a file you want to open.
Since both these things can cause problems, it can be helpful
to add some debug information. Of course, it's always a good idea
to check return values of functions you use.
To make it easier to display debug or error messages, I'd first
move the following code up, so that all output that comes after
it will be rendered by the browser:
printf("Content-type: text/html\r\n\r\n");
puts("<HTML>");
puts("<BODY>");
It may be useful to know what the webserver uses as the directory
from which the program is started. The getcwd
call can help here. Let's use a buffer of size BUFFER to store
the result in, and check if it worked:
char curpath[BUFFER];
if (getcwd(curpath, BUFFER) == NULL)
printf("Can't get current path: %s<BR>\n", strerror(errno));
else
printf("Current path is: %s<BR>\n", curpath);
The getcwd function returns NULL in case of an error, and sets the value
of errno to a number which indicates what went wrong. To convert this
value to something readable, the strerror
function is used. For example, if BUFFER was not large enough to be
able to store the path, you'll see something like
Can't get current path: Numerical result out of range
The open call returns a negative number
if it didn't work, and sets errno again. So, to check if this worked:
fd = open(myfifo, O_RDONLY);
if (fd < 0)
printf("Can't open file: %s<BR>\n", strerror(errno));
In case the file can be found, but the webserver does not have permission
to open it, you'll see
Can't open file: Permission denied
If the program is started from another directory than you think, and
it's unable to locate the file, you would get:
Can't open file: No such file or directory
Adding such debug info should make it more clear what's going on, and more
importantly, what's going wrong.
To make sure the actual data is read without problems as well, the return
value of the read function should be
checked and appropriate actions should be taken. If read fails,
a negative number is returned. To handle this:
numread = read(fd, buff, BUFFER);
if (numread < 0)
printf("Error reading from file: %s<BR>\n", strerror(errno));
Another value indicates success, and returns the number of bytes that were
read. If really BUFFER bytes were read, it's not at all certain that the
last byte in buff is a 0, which is needed for printf to know when the
string ended. To make sure it is in fact null-terminated, the last byte in
buff is set to 0:
if (numread == BUFFER)
buff[BUFFER-1] = 0;
Note that this actually overwrites one of the bytes that were read in this
case.
If fewer bytes were read, it's still not certain that the last byte that was
read was a 0, but now we can place our own 0 after the bytes that were read
so none of them are overwritten:
else
buff[numread] = 0;
To make everything work, you may need the following additional include files:
#include <unistd.h>
#include <string.h>
#include <errno.h>
The complete code of what I described is shown below:
int main(void)
{
int fd, numread;
char *myfifo = "pressure.txt";
char buff[BUFFER];
char curpath[BUFFER];
long fTemp;
// Let's make sure all text output (even error/debug messages)
// will be visible in the web page
printf("Content-type: text/html\r\n\r\n");
puts("<HTML>");
puts("<BODY>");
// Some debug info: print the current path
if (getcwd(curpath, BUFFER) == NULL)
printf("Can't get current path: %s<BR>\n", strerror(errno));
else
printf("Current path is: %s<BR>\n", curpath);
// Open the file
fd = open(myfifo, O_RDONLY);
if (fd < 0)
{
// An error occurs, let's see what it is
printf("Can't open file: %s<BR>\n", strerror(errno));
}
else
{
// Try to read 'BUFFER' bytes from the file
numread = read(fd, buff, BUFFER);
if (numread < 0)
{
printf("Error reading from file: %s<BR>\n", strerror(errno));
}
else
{
if (numread == BUFFER)
{
// Make sure the last byte in 'buff' is 0, so that the
// string is null-terminated
buff[BUFFER-1] = 0;
}
else
{
// Fewer bytes were read, make sure a 0 is placed after
// them
buff[numread] = 0;
}
printf("Data is: %s<BR>\n", buff);
}
close(fd);
}
puts("</BODY>");
puts("</HTML>");
return EXIT_SUCCESS;
}

ActionScript3 - add thousands separator to negative values

This question relates to an animated map template which we have developed at the UKs Office for National Statistics. It has been applied to many datasets and geographies many uses without problem. For example,
http://www.ons.gov.uk/ons/interactive/vp3-census-map/index.html
http://www.statistica.md/pageview.php?l=ro&idc=390&id=3807
The .fla calls on a supporting .as file (see below) to introduce a thousand separator (in the UK a comma, in Germany a full stop (period) defined elsewhwere.
However, the dataset I am currently mapping has large negative values, and it tutrns out that the ORIGINAL HELPER FUNCTION below does not like negative values with 3, 6, 9 or 12 (etc) digits.
-100 to -999 for instance are rendered NaN,100 to NaN,999.
This is because such values are recognised as being 4 digits long. They are being split, the comma introduced, and the -ve sign is misunderstood.
I reckon the approach must be to use absolute values, add in the comma and then (for the negative values) add the -ve sign back in afterwards. But so far, trials of the ADAPTED HELPER FUNCTION have produced only error. :-(
Can anyone tell me how to put the -ve sign back in , please?
Many thanks.
Bruce Mitchell
==================================================================================
//ORIGINAL HELPER FUNCTION: ACCEPTS A NUMBER AND RETURNS A STRING WITH THOUSANDS SEPARATOR ATTACHED IF NECESSARY
function addThouSep(num) {
/*
a. Acquire the number - 'myTrendValue' or 'myDataValue' - from function calcValues
b. Record it (still as a number) to data precision.
1. Turn dataORtrend into a string
2. See if there is a decimal in it.
3. If there isn't, just run the normal addThouSep.
4. If there is, run addThouSep just on the first bit of the string - then add the decimal back on again at the end.
*/
var myNum:Number = correctFPE(num); // Create number variable myNum and populate it with 'num'
// (myTrendvalue or myData Value from calcValues function) passed thru 'correctPFE'
var strNum:String = myNum+""; // Create string version of the dataORtrend number - so instead of 63, you get '63'
var myArray = strNum.split("."); // Create array representing elements of strNum, split by decimal point.
//trace(myArray.length); // How long is the array?
if (myArray.length==1) { // Integer, no decimal.
if (strNum.length < 4)//999 doesn't need a comma.
return strNum;
return addThouSep(strNum.slice(0, -3))+xmlData.thouSep+strNum.slice(-3);
}
else { // Float, with decimal
if (myArray[0].length < 4)//999 doesn't need a comma
return strNum;
return (addThouSep(myArray[0].slice(0, -3))+xmlData.thouSep+myArray[0].slice(-3)+"."+myArray[1]);
}
}
==================================================================================
//ADAPTED HELPER FUNCTION: ACCEPTS A NUMBER AND RETURNS A STRING WITH THOUSANDS SEPARATOR ATTACHED IF NECESSARY
function addThouSep(num) {
/*
a. Acquire the number - 'myTrendValue' or 'myDataValue' - from function calcValues
b. Record it (still as a number) to data precision.
1. Turn dataORtrend into a string
2. See if there is a decimal in it.
3. If there isn't, just run the normal addThouSep.
4. If there is, run addThouSep just on the first bit of the string - then add the decimal back on again at the end.
*/
var myNum:Number = correctFPE(num); // Create number variable myNum and populate it with 'num'
// (myTrendvalue or myData Value from calcValues function) passed thru 'correctPFE'
var myAbsNum:Number = Math.abs(myNum); // ABSOLUTE value of myNum
var strNum:String = myAbsNum+""; // Create string version of the dataORtrend number - so instead of 63, you get '63'
var myArray = strNum.split("."); // Create array representing elements of strNum, split by decimal point.
//trace(myArray.length); // How long is the array?
if (myNum <0){ // negatives
if (myArray.length==1) { // Integer, no decimal.
if (strNum.length < 4)//999 doesn't need a comma.
return strNum;
return addThouSep(strNum.slice(0, -3))+xmlData.thouSep+strNum.slice(-3);
}
else { // Float, with decimal
if (myArray[0].length < 4)//999 doesn't need a comma
return strNum;
return (addThouSep(myArray[0].slice(0, -3))+xmlData.thouSep+myArray[0].slice(-3)+"."+myArray[1]);
}
}
else // positive
if (myArray.length==1) { // Integer, no decimal.
if (strNum.length < 4)//999 doesn't need a comma.
return strNum;
return addThouSep(strNum.slice(0, -3))+xmlData.thouSep+strNum.slice(-3);
}
else { // Float, with decimal
if (myArray[0].length < 4)//999 doesn't need a comma
return strNum;
return (addThouSep(myArray[0].slice(0, -3))+xmlData.thouSep+myArray[0].slice(-3)+"."+myArray[1]);
}
}
==================================================================================
If you're adding commas often (or need to support numbers with decimals) then you may want a highly optimized utility function and go with straightforward string manipulation:
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,
pos:int = input < 0 ? 2 : 1,
commas:int = Math.floor( (front.length - pos) / 3 ),
i:int = 1;
for ( ; i <= commas; i++ )
{
pos = front.length - (3 * i + i - 1);
front = front.slice( 0, pos ) + "," + front.slice( pos );
}
if ( back )
return front + back;
else
return front;
}
While less elegant it's stable and performant — you can find a comparison suite at my answer of a similar question https://stackoverflow.com/a/13410560/934195
Why not use something simple like this function I've made?
function numberFormat(input:Number):String
{
var base:String = input.toString();
base = base.split("").reverse().join("");
base = base.replace(/\d{3}(?=\d)/g, "$&,");
return base.split("").reverse().join("");
}
Tests:
trace( numberFormat(-100) ); // -100
trace( numberFormat(5000) ); // 5,000
trace( numberFormat(-85600) ); // -85,600
Explanation:
Convert the input number to a string.
Reverse it.
Use .replace() to find all occurrences of three numbers followed by another number. We use $&, as the replacement, which basically means take all of those occurences and replace it with the value we found, plus a comma.
Reverse the string again and return it.
Did you try using the built in Number formatting options that support localized number values:
Localized Formatting with NumberFormatter