This could be a very trivial question, but I have been searching how to get around it without much luck. I have a function to read from the serial port using the libserial function, the response I will get always finishes with a carriage return or a "\r" so, in order to read it, I was thinking in reading character by character comparing if it is not a \r and then storing each character into an array for later usage. My function is as follows:
void serial_read()
{
char character;
int numCharacter = 0;
char data[256];
while(character != '\r')
{
serial_port >> character;
numCharacter++;
character >> data[numCharacter];
}
cout << data;
}
In summary, probably my question should be how to store consecutive chars into an array. Thank you very much for your valuable insight.
I guess you intended
void serial_read()
{
char character = 0;
int numCharacter = 0;
char data[256];
while(character != '\r' && numCharacter < 255)
{
serial_port >> character;
data [numCharacter ++] = character;
}
data [numCharacter] = 0; // close "string"
cout << data;
}
Related
I need to make a program that capitalizes the first character of each sentence in a string. For instance, if the string argument is “hello. my name is Joe. what is your name?” the function should manipulate the string so it contains “Hello. My name is Joe. What is your name?” I'm not sure what I am doing wrong. Any suggestions? Here is my code:
#include<iostream>
#include<cctype>
#include<cstdlib>
using namespace std;
void capitalize(char sentence[], int const SIZE);
int main()
{
const int SIZE = 1024;
char sentence[SIZE];
cout << "Enter a string: " << endl << endl;
cin.getline(sentence, SIZE);
capitalize(sentence, SIZE);
system("pause");
return(0);
}
void capitalize(char sentence[], int SIZE)
{
char *strPtr;
int count = 0;
sentence[0] = toupper(sentence[0]);
for (int i = 0; i < SIZE; i++)
{
strPtr = strstr(sentence[i], ".");
if (*strPtr == '.')
{
*strPtr = toupper(*strPtr);
}
}
while (sentence[count] != '\0')
{
cout << sentence[count];
count++;
}
}
#include <cstring> // need this for strstr()
void capitalize(char sentence[], int SIZE)
{
char *strPtr;
int count = 0;
sentence[0] = toupper(sentence[0]);
for (int i = 0; i < SIZE; i++)
{
strPtr = strstr(&sentence[i], ".");
//strPtr returns the pointer to
//the first occurence of "." after sentence[i]
if(strPtr==NULL) break;
if (*strPtr == '.')
{
// you really dont want to do this
//*strPtr = toupper(*strPtr);
// put the suitable code here and everything will work
}
}
//why the while loop? and count?
while (sentence[count] != '\0')
{
cout << sentence[count];
count++;
}
}
What you were doing was to capitalize "." but clearly you want the next character to be capitalized. So write that part of code yourself as you'll find it more rewarding.
First, as mentioned in the comments, you're not including cstring. Second, you're calling strstr on sentence[i], which is a char. You want sentence + i which is a char*. That'll fix your syntax errors.
For logical error, it looks like you're trying toupper the period.
strPtr = strstr(sentence[i], "."); should find the first period in the string starting at i (inclusive). Then you check if strstr found anything (if not it would return null. If it's found the sequence you uppercase strPtr, but strPtr still points at the first character of the target string, that is '.'. You should be looking for the target string ". " then incrementing one past that to find the first letter of the next sentence. Unfortunately there's no safe way of doing that with strstr since it doesn't tell you how far into the string it looked, so it's possible the string simply ends with ". " and one past that falls off the array. You're either going to need to iterate through the array manually, looking for '.' then checking past that, or use std::find instead.
The current_name is a local char array inside the following loop. I declared it inside the loop so it changes every time I read a new line from a file. But, for some reason the previous data is not removed from the current_name! It prints old data out if it wasn't overridden by new characters from the next line.
ANY IDEAS?
while (isOpen && !file.eof()) {
char current_line[LINE];
char current_name[NAME];
file.getline(current_line, LINE);
int i = 0;
while (current_line[i] != ';') {
current_name[i] = current_line[i];
i++;
}
cout << current_name << endl;
}
You're not terminating current_name after filling it. Add current_name[i] = 0 after the inner loop just before your cout. You're probably seeing this if you read abcdef then read jkl and probably get jkldef for output
UPDATE
You wanted to know if there is a better way. There is--and we'll get to it. But, coming from Java, your question and followup identified some larger issues that I believe you should be aware of. Be careful what you wish for--you may actually get it [and more] :-). All of the following is based on love ...
Attention All Java Programmers! Welcome to "A Brave New World"!
Basic Concepts
Before we even get to C the language, we need to talk about a few concepts first.
Computer Architecture:
https://en.wikipedia.org/wiki/Computer_architecture
https://en.wikipedia.org/wiki/Instruction_set
Memory Layout of Computer Programs:
http://www.geeksforgeeks.org/memory-layout-of-c-program/
Differences between Memory Addresses/Pointers and Java References:
Is Java "pass-by-reference" or "pass-by-value"?
https://softwareengineering.stackexchange.com/questions/141834/how-is-a-java-reference-different-from-a-c-pointer
Concepts Alien to Java Programmers
The C language gives you direct access the underlying computer architecture. It will not do anything that you don't explicitly specify. Herein, I'm mentioning C [for brevity] but what I'm really talking about is a combination of the memory layout and the computer architecture.
If you read memory that you didn't initialize, you will see seemingly random data.
If you allocate something from the heap, you must explicitly free it. It doesn't magically get marked for deletion by a garbage collector when it "goes out of scope".
There is no garbage collector in C
C pointers are far more powerful that Java references. You can add and subtract values to pointers. You can subtract two pointers and use the difference as an index value. You can loop through an array without using index variables--you just deference a pointer and increment the pointer.
The data of automatic variables in Java are stored in the heap. Each variable requires a separate heap allocation. This is slow and time consuming.
In C, the data of automatic variables in stored in the stack frame. The stack frame is a contiguous area of bytes. To allocate space for the stack frame, C simply subtracts the desired size from the stack pointer [hardware register]. The size of the stack frame is the sum of all variables within a given function's scope, regardless of whether they're declared inside a loop inside the function.
Its initial value depends upon what previous function used that area for and what byte values it stored there. Thus, if main calls function fnca, it will fill the stack with whatever data. If then main calls fncb it will see fnca's values, which are semi-random as far as fncb is concerned. Both fnca and fncb must initialize stack variables before they are used.
Declaration of a C variable without an initializer clause does not initialize the variable. For the bss area, it will be zero. For a stack variable, you must do that explicitly.
There is no range checking of array indexes in C [or pointers to arrays or array elements for that matter]. If you write beyond the defined area, you will write into whatever has been mapped/linked into the memory region next. For example, if you have a memory area: int x[10]; int y; and you [inadvertently] write to x[10] [one beyond the end] you will corrupt y
This is true regardless of which memory section (e.g. data, bss, heap, or stack) your array is in.
C has no concept of a string. When people talk about a "c string" what they're really talking about is a char array that has an "end of string" (aka EOS) sentinel character at the end of the useful data. The "standard" EOS char is almost universally defined as 0x00 [since ~1970]
The only intrinsic types supported by an architecture are: char, short, int, long/pointer, long long, and float/double. There may be some others on a given arch, but that's the usual list. Everything else (e.g. a class or struct is "built up" by the compiler as a convenience to the programmer from the arch intrinsic types)
Here are some things that are about C [and C++]:
- C has preprocessor macros. Java has no concept of macros. Preprocessor macros can be thought of as a crude form of metaprogramming.
- C has inline functions. They look just like regular functions, but the compiler will attempt to insert their code directly into any function that calls one. This is handy if the function is cleanly defined but small (e.g. a few lines). It saves the overhead of actually calling the function.
Examples
Here are several versions of your original program as an example:
// myfnc1 -- original
void
myfnc1(void)
{
istream file;
while (isOpen && !file.eof()) {
char current_line[LINE];
char current_name[NAME];
file.getline(current_line, LINE);
int i = 0;
while (current_line[i] != ';') {
current_name[i] = current_line[i];
i++;
}
current_name[i] = 0;
cout << current_name << endl;
}
}
// myfnc2 -- moved definitions to function scope
void
myfnc2(void)
{
istream file;
int i;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof()) {
file.getline(current_line, LINE);
i = 0;
while (current_line[i] != ';') {
current_name[i] = current_line[i];
i++;
}
current_name[i] = 0;
cout << current_name << endl;
}
}
// myfnc3 -- converted to for loop
void
myfnc(void)
{
istream file;
int i;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof()) {
file.getline(current_line, LINE);
for (i = 0; current_line[i] != ';'; ++i)
current_name[i] = current_line[i];
current_name[i] = 0;
cout << current_name << endl;
}
}
// myfnc4 -- converted to use pointers
void
myfnc4(void)
{
istream file;
const char *line;
char *name;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof()) {
file.getline(current_line, LINE);
name = current_name;
for (line = current_line; *line != ';'; ++line, ++name)
*name = *line;
*name = 0;
cout << current_name << endl;
}
}
// myfnc5 -- more efficient use of pointers
void
myfnc5(void)
{
istream file;
const char *line;
char *name;
int chr;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof()) {
file.getline(current_line, LINE);
name = current_name;
line = current_line;
for (chr = *line++; chr != ';'; chr = *line++, ++name)
*name = chr;
*name = 0;
cout << current_name << endl;
}
}
// myfnc6 -- fixes bug if line has no semicolon
void
myfnc6(void)
{
istream file;
const char *line;
char *name;
int chr;
char current_line[LINE];
char current_name[NAME];
while (isOpen && !file.eof()) {
file.getline(current_line, LINE);
name = current_name;
line = current_line;
for (chr = *line++; chr != 0; chr = *line++, ++name) {
if (chr == ';')
break;
*name = chr;
}
*name = 0;
cout << current_name << endl;
}
}
// myfnc7 -- recoded to use "smart" string
void
myfnc7(void)
{
istream file;
const char *line;
char *name;
int chr;
char current_line[LINE];
xstr_t current_name;
xstr_t *name;
name = ¤t_name;
xstrinit(name);
while (isOpen && !file.eof()) {
file.getline(current_line, LINE);
xstragain(name);
line = current_line;
for (chr = *line++; chr != 0; chr = *line++) {
if (chr == ';')
break;
xstraddchar(name,chr);
}
cout << xstrcstr(name) << endl;
}
xstrfree(name);
}
Here is a "smart" string [buffer] class similar to what you're used to:
// xstr -- "smart" string "class" for C
typedef struct {
size_t xstr_maxlen; // maximum space in string buffer
char *xstr_lhs; // pointer to start of string
char *xstr_rhs; // pointer to start of string
} xstr_t;
// xstrinit -- reset string buffer
void
xstrinit(xstr_t *xstr)
{
memset(xstr,0,sizeof(xstr));
}
// xstragain -- reset string buffer
void
xstragain(xstr_t xstr)
{
xstr->xstr_rhs = xstr->xstr_lhs;
}
// xstrgrow -- grow string buffer
void
xstrgrow(xstr_t *xstr,size_t needlen)
{
size_t curlen;
size_t newlen;
char *lhs;
lhs = xstr->xstr_lhs;
// get amount we're currently using
curlen = xstr->xstr_rhs - lhs;
// get amount we'll need after adding the whatever
newlen = curlen + needlen + 1;
// allocate more if we need it
if ((newlen + 1) >= xstr->xstr_maxlen) {
// allocate what we'll need plus a bit more so we're not called on
// each add operation
xstr->xstr_maxlen = newlen + 100;
// get more memory
lhs = realloc(lhs,xstr->xstr_maxlen);
xstr->xstr_lhs = lhs;
// adjust the append pointer
xstr->xstr_rhs = lhs + curlen;
}
}
// xstraddchar -- add character to string
void
xstraddchar(xstr_t *xstr,int chr)
{
// get more space in string buffer if we need it
xstrgrow(xstr,1);
// add the character
*xstr->xstr_rhs++ = chr;
// maintain the sentinel/EOS as we go along
*xstr->xstr_rhs = 0;
}
// xstraddstr -- add string to string
void
xstraddstr(xstr_t *xstr,const char *str)
{
size_t len;
len = strlen(str);
// get more space in string buffer if we need it
xstrgrow(xstr,len);
// add the string
memcpy(xstr->xstr_rhs,str,len);
*xstr->xstr_rhs += len;
// maintain the sentinel/EOS as we go along
*xstr->xstr_rhs = 0;
}
// xstrcstr -- get the "c string" value
char *
xstrcstr(xstr_t *xstr,int chr)
{
return xstr->xstr_lhs;
}
// xstrfree -- release string buffer data
void
xstrfree(xstr_t *xstr)
{
char *lhs;
lhs = xstr->xstr_lhs;
if (lhs != NULL)
free(lhs);
xstrinit(xstr);
}
Recommendations
Before you try to "get around" a "c string", embrace it. You'll encounter it in many places. It's unavoidable.
Learn how to manipulate pointers as easily as index variables. They're more flexible and [once you get the hang of them] easier to use. I've seen code written by programmers who didn't learn this and their code is always more complex than it needs to be [and usually full of bugs that I've needed to fix].
Good commenting is important in any language but, perhaps, more so in C than Java for certain things.
Always compile with -Wall -Werror and fix any warnings. You have been warned :-)
I'd play around a bit with the myfnc examples I gave you. This can help.
Get a firm grasp of the basics before you ...
And now, a word about C++ ...
Most of the above was about architecture, memory layout, and C. All of that still applies to C++.
C++ does do a more limited reclamation of stack variables when the function returns and they go out of scope. This has its pluses and minuses.
C++ has many classes to alleviate the tedium of common functions/idioms/boilerplate. It has the std standard template library. It also has boost. For example, std::string will probably do what you want. But, compare it against my xstr first.
But, once again, I wish to caution you. At your present level, work from the fundamentals, not around them.
Adding current_name[i] = 0; as described did not work for me.
Also, I got an error on isOpen as shown in the question.
Therefore, I freehanded a revised program beginning with the code presented in the question, and making adjustments until it worked properly given an input file having two rows of text in groups of three alpha characters that were delimited with " ; " without the quotes. That is, the delimiting code was space, semicolon, space. This code works.
Here is my code.
#define LINE 1000
int j = 0;
while (!file1.eof()) {
j++;
if( j > 20){break;} // back up escape for testing, in the event of an endless loop
char current_line[LINE];
//string current_name = ""; // see redefinition below
file1.getline(current_line, LINE, '\n');
stringstream ss(current_line); // stringstream works better in this case
while (!ss.eof()) {
string current_name;
ss >> current_name;
if (current_name != ";")
{
cout << current_name << endl;
} // End if(current_name....
} // End while (!ss.eof...
} // End while(!file1.eof() ...
file1.close();
cout << "Done \n";
int ascii[1000] = {0};
string *data = (string*)malloc ( 1000*sizeof( string));
char *text = (char*)malloc ( 1000 *sizeof( char));
cout << "Enter the first arrangement of data." << endl;
cin.getline(text, 1000);
char *token = strtok(text, " ");
while ( token != NULL )
{
if ( strlen(token) > 0)
{
cout << "The tokens are: " << token << endl;
data[Tcount++] = *token;
}
token = strtok(NULL, " ");
for(i=0; i < (Tcount); i++)
{
ascii[i] = (int)data[i]; // error here
}
Im using this code to build a parser and i want to store the ascii values of the tokens which are stored in 'data' into an array named 'ascii'.
When i run the program i get the error message, "error: assigning to 'int' from incompatible type 'string' (aka 'basic_string, allocator >')
Any help would be appreciated.
One thing before the main event here. Obviously you're allowed to use std::string so, let's get the data in a more civilized fashion.
std::vector<std::string> data;
std::string line;
std::getline(cin, line); //read a whole line
std::stringstream tokenizer(line); // stuff the line into an object that's
// really good at tokenizing
std::string token;
while (tokenizer >> token) // one by one push a word out of the tokenizer
{
data.push_back(token); //and stuff it into a vector
}
we now have all of the individual words on the line packed into a nice resizable container, a vector. No messy dynamic memory to clean up.
Step 2: turn those strings into ints. 'Fraid you can't do that, ace. You could take a string that represents a number and turn it into an int. That's easy. Dozens of ways to do it. I like strtol.
But the ascii values are character by character. A string is a variable number of characters. You can pack one into an int, shift the int over by the width of one character and stuff in another, but you're going to run out of space after probably 4 or 8 characters.
Let's go with that, shall we? And we'll do it the old way without an iterator.
std::string data;
int ascii = 0;
if (data.length() > 0)
{
ascii |= data[index];
for(size_t index = 0; index < data.length(); index++)
{
ascii <<= 8; //we're talking ascii here so no unicode bit counting games
ascii |= data[index];
}
}
Done. Not very useful unless all the strings are pretty short, but done.
Instead if you're going to do a parser why not go full geek and try this:
typedef void handlerfunc();
std::map<std::string, handlerfunc> parser;
parser["do something"] = somethingfunc;
parser["do something else"] = somethingelsefunc;
Where somethingfunc is a function that looks like void somethingfunc() that, obviously, does something. Dito somethingelsefunc. Only it does somethingelse.
Usage could be as simple as:
parser[token]();
But it's not. Sigh.
It's more like
found = parser.find(token)
if (found != parser.end())
{
found->second();
return CMD_OK;
}
else
{
return CMD_NOT_FOUND;
}
But seriously, look into some of the fun stuff a good container can do for you. Save a ton of time.
I crapped out all of the code without a compiler. Please let me know if I borked any of it.
This is my code:
char A[10];
char B[5];
cin >> setw(10) >> A;
cin >> setw(5) >> B;
cout << A;
cout << B;
If the input exceeds the array size (ex: 10 for A variable), then the program does not prompt me to enter the data for the second one. It goes right to the the end and execute the two "cout" lines.
Input: abcabcabcabcabcabc (for A)
Output: abcabcabcabca (13 space for char + 2 '\n')
Output expected:
abcabcabc (for A)
dddd (for B)
I want to enter data for both variables even if I entered too many characters for one of them
In C++ you would do this more like as follows
std::string A,B;
std::getline(std::cin,A);
std::getline(std::cin,B);
This avoids any pitfalls with fixed-size arrays, such as char[10] and reads the full line. Alternatively, you may add a delimiter
const auto delim = '.'; // say
std::getline(std::cin,A,delim);
std::getline(std::cin,B,delim);
I don't think there is a simple way (i.e. not coding it yourself) for allowing multiple delimiters.
If you would like to read C strings with a fixed limit, the best approach is to use fgets, which is part of the standard C++ library.
You can also use iomanip to setw, like this:
char A[10];
char B[15];
cin >> setw(10) >> A;
cin >> setw(15) >> B;
Note that the length of the string that you get back will be less by one than the width that you set, because C strings require null termination.
Demo.
Note: Although this mixture of C and C++ would work, you would be better off using std::string for an approach that is more idiomatic to C++. I recognize that this could be a learning exercise in which you are not allowed to use std::string, though.
As you are using C++, you can use string
string A,B;
cin>>A>>B;
Here you can scan as many characters as you want.
If you want to stick with C functions, you've got a couple of options.
The first option is to leverage the fact that fgets includes the newline in the string it reads, but only if the reason it stopped reading is because it hit the end of a line. You can check whether the last character is a newline, and if not, throw out anything left in the input up to and including the next newline:
int count;
fgets(A, 10, stdin);
count = strlen(A);
if (count == 9 && A[8] != '\n') {
do {} while (getc(stdin) != '\n');
}
fgets(B, 15, stdin);
printf("A: %s; B: %s\n", A, B);
If you don't want the newline in your string, be sure to remove it. And you may want to treat too much input as an error rather than just skipping extra characters.
A slightly simpler option is to use scanf instead, but only if you don't want to allow spaces in each variable's input:
int count;
scanf("%9s%n", A, &count);
if (count == 9) {
do {} while (!isspace(getc(stdin)));
}
scanf("%14s", B);
printf("A: %s; B: %s\n", A, B);
This C function reads a line of any length and returns a pointer to it in a newly allocated memory block (remember to free() it). If keepNL is true and a newline character (i.e. not EOF) stopped the reading, it's included at the end of the string. If len isn't NULL, *len is set to the length of the line, including any newline character. It makes it possible to read lines with '\0' in, which strlen() can't handle.
On failure, NULL is returned and *len is unchanged. If feof() is true, EOF was reached before any characters was read (no more lines in the file). If ferror() is true, an I/O error occured. If neither feof() nor ferror() is true, memory was exhausted.
Note that the memory block may be larger than the length of the string. If you need to conserve memory, realloc() it yourself to *len + 1U.
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define MIN_LINE_BUF ((size_t) 128U) /* >= 1 */
char *fgetline(size_t *len, FILE *stream, int keepNL) {
char *buf;
int c;
size_t i, size;
if (!(buf = malloc(size = MIN_LINE_BUF))) {
return NULL;
}
i = 0U;
while ((c = getc(stream)) != EOF) {
if (c != '\n' || keepNL) {
buf[i++] = (char) c;
if (i == size) {
char *newPtr;
if (size > (size_t) -1 - size
|| !(newPtr = realloc(buf, size <<= 1))) {
free(buf);
return NULL;
}
buf = newPtr;
}
}
if (c == '\n') {
break;
}
}
if ((c == EOF && i == 0U) || ferror(stream)) {
free(buf);
return NULL;
}
buf[i++] = '\0';
if (len) {
*len = i;
}
return buf;
}
I basically have a txt file that looks like this...
High Score: 50
Player Name: Sam
Number Of Kills: 5
Map
Time
I want to store everything before the : or whitespace after Map and Time into one array and everything after in another. For both Map and Time, there is nothing after and so I want to store the whitespace as null.
So far, I have managed to read and store all this information into a temp array. However, it is separating that I am having trouble with. This is my code:
istream operator >> (istream &is, Player &player)
{
char **temp;
char **tempNew;
char lineInfo[200]
temp = new char*[5];
tempNew = new char*[5];
for (int i=0; i<5; i++)
{
temp[i] = new char[200];
is.getline(lineInfo, sizeof(lineInfo));
int length = strlen(lineInfo);
for (int z=0; z < length; z++)
{
if(lineInfo[z] == '= ' ){ //HOW DO I CHECK IF THERE IS NOTHING AFTER THE LAST CHAR
lineInfo [length - (z+1)] = lineInfo [length];
cout << lineInfo << endl;
strncpy(temp[i], lineInfo, sizeof(lineInfo));
}
else{
tempNew[i] = new char[200];
strncpy(tempNew[i], lineInfo, sizeof(lineInfo));
}
}
}
If what you need is to find ':'
#include <cstring>
and just
auto occurance = strstr(string, substring);
Documentation here.
if occurance is not a null ptr, then see if occurance is at the end of the line from get line. If not, your value is everything after that :
Much easier with std::string.
// Read high score
int high_score;
my_text_file.ignore(10000, ':');
cin >> high_score;
// Read player name
std::string player_name;
my_text_file.ignore(10000, ':');
std::getline(my_text_file, player_name);
// Remove spaces at beginning of string
std::string::size_type end_position;
end_position = player_name.find_first_not_of(" \t");
if (end_position != std::string::npos)
{
player_name.erase(0, end_position - 1);
}
// Read kills
unsigned int number_of_kills = 0;
my_text_file.ignore(':');
cin >> number_of_kills;
// Read "Map" line
my_text_file.ignore(10000, '\n');
std::string map_line_text;
std::getline(my_text_file, map_line_text);
// Read "Text" line
std::string text_line;
std::getline(my_text_file, text_line);
If you insist on using C-style strings (arrays of char), you will have to use more complex and less safe functionality. Look up the following functions:
fscanf, strchr, strcpy, sscanf