String not displaying all characters in c++ [duplicate] - c++

This question already has answers here:
Print leading zeros with C++ output operator?
(6 answers)
Closed 2 years ago.
I want cout to output an int with leading zeros, so the value 1 would be printed as 001 and the value 25 printed as 025. How can I do this?

With the following,
#include <iomanip>
#include <iostream>
int main()
{
std::cout << std::setfill('0') << std::setw(5) << 25;
}
the output will be
00025
setfill is set to the space character (' ') by default. setw sets the width of the field to be printed, and that's it.
If you are interested in knowing how the to format output streams in general, I wrote an answer for another question, hope it is useful:
Formatting C++ Console Output.

Another way to achieve this is using old printf() function of C language
You can use this like
int dd = 1, mm = 9, yy = 1;
printf("%02d - %02d - %04d", mm, dd, yy);
This will print 09 - 01 - 0001 on the console.
You can also use another function sprintf() to write formatted output to a string like below:
int dd = 1, mm = 9, yy = 1;
char s[25];
sprintf(s, "%02d - %02d - %04d", mm, dd, yy);
cout << s;
Don't forget to include stdio.h header file in your program for both of these functions
Thing to be noted:
You can fill blank space either by 0 or by another char (not number).
If you do write something like %24d format specifier than this will not fill 2 in blank spaces. This will set pad to 24 and will fill blank spaces.

cout.fill('*');
cout << -12345 << endl; // print default value with no field width
cout << setw(10) << -12345 << endl; // print default with field width
cout << setw(10) << left << -12345 << endl; // print left justified
cout << setw(10) << right << -12345 << endl; // print right justified
cout << setw(10) << internal << -12345 << endl; // print internally justified
This produces the output:
-12345
****-12345
-12345****
****-12345
-****12345

In C++20 you'll be able to do:
std::cout << std::format("{:03}", 25); // prints 025
In the meantime you can use the {fmt} library, std::format is based on.
Disclaimer: I'm the author of {fmt} and C++20 std::format.

cout.fill( '0' );
cout.width( 3 );
cout << value;

Another example to output date and time using zero as a fill character on instances of single digit values: 2017-06-04 18:13:02
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
int main()
{
time_t t = time(0); // Get time now
struct tm * now = localtime(&t);
cout.fill('0');
cout << (now->tm_year + 1900) << '-'
<< setw(2) << (now->tm_mon + 1) << '-'
<< setw(2) << now->tm_mday << ' '
<< setw(2) << now->tm_hour << ':'
<< setw(2) << now->tm_min << ':'
<< setw(2) << now->tm_sec
<< endl;
return 0;
}

I would use the following function. I don't like sprintf; it doesn't do what I want!!
#define hexchar(x) ((((x)&0x0F)>9)?((x)+'A'-10):((x)+'0'))
typedef signed long long Int64;
// Special printf for numbers only
// See formatting information below.
//
// Print the number "n" in the given "base"
// using exactly "numDigits".
// Print +/- if signed flag "isSigned" is TRUE.
// Use the character specified in "padchar" to pad extra characters.
//
// Examples:
// sprintfNum(pszBuffer, 6, 10, 6, TRUE, ' ', 1234); --> " +1234"
// sprintfNum(pszBuffer, 6, 10, 6, FALSE, '0', 1234); --> "001234"
// sprintfNum(pszBuffer, 6, 16, 6, FALSE, '.', 0x5AA5); --> "..5AA5"
void sprintfNum(char *pszBuffer, int size, char base, char numDigits, char isSigned, char padchar, Int64 n)
{
char *ptr = pszBuffer;
if (!pszBuffer)
{
return;
}
char *p, buf[32];
unsigned long long x;
unsigned char count;
// Prepare negative number
if (isSigned && (n < 0))
{
x = -n;
}
else
{
x = n;
}
// Set up small string buffer
count = (numDigits-1) - (isSigned?1:0);
p = buf + sizeof (buf);
*--p = '\0';
// Force calculation of first digit
// (to prevent zero from not printing at all!!!)
*--p = (char)hexchar(x%base);
x = x / base;
// Calculate remaining digits
while(count--)
{
if(x != 0)
{
// Calculate next digit
*--p = (char)hexchar(x%base);
x /= base;
}
else
{
// No more digits left, pad out to desired length
*--p = padchar;
}
}
// Apply signed notation if requested
if (isSigned)
{
if (n < 0)
{
*--p = '-';
}
else if (n > 0)
{
*--p = '+';
}
else
{
*--p = ' ';
}
}
// Print the string right-justified
count = numDigits;
while (count--)
{
*ptr++ = *p++;
}
return;
}

Related

How to align numbers after decimal in C++ output [duplicate]

This question already has answers here:
Print leading zeros with C++ output operator?
(6 answers)
Closed 2 years ago.
I want cout to output an int with leading zeros, so the value 1 would be printed as 001 and the value 25 printed as 025. How can I do this?
With the following,
#include <iomanip>
#include <iostream>
int main()
{
std::cout << std::setfill('0') << std::setw(5) << 25;
}
the output will be
00025
setfill is set to the space character (' ') by default. setw sets the width of the field to be printed, and that's it.
If you are interested in knowing how the to format output streams in general, I wrote an answer for another question, hope it is useful:
Formatting C++ Console Output.
Another way to achieve this is using old printf() function of C language
You can use this like
int dd = 1, mm = 9, yy = 1;
printf("%02d - %02d - %04d", mm, dd, yy);
This will print 09 - 01 - 0001 on the console.
You can also use another function sprintf() to write formatted output to a string like below:
int dd = 1, mm = 9, yy = 1;
char s[25];
sprintf(s, "%02d - %02d - %04d", mm, dd, yy);
cout << s;
Don't forget to include stdio.h header file in your program for both of these functions
Thing to be noted:
You can fill blank space either by 0 or by another char (not number).
If you do write something like %24d format specifier than this will not fill 2 in blank spaces. This will set pad to 24 and will fill blank spaces.
cout.fill('*');
cout << -12345 << endl; // print default value with no field width
cout << setw(10) << -12345 << endl; // print default with field width
cout << setw(10) << left << -12345 << endl; // print left justified
cout << setw(10) << right << -12345 << endl; // print right justified
cout << setw(10) << internal << -12345 << endl; // print internally justified
This produces the output:
-12345
****-12345
-12345****
****-12345
-****12345
In C++20 you'll be able to do:
std::cout << std::format("{:03}", 25); // prints 025
In the meantime you can use the {fmt} library, std::format is based on.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
cout.fill( '0' );
cout.width( 3 );
cout << value;
Another example to output date and time using zero as a fill character on instances of single digit values: 2017-06-04 18:13:02
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;
int main()
{
time_t t = time(0); // Get time now
struct tm * now = localtime(&t);
cout.fill('0');
cout << (now->tm_year + 1900) << '-'
<< setw(2) << (now->tm_mon + 1) << '-'
<< setw(2) << now->tm_mday << ' '
<< setw(2) << now->tm_hour << ':'
<< setw(2) << now->tm_min << ':'
<< setw(2) << now->tm_sec
<< endl;
return 0;
}
I would use the following function. I don't like sprintf; it doesn't do what I want!!
#define hexchar(x) ((((x)&0x0F)>9)?((x)+'A'-10):((x)+'0'))
typedef signed long long Int64;
// Special printf for numbers only
// See formatting information below.
//
// Print the number "n" in the given "base"
// using exactly "numDigits".
// Print +/- if signed flag "isSigned" is TRUE.
// Use the character specified in "padchar" to pad extra characters.
//
// Examples:
// sprintfNum(pszBuffer, 6, 10, 6, TRUE, ' ', 1234); --> " +1234"
// sprintfNum(pszBuffer, 6, 10, 6, FALSE, '0', 1234); --> "001234"
// sprintfNum(pszBuffer, 6, 16, 6, FALSE, '.', 0x5AA5); --> "..5AA5"
void sprintfNum(char *pszBuffer, int size, char base, char numDigits, char isSigned, char padchar, Int64 n)
{
char *ptr = pszBuffer;
if (!pszBuffer)
{
return;
}
char *p, buf[32];
unsigned long long x;
unsigned char count;
// Prepare negative number
if (isSigned && (n < 0))
{
x = -n;
}
else
{
x = n;
}
// Set up small string buffer
count = (numDigits-1) - (isSigned?1:0);
p = buf + sizeof (buf);
*--p = '\0';
// Force calculation of first digit
// (to prevent zero from not printing at all!!!)
*--p = (char)hexchar(x%base);
x = x / base;
// Calculate remaining digits
while(count--)
{
if(x != 0)
{
// Calculate next digit
*--p = (char)hexchar(x%base);
x /= base;
}
else
{
// No more digits left, pad out to desired length
*--p = padchar;
}
}
// Apply signed notation if requested
if (isSigned)
{
if (n < 0)
{
*--p = '-';
}
else if (n > 0)
{
*--p = '+';
}
else
{
*--p = ' ';
}
}
// Print the string right-justified
count = numDigits;
while (count--)
{
*ptr++ = *p++;
}
return;
}

Using CheckSum with C++ for 13 Digit ISBN

I am trying to calculate the final digit of a 13 digit ISBN using the first 12 digits using C++. I feel like my code should be correct but I have a feeling the formula I'm using may be wrong.
The formula is:
10 - (d0 + d1 * 3 + d2 + d3 * 3 + d4 + d5 * 3 + d6 + d7 * 3 + d8 + d9 * 3 + d10 + d11 * 3) % 10
Here's what I have:
#include <cstring>
#include <iostream>
int main() {
int weightedSum = 0;
int checksum = 0;
int i; //for loop decrement
int mul = 3;
const int LENGTH = 12;
char ISBNinput[LENGTH];
std::cout << "Enter first 12 digits of ISBN: "; //ask user for input
std::cin >> ISBNinput; //stores input into ISBNinput
std::cout << std::endl;
for (i = 0; i < strlen(ISBNinput); i++) {
weightedSum += (ISBNinput[i] % 12) * mul;
if (mul == 3) {
mul = 1;
} else {
mul = 3;
}
}//close for loop
checksum = weightedSum % 10; //calculates checksum from weightedSum
std::cout << checksum << std::endl; //prints checksum with new line for format
return 0;
}
For example:
978007063546 should return 3
and
978032133487 should return 9
Thank you for any help.
Here's how I go about this.
First, let's decide how we're going to test this. I'll assume that we've written the function, and that it gives the correct output. So I pick up a couple of books off my desk, and test that it works for them:
#include <iostream>
int main()
{
std::cout << "Book 1 - expect 3, got " << checksum("978032114653") << std::endl;
std::cout << "Book 2 - expect 0, got " << checksum("978020163361") << std::endl;
}
Of course, when we try to compile that, we get an error. So create the function, before main():
char checksum(const char *s)
{
return '1';
}
Now it compiles, but the result is always 1, but now we can start to fill in the body. Let's start with some smaller examples, that we can calculate by hand; add these at the beginning of main():
std::cout << "1 digit - expect 4, got " << checksum("6") << std::endl;
Now let's get this one working - this gives us conversion from character to digit and back, at least:
char checksum(const char *s)
{
int digit = *s - '0';
return '0' + 10 - digit;
}
Let's try 2 digits:
std::cout << "1 digit - expect 6, got " << checksum("11") << std::endl;
And now our test fails again. So add some more processing, to make this pass (and not break the single-digit test):
char checksum(const char *s)
{
int sum = 0;
int digit = *s - '0';
sum += digit;
++s;
if (*s) {
digit = *s - '0';
sum += 3 * digit;
}
return '0' + (10 - sum)%10;
}
We're probably ready to make this into a loop now. Once that's passed, we no longer need the short tests, and I have:
#include <iostream>
char checksum(const char *s)
{
int sum = 0;
for (int mul = 1; *s; ++s) {
int digit = *s - '0';
sum += mul * digit;
mul = 4 - mul;
}
return '0' + (1000 - sum)%10;
}
int test(const char *name, char expected, const char *input)
{
char actual = checksum(input);
if (actual == expected) {
std::cout << "PASS: " << name << ": "
<< input << " => " << actual
<< std::endl;
return 0;
} else {
std::cout << "FAIL: " << name << ": "
<< input << " => " << actual
<< " - expected " << expected
<< std::endl;
return 1;
}
}
int main()
{
int failures = 0;
failures += test("Book 1", '3', "978032114653");
failures += test("Book 2", '0', "978020163361");
return failures > 0;
}
I factored out the actual checking into a function here, so we can keep count of failures, and exit with the appropriate status, but everything else is as I described above.
You'll want to add a few more test cases - in particular, make sure the function correctly returns the extreme values 0 and 9 when it should.
There is one clear bug in your code: you are not allocating enough space in for ISBNinput. You should make it one character longer:
const int LENGTH = 13;
The reason for this is that that character-array strings are terminated with an extra null character. You might be lucky and the next byte in memory could sometimes happen to be a null byte, in which case the program would still work sometimes.
If you run the program with valgrind or a similar memory checker you are likely to see an error as the program access memory beyond what was allocated on the stack.
Also I think there is another bug. I think that mul should be initialized to 1.
By the way, this code is very fragile, depending on you entering no more than 12 characters, all of which are assumed to be digits. It might be OK as a quick hack for a proof-of-concept, but should not be used in any real program.

Trying to make an ASCII table in C++, cannot get the "special characters" to display properly

I'm working on an assignment where I need to print out the ASCII table in the table format exactly like the picture below.
http://i.gyazo.com/f1a8625aad1d55585df20f4dba920830.png
I currently can't get the special words/symbols to display (8, 9, 10, 13, 27, 32, 127).
Here it is running:
http://i.gyazo.com/80c8ad48ef2993e93ef9b8feb30e53af.png
Here is my current code:
#include <iomanip>
#include <iostream>
using namespace std;
int main()
{
cout<<"ASCII TABLE:"<<endl;
cout<<endl;
for (int i = 0; i < 128; i++)
{
if (i <= 32)
cout << "|" << setw(2)
<<i
<< setw(3)
<< "^" << char (64+i) <<"|";
if (i >= 33)
cout << "|" << setw(3)
<<i
<< setw(3)
<<char (i) << "|";
if((i+1)%8 == 0) cout << endl;
}
return 0;
}
8 Back Space
9 Horizontal Tab
10 New Line
13 carriage return
27 Escape (Esc)
32 Space
127 Del
As Above these ASCII characters doesn't display any visible or printed character. That's why you might be thinking you are not getting these values.
I'm no sure what's your real problem there, but you didn't get an answer yet about how to print the special codes.
Running your programme I see that you have some minor alignment problems. If that's the problem, note that setw(3) only applies to the next element:
cout << setw(3) << "^" << char (64+i); // prints " ^A" instead of " ^A".
If you try to correct into
cout << setw(3) << "^"+ char (64+i); // ouch !!!!
you'll get undefined behaviour (garbage) because "^" is a pointer to a string and adding char(64+i) is understood as adding an offset of 64+i to this pointer. As this is a rather random address, you'll get garbage. Use a std::string instead.
The other difference I see between your programme's output and the expected result is that you don't print the code of the special chars. If that's the problem, either use a switch statement (very repetitive here), or a lot of if/else or use an associative map.
Here an alternative proposal putting all this together:
map<char, string>special{ { 8, "BS " }, { 9, "\\t " }, { 10, "\\n " }, { 13, "CR " }, { 27, "ESC" }, { 32, "SP " }, { 127, "DEL" } };
cout << "ASCII TABLE:" << endl << endl;
for (int i = 0; i < 128; i++) {
cout << "|" << setw(3)<<i<<setw(4); // setw() only applies to next
if (iscntrl(i) || isspace(i)) { // if its a control char or a space
auto it = special.find(i); // look if there's a special translation
if (it != special.end()) // if yes, use it
cout << it->second;
else cout << string("^") + char(64 + i)+ string(" "); // if not, ^x, using strings
}
else if (isprint(i)) // Sorry I'm paranoïd: but I always imagine that there could be a non printable ctrl ;-)
cout << char(i)+string(" ") ; // print normal char
cout << "|";
if ((i + 1) % 8 == 0) cout << endl;
}
Now some additional advices:
take the effort to indent
instead of manual categorization of chars, use iscntrl(), isspace(), isprint(). As long as you only use ascii, it's manageable to do like you did. But as soons as you move to internationalisation and wide chars it becomes increasinlgy cumbersome to do that whereas there are easy wide equivalents like iswcntrl(), iswspace(), iswprint().
also be rigorous on two consecutive if: If you know that only one of the two should apply, make the effort to write if ... else if these four additional lettes can save you hours of debugging later.

Write int value into an array of bytes

I have this array : BYTE set[6] = { 0xA8,0x12,0x84,0x03,0x00,0x00, } and i need to insert this value : "" int Value = 1200; "" ....on last 4 bytes. Practically to convert from int to hex and then to write inside the array...
Is this possible ?
I already have BitConverter::GetBytes function, but that's not enough.
Thank you,
To answer original quesion: sure you can.
As soon as your sizeof(int) == 4 and sizeof(BYTE) == 1.
But I'm not sure what you mean by "converting int to hex". If you want a hex string representation, you'll be much better off just using one of standard methods of doing it.
For example, on last line I use std::hex to print numbers as hex.
Here is solution to what you've been asking for and a little more (live example: http://codepad.org/rsmzngUL):
#include <iostream>
using namespace std;
int main() {
const int value = 1200;
unsigned char set[] = { 0xA8,0x12,0x84,0x03,0x00,0x00 };
for (const unsigned char* c = set; c != set + sizeof(set); ++c) {
cout << static_cast<int>(*c) << endl;
}
cout << endl << "Putting value into array:" << endl;
*reinterpret_cast<int*>(&set[2]) = value;
for (const unsigned char* c = set; c != set + sizeof(set); ++c) {
cout << static_cast<int>(*c) << endl;
}
cout << endl << "Printing int's bytes one by one: " << endl;
for (int byteNumber = 0; byteNumber != sizeof(int); ++byteNumber) {
const unsigned char oneByte = reinterpret_cast<const unsigned char*>(&value)[byteNumber];
cout << static_cast<int>(oneByte) << endl;
}
cout << endl << "Printing value as hex: " << hex << value << std::endl;
}
UPD: From comments to your question:
1. If you need just getting separate digits out of the number in separate bytes, it's a different story.
2. Little vs Big endianness matters as well, I did not account for that in my answer.
did you mean this ?
#include <stdio.h>
#include <stdlib.h>
#define BYTE unsigned char
int main ( void )
{
BYTE set[6] = { 0xA8,0x12,0x84,0x03,0x00,0x00, } ;
sprintf ( &set[2] , "%d" , 1200 ) ;
printf ( "\n%c%c%c%c", set[2],set[3],set[4],set[5] ) ;
return 0 ;
}
output :
1200

strtok read length

Here's the contents of a text file:
SQUARE 2
SQUARE
RECTANGLE 4 5
I'm trying to figure out why my strtok() loop won't take the end of the 2ND "SQUARE" and just make the length = 0. Don't fully understand the concept behind strtok either, I wouldn't mind a lecture about strtok. Here's the code:
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
ifstream fin;
fin.open("geo.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
//PI
float pi = 3.14159265359;
//DIMENSIONS
float length, width, height, radius;
//AREAS, PERIMETERS, VOLUMES
float areaSquare, periSquare;
float areaRectangle, periRectangle;
float areaCube, volCube;
float areaPrism, volPrism;
float areaCircle, periCircle;
float areaCylinder, volCylinder;
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
const char* token[MAX_TOKENS_PER_LINE] = {0}; // initialize to 0
// parse the line
token[0] = strtok(buf, DELIMITER); // first token
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
token[n] = strtok(0, DELIMITER); // subsequent tokens
if (!token[n] || token[n]==0) break;
}
}
if(strcmp("SQUARE", token[0]) == 0) //1
{
length = atof(token[1])?atof(token[1]):0;
areaSquare = length * length;
periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if(strcmp("RECTANGLE", token[0]) == 0) //2
{
length = atof(token[1])?atof(token[1]):0;
width = atof(token[2])?atof(token[2]):0;
areaRectangle = length * width;
periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "End of program. Press ENTER to exit.";
cin.ignore(1000,10);
break;
}
}
}
Your segmentation fault is caused by this:
length = atof(token[1])?atof(token[1]):0;
You made the mistake of assuming that token[1] was tokenized. If you look at your 2nd 'SQUARE' you'll find that for that line it will have set token[1] to NULL. You then pass NULL to atof() which understandably errors out.
You're also using strtok() improperly. There is no reason to strcpy() from its result, because strtok() itself is a destructive operation.
So here's a lecture about strtok.
Firstly, it's evil, but so handy that you use it anyway sometimes. Tokenizers can be a pain in the butt to write.
The idea behind strtok was to create an easy tokenizer. A tokenizer is a pain in the butt to write, and the interface for it is actually fairly decent if you don't mind making it really easy to blow your computer up with it. You can use a very small amount of code to parse command line arguments, for example.
However, strtok is destructive to the string you use it on. It will replace the token that it finds with a 0, automatically null-terminating the returned value. That means that you can directly use the returned string without needing to copy it. A string like this:
here are spaces0
Is changed into
here0are0spaces0
where 0 delimits end of string character (0). This is done in place, and you get pointers to here, are, and spaces.
strtok uses static variables - meaning it retains state information between calls. On the first call you pass it a pointer to the string you're trying to tokenize; from then on, you pass it a NULL pointer to signal that you want it to continue where it left off before. It returns the next token, returning NULL when it finds the end of the string.
An strtok loop is very easy to write. This code will tokenize a string for you properly. The following example code is ugly, but I blame being tired.
char *input_string; // Just so we have it
const int MAX_TOKENS = 10; // Arbitrary number
char *tokens[MAX_TOKENS]; // This will do all the storage we need.
tokens[0] = strtok(input_string, " -=\""); // Setup call.
int number_of_tokens = 1; // We've already filled tokens[0], so we have 1 token. We want to start filling from 1.
do {
if (tokens[number_of_tokens] = strtok(NULL," -=\"")) number_of_tokens++;
else break;
} while(number_of_tokens < MAX_TOKENS);
That first line in the loop is common practice for C programmers, but is ugly for readability. Here's what it does:
a) It sets tokens[number_of_tokens] to the return value of strtok.
b) If that is NULL, it terminates the loop (second line).
addendnum: there is an inline test. You can do if (a = 1) and it will return true and set a to 1. You can do if (a = 0) it will return false while setting a to 0. This line takes advantage of that fact, if strtok() returns NULL, well, that's false.
c) If that is not NULL, tokens[number_of_tokens] now contains a pointer to the next token found in the string.
d) Since a token was found, the number of tokens (number_of_tokens) is incremented.
5) It reuses the variable that keeps count of how many tokens there are as an index into the array of pointers that it keeps.
6) It loops infinitely until it either meets the condition of strtok returning NULL, or the while() condition (in this case, there are more than 10 tokens).
If it was given this string:
here are some=words0
It would be
*tokens[0]="here"
*tokens[1]="are"
*tokens[2]="some"
*tokens[3]="words"
*tokens[4] = NULL
number_of_tokens = 4
As you can see, there's no need to copy anything, because that string is replaced in memory as such:
here0are0some0words0
where 0 delimits end of string character (0).
I hope this answers your questions.
Here is a version that works.
Main differences are,
Have changed the array of char * to array of 20 char strings. This guarantees the array elements have memory allocated, in your case they are null pointers and stay this way when strtok returns NULL, you cannot then use a NULL pointer.
The second call to strtok is "strtok(0, DELIMITER)"
but should be "strtok(NULL, DELIMITER)".
I think they are the only diffs, but use the diff utility to check.
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
char *tok;
ifstream fin;
fin.open("geo.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
//PI
float pi = 3.14159265359;
//DIMENSIONS
float length, width, height, radius;
//AREAS, PERIMETERS, VOLUMES
float areaSquare, periSquare;
float areaRectangle, periRectangle;
float areaCube, volCube;
float areaPrism, volPrism;
float areaCircle, periCircle;
float areaCylinder, volCylinder;
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
// const char* token[MAX_TOKENS_PER_LINE] = {0}; // initialize to 0
char token[MAX_TOKENS_PER_LINE][20];
for (n=0;n<MAX_TOKENS_PER_LINE;n++)
{
token[n][0] = NULL;
}
// parse the line
tok = strtok(buf, DELIMITER); // first token
if (tok == NULL)
break;
strcpy(token[0],tok);
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
tok = strtok(NULL, DELIMITER); // subsequent tokens
if (tok == NULL)
break;
strcpy(token[n],tok);
// if (!token[n] || token[n]==0) break;
}
}
if(strcmp("SQUARE", token[0]) == 0) //1
{
length = atof(token[1])?atof(token[1]):0;
areaSquare = length * length;
periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if(strcmp("RECTANGLE", token[0]) == 0) //2
{
length = atof(token[1])?atof(token[1]):0;
width = atof(token[2])?atof(token[2]):0;
areaRectangle = length * width;
periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "End of program. Press ENTER to exit.";
cin.ignore(1000,10);
break;
}
}
}
Ok. When your line
const char* token[MAX_TOKENS_PER_LINE] = {0};
creates an array of pointers, but none of them point to anything. The first element is set to 0 (which is a NULL address) and the rest are not initialised. When you run and process line 2 (which has 1 element) token[0] points to 'SQUARE' but token[1] is given the value 0x00 (NULL). This is an invalid memory location. You then process token[1] with the line
length = atof(token[1])?atof(token[1]):0;
and this causes a Segmentation fault because token[1] is a NULL pointer. In my version token[1] is a valid pointer to a NULL string, which sets length to 0. I suggest you compile with the -g flag (eg g++ -g test.cpp -o test). Then call 'gdb test' and use break, run, continue commands to step through the code. You can use the print command to display the contents of variables.
In the first run in gdb just enter 'run'. This will fail, then enter 'bt' which will tell you the failing line, let's call it linenumber.
In the second run enter 'break linenumber' and then 'run' and the execution will stop on the failing line but before it is executed. You can then look at the contents of the variables which will give you a big clue to why it is failing.
Here is some working C++ based closely on your code.
I've revised the I/O handling; fin.getline() reports whether it got a line or not, so it should be used to control the loop; fin.eof() is a red flag warning in my estimation (as is feof(fp) in C).
The core dump occurs because you don't check that you got a length token after the word SQUARE. The revised code checks that it got exactly the correct number of tokens, complaining if not. The code using strtok() has been unified into a single loop; it contains a diagnostic print statement that shows the token just found (valuable for checking what's going on).
I removed a pile of unused variables; each variable is defined and initialized in the calculation blocks.
There are endless possible reservations about using C strings and strtok() in C++ (the printing would be a lot more succinct if all the code were written in C using the C standard I/O functions like printf()). You can find a discussion of the alternatives to strtok() at Strange strtok() error. You can find another discussion on why strtok() is a disaster in a library function at Reading user input and checking the string.
Working code for the 3 lines of data in the question
#include <cstring>
#include <cstdlib>
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
using std::ios;
using std::cerr;
#include<iomanip>
using std::setprecision;
#include <fstream>
using std::ifstream;
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
const char *fname = "geo.txt";
ifstream fin;
fin.open(fname); // open a file
if (!fin.good())
{
cerr << "Failed to open file " << fname << endl;;
return 1; // exit if file not found
}
// read each line of the file
char buf[MAX_CHARS_PER_LINE];
while (fin.getline(buf, sizeof(buf)))
{
int n = 0;
const char *token[MAX_TOKENS_PER_LINE] = {0};
char *position = buf;
while ((token[n] = strtok(position, DELIMITER)) != 0)
{
cout << "Token " << n << ": " << token[n] << endl;
n++;
position = 0;
}
if (strcmp("SQUARE", token[0]) == 0 && n == 2)
{
float length = atof(token[1])?atof(token[1]):0;
float areaSquare = length * length;
float periSquare = 4 * length;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "Area=" << areaSquare << ' ';
cout << "Perimeter=" << periSquare << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else if (strcmp("RECTANGLE", token[0]) == 0 && n == 3)
{
float length = atof(token[1])?atof(token[1]):0;
float width = atof(token[2])?atof(token[2]):0;
float areaRectangle = length * width;
float periRectangle = 2 * length + 2 * width;
cout.setf(ios::fixed|ios::showpoint);
cout << setprecision(2);
cout << token[0] << ' ' << "length="<< token[1] << ' ';
cout << "width=" << token[2] << ' ' ;
cout << "Area=" << areaRectangle << ' ';
cout << "Perimeter=" << periRectangle << '\n';
cout.unsetf(ios::fixed|ios::showpoint);
cout << setprecision(6);
}
else
{
cout << "Unrecognized data: " << buf << endl;
}
}
}