void Test(Packets& packet)
{
char buf[BUFLEN];
char* offset = buf;
unsigned int foo;
for(;!gzeof(file_handle);){
int len = gzread(file_handle, offset, sizeof(buf)-(offset-buf));
char* cur = buf;
char* end = offset+len;
for (char* eol; (cur<end) && (eol = std::find(cur, end, '\n')) < end; cur = eol + 1)
{
string string_array = string(cur, eol);
if(string_array[0] == 'L'){
packet.foo = blah;
}else{
packet.foo = bar;
}
//After readnig a line.. how do I make sure it quits this function (does another job in some other function) and continous from the next line?
}
#ifdef ROTATION
offset = std::rotate(buf, cur, end);
#else
offset = buf + (end-cur);
std::rotate(buf, cur, end);
#endif
}
std::cout<<std::string(buf, offset) ;
}
After readnig a line.. how do I make sure it quits this function (does another job in some other function) and continous from the next line?
I tried to make buf, offset as global variables and read the file only when the buffer is empty.. it still doesn't work.
Why you want to quit the function and continue from the next line?
Change your functions the way that one read a line and the other does whatever with it
somewhere you probably want to do something like or am i wrong?
{
Packets myPacket;
Test(packet);
OtherFunction(packet);
}
Related
I need to return to a previous position in a file that I'm reading after a certain event. The file is opened in text read mode. So I get the read position of the file before reading the needed segment and after that I'm calling ftell to return me to that position. The problem is that it doesn't go to the position that I specified, it instead jumps ahead by two places.
To get the current position I'm using this simplified function.
I'm not sure if the fflush is necessary but it seems to make no difference in the result.
long GetCurPos(void)
{
return ftell(file);
}
To open the file I'm simply using
void Open(void)
{
file = fopen(path, "r");
}
To return to a previous position I'm using this simplified function.
It return a zero value.
void SetPos(long pos) const
{
if (fseek(file, pos, SEEK_SET)) LOG("ferror %d occured!", ferror(file));
}
And finally for function that causes the problems
bool TryPeekWord(char * value, size_t maxLen) const
{
bool result = false;
long start = GetCurPos();
int c;
size_t len;
for (len = 0; len < maxLen; len++)
{
c = fgetc(file);
if (c == EOF)
{
*(value + len) = '\0';
result = len > 0;
break;
}
if ((c == ' ' || c == '\n') && len > 0)
{
*(value + len) = '\0';
result = true;
break;
}
*(value + len) = static_cast<char>(c);
}
SetPos(start);
return result;
}
If I call fseek again after the SetPos call it will be the same value as start but if I then read from the file it will not return the expected characters. What am I doing wrong?
Over the course of improving my question to a proper standard I found that the file I was working on got corrupt. Maybe from moving to different operating system or something?
I copied over the contents of the old file into a new file and it worked fine again.
I am working to validate that a string is utf8.
I have found method g_utf8_validate from glib, which returns:
true/false
the location of the last valid data that was read from the string
Is there a posibility to ge beyond this, and also get the valid data after the non-utf8 portion? Example:
std::string invalid = "okdata\xa0\xa1morevalid";
Currenlty I am able to save "okdata" but I would like to get "okdatamorevalid".
Any ideas? Thank you.
You could keep calling g_utf8_validate on the remaining string (skipping the first byte every time) to find more valid sections:
#include <iostream>
#include <string>
#include <glib.h>
int main() {
char const *data = "okdata\xa0\xa1morevalid";
std::string s;
// Under the assumption that the string is null-terminated.
// Otherwise you'll have to know the length in advance, pass it to
// g_utf8_validate and reduce it by (pend - p) every iteration. The
// loop condition would then be remaining_size > 0 instead of *pend != '\0'.
for(char const *p = data, *pend = data; *pend != '\0'; p = pend + 1) {
g_utf8_validate(p, -1, &pend);
s.append(p, pend);
}
std::cout << s << std::endl; // prints "okdatamorevalid"
}
You can call it in a loop. Something like this:
std::string sanitize_utf8(const std::string &in) {
std::string result;
const char *ptr = in.data(), *end = ptr + in.size();
while (true) {
const char *ptr2;
g_utf8_validate(ptr, end - ptr, &ptr2);
result.append(ptr, ptr2);
if (ptr2 == end)
break;
ptr = ptr2 + 1;
}
return result;
}
I want to create a function that can read a file char by char continuously until some specific char encountered.
This is my method in a class FileHandler.
char* tysort::FileHandler::readUntilCharFound(size_t start, char seek)
{
char* text = new char;
if(this->inputFileStream != nullptr)
{
bool goOn = true;
size_t seekPos = start;
while (goOn)
{
this->inputFileStream->seekg(seekPos);
char* buffer = new char;
this->inputFileStream->read(buffer, 1);
if(strcmp(buffer, &seek) != 0)
{
strcat(text, buffer); // Execution stops here
seekPos++;
}
else
{
goOn = false;
}
}
}
//printf("%s\n", text);
return text;
}
I test this function and it actually works. This is an example to read a file content until new line character '\n' found.
size_t startPosition = 0;
char* text = this->fileHandler->readUntilCharFound(startPosition, '\n');
However, I am sure that something not right is exists somewhere in the code because if I use those method in a loop block, the app will just hangs. I guess the 'not right' things are about pointer but I don't know exactly where. Could you please point out for me?
C++ provides some easy-to-use solutions. For instance:
istream& getline (istream& is, string& str, char delim);
In your case, the parameter would be the equivalent of your text variable and delim would be the equivalent of your seek parameter. Also, the return value of getline would in some way be the equivalent of your goOn flag (there are good FAQs regarding the right patterns to check for EOF and IO errors using the return value of getline)
The lines
if(strcmp(buffer, &seek) != 0)
and
strcat(text, buffer); // Execution stops here
are causes for undefined behavior. strcmp and strcat expect null terminated strings.
Here's an updated version, with appropriate comments.
char* tysort::FileHandler::readUntilCharFound(size_t start, char seek)
{
// If you want to return a string containing
// one character, you have to allocate at least two characters.
// The first one contains the character you want to return.
// The second one contains the null character - '\0'
char* text = new char[2];
// Make it a null terminated string.
text[1] = '\0';
if(this->inputFileStream != nullptr)
{
bool goOn = true;
size_t seekPos = start;
while (goOn)
{
this->inputFileStream->seekg(seekPos);
// No need to allocate memory form the heap.
char buffer[2];
this->inputFileStream->read(buffer, 1);
if( buffer[0] == seek )
{
buffer[1] = '\0';
strcat(text, buffer);
seekPos++;
}
else
{
goOn = false;
}
}
}
return text;
}
You can further simplify the function to:
char* tysort::FileHandler::readUntilCharFound(size_t start, char seek)
{
// If you want to return a string containing
// one character, you have to allocate at least two characters.
// The first one contains the character you want to return.
// The second one contains the null character - '\0'
char* text = new char[2];
text[1] = '\0';
if(this->inputFileStream != nullptr)
{
this->inputFileStream->seekg(start);
// Keep reading from the stream until we find the character
// we are looking for or EOF is reached.
int c;
while ( (c = this->inputFileStream->get()) != EOF && c != seek )
{
}
if ( c != EOF )
{
text[0] = c;
}
}
return text;
}
this->inputFileStream->read(buffer, 1);
No error checking.
if(strcmp(buffer, &seek) != 0)
The strcmp function is used to compare strings. Here you just want to compare two characters.
I attempted writing a thesaurus program which reads a thesaurus file, for example:
drink:beverage
clever:smart,witty
and a .txt document, changing up the words it finds from the thesaurus and creating a new document with the modified text. However there appears to be a bug, I have narrowed it down to the while loop in getReplacement(), by checking a print operation before and after. I would really appreciate someone finding why it won't work.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <iostream>
char* getReplacement(char* original, FILE* file);
int main(int argc, char* argv[])
{
using namespace std;
FILE* thes = fopen(argv[1], "r");
FILE* text = fopen(argv[2], "r+");
FILE* nText = fopen("temp.txt", "w");
if(thes == NULL || text == NULL || nText == NULL)
return 1;
char word[20] = {};
char c;
int bytesW=0;
while((c = fgetc(text)) != EOF)
{
fputc(c, nText);
bytesW++;
if(isalpha(c))
{
int len = strlen(word);
word[len] = c;
word[len + 1] = '\0';
}
else
{
if(word == "")
continue;
cout << 7<<endl;
char* replacement = getReplacement(word, thes);
if(replacement == NULL)
continue;
fseek(nText,bytesW-1-strlen(word),SEEK_SET);
for(int i=0;i<strlen(replacement);i++)
fputc(replacement[i],nText);
int diff = strlen(word) - strlen(replacement);
while(diff-- >0)
fputc(' ', nText);
bytesW = bytesW-1-strlen(word)+strlen(replacement);
fseek(nText, bytesW, SEEK_SET);
}
}
fclose(thes);
fclose(text);
fclose(nText);
return 0;
}
char* getReplacement(char* const original, FILE* file)
{
using namespace std;
char* line="";
const short len = strlen(original);
int numOfOptions=1;
int toSkip=0; // number of commas to skip over
outer: while(fgets(line,1000,file) != NULL)
{
for(int i=0;i<len;i++)
if(line[i] != original[i])
{
goto outer;
}
if(line[len] != ':')
goto outer;
for(int i=0;i<len;i++)
line++;
for(int i=0;i<strlen(line);i++)
if(line[i] == ',')
numOfOptions++;
toSkip = rand()%numOfOptions;
while(toSkip >0)
{
if(line[0] == ',')
toSkip--;
line++;
}
return line;
}
return NULL;
}
char* line="";
// ... snip ...
outer: while(fgets(line,1000,file) != NULL)
Here's your problem. You are trying to read into a literal string; you instead need to allocate an array, on the stack or via malloc() to read into.
A string that you write in quotes in C is known as a literal. This means that this string gets embedded in the code of your program, and later loaded into memory when your programs is loaded. Usually it gets loaded into memory that's marked read-only, but that's platform dependent. That string that you wrote has room only for the null terminator. But you are trying to read up to 1000 characters into it. This will either lead to a segmentation fault because you were writing to read-only memory, or will lead to you writing all over some other memory, producing who knows what behavior.
What you want to do instead is allocate a buffer that you can read into:
char line[1000];
or, if you have limited stack space:
char *line = malloc(1000 * sizeof(char));
Furthermore, in your main() function, you do:
char c;
while((c = fgetc(text)) != EOF)
fgetc() returns an int, not a char. This way, it can return a value corresponding to a valid character if a value is read, or a value that is outside that range if you hit the end of file.
You can't compare strings in C using ==; what that does is compare whether they are the same pointer, not whether they have the same contents. It doesn't really make sense to recalculate the length of the current word each time; why not just keep track of len yourself, incrementing it every time you add a character, and then when you want to check if the word is empty, check if len == 0? Remember to reset len to 0 after the end of the word so you'll start over on the next word. Also remember to reset if len goes over sizeof(word); you don't want to write more than word can hold, or you will start scribbling all over random stuff on your stack and lots of things will break.
Let's say I have a constant c-style string say
const char* msg = "fred,jim,345,7665";
I'd like to tokenize this and read out the individual fields but for performance reasons I don't want to make a copy. How can I do this?
Obviously strtok takes a non-constant pointer and boost::tokenizer is an option but I am unsure what is doing behind the scenes.
Inevitably you will require some copy of the string, even if it is a substring being copied.
If you have a strtok_r function, you can use that, but it will still require a mutable string to do its work. Beware, however, as not all systems provide the function (e.g. Windows), which is why I've provided an implementation here. It works by requiring an additional parameter: a pointer to a C string to save the address of the next match. This allows for it to be more reentrant (thread-safe) in theory. However, you'll still be mutating the value. You could modify it to suit your needs if you like, perhaps copying N bytes into a destination buffer and null-terminating that buffer to avoid the need to modify the source string.
/*
Usage:
char *tok;
char *savep;
tok = mystrtok_r (somestr, ",", &savep);
while (NULL != tok)
{
/* Do something with `tok'. */
tok = mystrtok_r (NULL, ",", &savep);
}
*/
char *
mystrtok_r (char *str, const char *delims, char **nextp)
{
if (str == NULL)
str = *nextp;
str += strspn (str, delims);
*nextp = str + strcspn (str, delims);
**nextp = 0;
if (*str == 0)
return NULL;
++*nextp;
return str;
}
It depends on how you're going to use it.
If you want to get the next token, and then the next (like an iteration over the string, then you only really need to copy the current token into memory.
long strtok2( char *strDest, const char *strSrc, const char cTok, long lOffset, long lMax)
{
if(lMax > 0)
{
strSrc += lOffset;
char * start = strDest;
while(--lMax && *strSrc != cTok && (*strDest++ = * strSrc++) );
*strDest = 0; //for when the token was found, not the null.
return strDest - start - 1; //the length of the token
}
return 0;
}
I snagged a simple strcpy from http://vijayinterviewquestions.blogspot.com.au/2007/07/implement-strcpy-function.html
const char* msg = "fred,jim,345,7665";
char * buffer[20];
long offset = 0;
while(length = strtok2(buffer, msg, ',', offset, 20))
{
cout << buffer;
offset += (length+1);
}
Well, without a little more detail it's hard to know exactly what you want. I'll guess you are parsing delimited items where consecutive delimiters should be treated as zero length tokens (which is usually correct for comma separated elements). I'm also assuming a blank line counts as a single zero length token. This is how I'd approach it:
const char *token_begin = msg;
int length;
for(;;)
{
length = 0;
while(!isDelimiter(token_begin[length])) //< must include \0 as delimiter
++length;
//..do something here with token. token is at: token_begin[0..length)
if ( token_begin[length] != 0 )
token_begin = &token_begin[length+1]; //skip beyond non-null delimiter
else
break; //token null terminated. exit
}
If you are going to store the tokens somewhere then a copy will be necessary in any case and strtok does this nicely by using the string a placing null terminating character inside it.
The only other option I see to avoid copying it is a lexer which reads the string and through a state machine produces tokens by scanning the string and storing the partial results in a buffer but every token should in any case be stored at least in a null terminated string to you are not really saving anything.
Here is my proposal, my code is structured and use a global variable pos(I know global variable are a bad practice but is only to give you the idea), you can replace it with a data member if you need OOP.
int position, messageLength;
char token[MAX]; // MAX = Value greater than the maximum length
// of the tokens(e.g. 1,000);
bool hasNext()
{
return position < messageLength;
}
char* next(const char* message)
{
int i = 0;
while (position < messageLength && message[position] != ',') {
token[i++] = message[position];
position++;
}
position++; // ',' found
token[i] = '\0';
return token;
}
int main(int argc, char **argv)
{
const char* msg = "fred,jim,345,7665";
position = 0;
messageLength = strlen(msg);
while (hasNext())
cout << next(msg) << endl;
return EXIT_SUCCESS;
}