Thread: Problems with std::string

  1. #1
    Registered User
    Join Date
    Jan 2014
    Posts
    139

    Problems with std::string

    Hello

    I am having issues with std::string formatting. I noticed that the carrige return doesnt work between 1 and 2(see output below)
    It doesnt matter which function i call GetArtist GetGenre etc

    What could be causing this?

    Code:
            std::string sSql = "INSERT INTO songs (";
            sSql.append("  track ");
            sSql.append(", artist ");
            sSql.append(", song  ");
            sSql.append(", album ");
            sSql.append(", genre ");
            sSql.append(", year ");
            sSql.append(", comment ");
            //sSql.append(", path ");
            //sSql.append(", filename ");
            sSql.append(") VALUES ( ");
            sSql.append(" '");
            sSql.append(oMp3->GetTrackNumber());
            sSql.append("', ");
            sSql.append(" '");
            printf_s("1-->");
            printf_s(sSql.c_str());
            sSql.append(oMp3->GetGenre()); // GetArtist doesnt work either 
            printf_s("2-->");
            printf_s(sSql.c_str());
            std::cout << std::endl;
            sSql.append("', ");
            printf_s("3-->");
            printf_s(sSql.c_str());
            std::cout << std::endl;
            sSql.append(" '");
            printf_s("4-->");
            printf_s(sSql.c_str());
            std::cout << std::endl;
            sSql.append(oMp3->GetSongName());
            printf_s("5-->");
            printf_s(sSql.c_str());
            std::cout << std::endl;
            sSql.append("', ");
            printf_s("6-->");
            printf_s(sSql.c_str());

    Problems with std::string-output[1]-jpg

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,403
    You should at least post the relevant part of the class definition of oMp3. Better yet: define a reduced version of the class, then create the object with test data, wrap it all in a small and simple program such that we can compile and run it and see for ourselves what you're talking about.

    Also, why are you using printf_s when you can just use C++-style I/O? If you do want to use printf_s, then you should specify the format string (e.g., "%s\n") just in case the string contains a character sequence that could be misinterpreted to be a format specification.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    edit: let me make those changes and get back to you
    Attached Images Attached Images Problems with std::string-output2-jpg 
    Last edited by EverydayDiesel; 02-17-2014 at 11:25 AM.

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,403
    Well, okay. What carriage return? I normally prefer to talk about newline characters though, since in text mode those will be translated to the appropriate newline sequence, which may or may not involve a carriage return. At the moment, it looks like the only newlines you introduce are when you print std::endl.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    After looking at it closely I determined that the problem is that i am initializing the variables to the MAX length of the possible string.

    when I assign to std::string it seems to be appending the entire 30 characters (the max length - current length) which is full of '\0'

    Even though the length of the m_sAlbum is 7 it is placing 23 additional '\0'. I am not sure that I can initialize all the variables with \0 since append doesnt like it.

    Code:
    CMp3::CMp3() : m_sSongName(30, '\0'),
    m_sArtist(30, '\0'),
    m_sAlbum(30, '\0'),
    m_sYear(4, '\0'),
    m_sComment(28, '\0'),
    m_sTrackNumber(2, '\0'),
    m_sGenreId(1, '\0'),
    //m_sTrackNumber(1, '\0'),
    m_bHasATag(false)
    {
    
    }

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,403
    Ah. Well, I once showed you the technique of initialising a std::string to a specific length with null characters because you needed a buffer, and having such a string is a way of having such a buffer. Generally, this is not done: if you just need string that will later be expanded to the desired length, then default constructing the string will do. In this case, this means that you don't have to list all those std::string members in the constructor's initialiser list because they will be be default constructed anyway.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    I still need to be able to read that block (even if it is white space). I guess the question then becomes, how can I go from say a 30 (even though the real length is 7 + 23 '\0') and get it down to the proper length.

    Is there a way I can resize a buffer and 'trim' the '\0's?

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,403
    Quote Originally Posted by EverydayDiesel
    I still need to be able to read that block (even if it is white space). I guess the question then becomes, how can I go from say a 30 (even though the real length is 7 + 23 '\0') and get it down to the proper length.

    Is there a way I can resize a buffer and 'trim' the '\0's?
    The function that you use to read should return the number of bytes read. Therefore, you can use the resize member function to resize the string to that size.

    Alternatively, if for some reason you don't have that size, then you can use the find_first_of member function to find the first '\0', then use erase to erase the portion of the string starting from that position (as an offset to the iterator returned by the begin member function), if it exists.

    That said, I think that initialising these strings to be buffers of 30 null characters is a wrong approach. Let them be empty strings. Just before you read, you can resize them to the appropriate buffer size, then resize them to fit after the read.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    I did take the initializers out but this is leading to more issues.

    Now the year is not working correctly (please see comments below). If i add the initializers it works again.


    Code:
     
    // new constructor
    CMp3::CMp3() //: 
    //m_sSongName(30, '\0'),
    //m_sArtist(30, '\0'),
    //m_sAlbum(30, '\0'),
    //m_sYear(4, '\0'),
    //m_sComment(28, '\0'),
    //m_sTrackNumber(2, '\0'),
    //m_sGenreId(1, '\0'),
    //m_sTrackNumber(1, '\0'),
    //m_bHasATag(false)
    {
    
    }
    
    
    
        std::ifstream is(sFilePath, std::ifstream::binary);
        if (is)
        {
            int iHeaderLength = 128;
            is.seekg(-iHeaderLength, is.end);
            std::string sHeader(3, '\0');
            // read header
            is.read(&sHeader[0], 3);
    
            if (sHeader == "TAG")
            {
                m_bHasATag = true;
                std::string sTrackYN(1, '\0');
                is.read(&m_sSongName[0], 30);
                is.read(&m_sArtist[0], 30);
                is.read(&m_sAlbum[0], 30);
                is.read(&m_sYear[0], 4);            // this has the year 2000iiiiiiiiiiii (16 characters long)
                is.read(&m_sComment[0], 28);    // this value is -->   <Error reading characters of string.>
                is.read(&sTrackYN[0], 1);
                is.read(&m_sTrackNumber[0], 1);
                is.read(&m_sGenreId[0], 1);
                is.close();
    
                ............

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,403
    Quote Originally Posted by EverydayDiesel
    Now the year is not working correctly (please see comments below). If i add the initializers it works again.
    Recall what I wrote:
    Quote Originally Posted by laserlight
    Just before you read, you can resize them to the appropriate buffer size, then resize them to fit after the read.
    Hence, this is wrong:
    Code:
    is.read(&m_sSongName[0], 30);
    It should be something along the lines of:
    Code:
    m_sSongName.resize(30, '\0');
    is.read(&m_sSongName[0], m_sSongName.length());
    m_sSongName.resize(is.gcount());
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    Thanks. I was not aware that you could do that. I appreciate the example.

    All of the reads were modified like this
    Code:
                m_sArtist.resize(30, '\0');
                is.read(&m_sArtist[0], m_sArtist.length());
                m_sArtist.resize(is.gcount());

    Code:
    // this code puts me right where I was (it will not let me append anything)
    std::string CMp3::GetArtist()
    {
        return m_sArtist;  // the compiler shows "artist" like it should
    }
    This prints the full 30 characters for some reason.
    example "artist "
    Code:
            std::cout << " 1-->" << sSql << std::endl;
            sSql.append(oMp3->GetTrackNumber());
            std::cout << " 2-->" << sSql << std::endl;
            sSql.append("', ");
            std::cout << " 3-->" << sSql << std::endl;
            sSql.append(" '");
            std::cout << " 4-->" << sSql << std::endl;
            sSql.append(oMp3->GetArtist());               // this prints a full 30 chars (whitespace afterwards)
            std::cout << " 5-->" << sSql << std::endl;
            sSql.append("', ");
            std::cout << " 6-->" << sSql << std::endl;
            sSql.append(" '");

    I tried a stringstream trimmer function but that does not work because it does that weird thing where it wont append any more in the string.

    Code:
    std::stringstream trimmer;
    trimmer << str;
    str.clear();
    trimmer >> str;
    
    // i also tried this
    std::stringstream trimmer;
    trimmer << str;
    str.clear();
    trimmer >> str + '\0';
    Also I am not able to insert anything into a sql database because of an 'unrecognized token' error from the sqlite3 engine. I am able to insert if i just hard code the values so I know the database is working correctly.
    Last edited by EverydayDiesel; 02-17-2014 at 04:47 PM.

  12. #12
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,611
    As I said, std::string is not obliged to regard '\0' as the end of its content.

    When I had the idea of telling you to instantiate an MP3 tags object, I was thinking of code along the lines of:

    Code:
    std::string id3v1tags (128, '\0');
    is.seekg (-128, std::ifstream::end); 
    is.read (&idv1tags[0], 128);
    Now as for parsing said tags, you must remember that the data is not obliged to be zero terminated. This is especially true of the Year tag, which, according to this, is 4 bytes long. The solution is to always search for '\0', as many taggers do, in a field as big as the standard earmarks. That might look like this:

    Code:
    #include <algorithm>
    
    typedef std::string::size_type tagpos;
    tagpos scanned = 3; // skip TAG
    tagpos foundZero = id3v1tags.substr (scanned, 30).find_first_of ('\0');
    m_sTitle = id3v1tags.substr (scanned, std::min (30, foundZero));
    scanned += 30;
    
    foundZero = id3v1tags.substr (scanned, 30).find_first_of ('\0');
    m_sArtist = id3v1tags.substr (scanned, std::min (30, foundZero));
    scanned += 30;
    
    foundZero = id3v1tags.substr (scanned, 30).find_first_of ('\0');
    m_sAlbum = id3v1tags.substr (scanned, std::min (30, foundZero));
    scanned += 30;
    
    foundZero = id3v1tags.substr (scanned, 4).find_first_of ('\0');
    m_sYear = id3v1tags.substr (scanned, std::min(4, foundZero));
    scanned += 4;
    
    foundZero = id3v1tags.substr (scanned, 28).find_first_of ('\0');
    m_sComment = id3v1tags.substr (scanned, std::min(28, foundZero));
    scanned += 28;
    if (id3v1tags[scanned] == '\0')
        {
        ++scanned;
        m_cTrackByte = id3v1tags[scanned];
        ++scanned;
        m_cGenreByte = id3v1tags[scanned];
        }
    std::assert (scanned == 128);
    Fair warning that I didn't really test this and it is probably not optimal or very clean: It uses magic numbers and calls substr twice, but I think that is the general idea.
    Last edited by whiteflags; 02-17-2014 at 08:52 PM.

  13. #13
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,403
    Quote Originally Posted by EverydayDiesel
    Also I am not able to insert anything into a sql database because of an 'unrecognized token' error from the sqlite3 engine.
    Oh, so you're using SQLite. What you're doing now is building the SQL statement by string concatenation or string formatting. You have to do this where table and column names are involved, but for values, use prepared statements with the appropriate placeholders, then bind the values to the parameters.

    For example:
    Code:
    // assume that db is a valid sqlite3* and stmt is a sqlite3_stmt*
    
    std::string sql = "INSERT INTO songs "
                      "(track, artist, song, album, genre, year, comment) "
                      "VALUES (:track, :artist, :song, :album, :genre, :year, :comment);";
    
    if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, NULL) == SQLITE_OK)
    {
        sqlite3_bind_text(stmt,
                          sqlite3_bind_parameter_index(stmt, ":track"),
                          oMp3->GetTrackNumber().c_str(),
                          -1,
                          SQLITE_STATIC);
    
        // ...
    
        int result = sqlite3_step(stmt);
        if (result == SQLITE_DONE)
        {
            // Inserted... inform the user?
        }
        else
        {
            // handle the error, e.g., retry if SQLITE_BUSY
        }
    }
    else
    {
        // handle the error, e.g., log it and inform the user
    }
    One thing to think about is whether track and year should be text or integers. If they are supposed to be integers, then you should parse them and store them in the object with some integer type, and then use say, sqlite3_bind_int instead of sqlite3_bind_text when inserting to the database.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  14. #14
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    Quote Originally Posted by whiteflags View Post
    As I said, std::string is not obliged to regard '\0' as the end of its content.

    When I had the idea of telling you to instantiate an MP3 tags object, I was thinking of code along the lines of:

    Code:
    std::string id3v1tags (128, '\0');
    is.seekg (-128, std::ifstream::end); 
    is.read (&idv1tags[0], 128);
    Now as for parsing said tags, you must remember that the data is not obliged to be zero terminated. This is especially true of the Year tag, which, according to this, is 4 bytes long. The solution is to always search for '\0', as many taggers do, in a field as big as the standard earmarks. That might look like this:

    Code:
    #include <algorithm>
    
    typedef std::string::size_type tagpos;
    tagpos scanned = 3; // skip TAG
    tagpos foundZero = id3v1tags.substr (scanned, 30).find_first_of ('\0');
    m_sTitle = id3v1tags.substr (scanned, std::min (30, foundZero));  // it seems to have an issue with the std::min
    scanned += 30;
    
    foundZero = id3v1tags.substr (scanned, 30).find_first_of ('\0');
    m_sArtist = id3v1tags.substr (scanned, std::min (30, foundZero));
    scanned += 30;
    
    foundZero = id3v1tags.substr (scanned, 30).find_first_of ('\0');
    m_sAlbum = id3v1tags.substr (scanned, std::min (30, foundZero));
    scanned += 30;
    
    foundZero = id3v1tags.substr (scanned, 4).find_first_of ('\0');
    m_sYear = id3v1tags.substr (scanned, std::min(4, foundZero));
    scanned += 4;
    
    foundZero = id3v1tags.substr (scanned, 28).find_first_of ('\0');
    m_sComment = id3v1tags.substr (scanned, std::min(28, foundZero));
    scanned += 28;
    if (id3v1tags[scanned] == '\0')
        {
        ++scanned;
        m_cTrackByte = id3v1tags[scanned];
        ++scanned;
        m_cGenreByte = id3v1tags[scanned];
        }
    std::assert (scanned == 128);
    Fair warning that I didn't really test this and it is probably not optimal or very clean: It uses magic numbers and calls substr twice, but I think that is the general idea.




    EDIT:

    This seemed to clear up the issues

    Code:
    std::string::size_type myMin(const std::string::size_type& a, const std::string::size_type& b)
    {
        return !(b<a) ? a : b;     // or: return !comp(b,a)?a:b; for version (2)
    }
    Last edited by EverydayDiesel; 02-18-2014 at 09:06 PM.

  15. #15
    Registered User
    Join Date
    Jan 2014
    Posts
    139
    The reading now works. Thanks whiteflags!!!

    Now I need to work on the sqlite part.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. String problems
    By spikestar in forum C Programming
    Replies: 3
    Last Post: 08-04-2009, 07:00 AM
  2. String problems
    By Conspiracy in forum C++ Programming
    Replies: 10
    Last Post: 09-24-2007, 04:32 PM
  3. string problems
    By Halo2Master in forum C++ Programming
    Replies: 19
    Last Post: 08-04-2005, 12:11 PM
  4. String Problems
    By Junior89 in forum C++ Programming
    Replies: 8
    Last Post: 12-21-2004, 07:00 PM
  5. string problems
    By mart_man00 in forum C Programming
    Replies: 2
    Last Post: 03-27-2003, 06:45 PM