6

Given the following code:

 void parseInput(fstream &inputFile) {
        const int LENGTH = 81;
        char line[LENGTH];

        while(!inputFile.fail()) {
            inputFile.getline(line,LENGTH);
            line = tolower(line);
            cout << line << endl;
        }
    }

upon compiling I'm getting this error:

Error E2285 : Could not find a match for 'tolower(char *)' in function parseInput(fstream &)

I know it returns an int, but not an int[], does that mean instead of using getline i should get input character to character? is there a way to convert the whole line to lower? Thanks in advance for everyone's help!

Kyle Hale
  • 7,912
  • 1
  • 37
  • 58
  • 1
    **Read** the error message and **think** about the code that's being complained about. The return value is obviously not the problem, because `tolower` returns an int **and you assign to an int** - who said anything about an `int*` ? The **parameter** is the problem: it says that it can't find `tolower(char*)`, because that's what you're trying to call - i.e. you're **passing** a `char*` (really a `char[]` that decays), and that version of `tolower` doesn't exist - as others noted, you want to pass a `char`. We use a loop or `std::transform` to repeat the process for each `char` in the array. – Karl Knechtel Dec 05 '10 at 22:33

6 Answers6

6

Hi tolower function input parametr must be char not char*, but if you use std you can use string and std:transform to make string lower case

std::string data = “MyStrData”; 
std::transform(data.begin(), data.end(), data.begin(), ::tolower);
Sanja Melnichuk
  • 3,465
  • 3
  • 25
  • 46
  • 5
    That works with a `char*` too: `std::transform(line, line + strlen(line), ::tolower);` – ephemient Dec 05 '10 at 22:17
  • @ephemient Thanx ill never use transform with char* ill keep it in my mind ) – Sanja Melnichuk Dec 05 '10 at 22:18
  • thanks, I didn't know about transform, I've not used STL very much it is something I intend on learning probably during the Holidays. –  Dec 05 '10 at 22:24
  • 1
    @blakejc70 No problem just ask :) – Sanja Melnichuk Dec 05 '10 at 22:25
  • 2
    Note that (a) `tolower` is in namespace `std` and should be qualified and (b) there is also a locale `std::tolower` function, so `(int(*)(int))std::tolower` may need to be used to disambiguate the function. – James McNellis Dec 05 '10 at 22:28
  • 3
    One can easily exploit this unsafe code by passing it a `"\x80"`. On many systems the ctype functions use lookup tables and `-128` will read out of memory of that lookup table. On my system, for example, `islower(-2222222)` crashes with SIGSEGV. – Johannes Schaub - litb Dec 05 '10 at 22:33
  • 2
    -1 for incorrect use of `tolower` (its argument must be non-negative or EOF). – Cheers and hth. - Alf Dec 05 '10 at 23:11
3

The standalone tolower function only accepts one int, and the int needs to be strictly nonnegative or EOF, otherwise behavior is undefined. Another version of tolower exists, which however is a template. Both of these facts make it difficult to use them with transform easily and safely.

C++ also provides tolower in its ctype facet, which you can use here

std::ctype<char> const& c = std::use_facet< std::ctype<char> >(std::locale());
c.tolower(line, line + std::strlen(line));

However the whole code shows you aren't familiar with arrays and points, so maybe you should start using std::string and easy to use algorithms? Look into boost::string_algo's case conversions.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    +1 This answer is good in that it is apparently correct. Also ref to Boost. :-) But I think you should have discussed consequence of your first remark. Also I find the code abhorrent. Not your fault, that, I find the design of the standard library here abhorrent, resulting in unreadable gobbledegook, that takes time both to create and to decipher... So, posting my own simpler answer. Cheers, – Cheers and hth. - Alf Dec 05 '10 at 23:14
1

Hm, three answers here managed to use tolower incorrectly.

Its argument must be non-negative or the special EOF value, otherwise Undefined Behavior. If all you have are ASCII characters then the codes will all be non-negative, so in that special case, it can be used directly. But if there's any non-ASCII character, like in Norwegian "blåbærsyltetøy" (blueberry jam), then those codes are most likely negative, so casting the argument to unsigned char type is necessary.

Also, for this case, the C locale should be set to the relevant locale.

E.g., you can set it to the user's default locale, which is denoted by an empty string as argument to setlocale.

Example:

#include <iostream>
#include <string>           // std::string
#include <ctype.h>          // ::tolower
#include <locale.h>         // ::setlocale
#include <stddef.h>         // ::ptrdiff_t

typedef unsigned char   UChar;
typedef ptrdiff_t       Size;
typedef Size            Index;

char toLowerCase( char c )
{
    return char( ::tolower( UChar( c ) ) );     // Cast to unsigned important.
}

std::string toLowerCase( std::string const& s )
{
    using namespace std;
    Size const      n   = s.length();
    std::string     result( n, '\0' );

    for( Index i = 0;  i < n;  ++i )
    {
        result[i] = toLowerCase( s[i] );
    }
    return result;
}

int main()
{
    using namespace std;
    setlocale( LC_ALL, "" );                    // Setting locale important.
    cout << toLowerCase( "SARAH CONNER LIKES BLÅBÆRSYLTETØY" ) << endl;
}

Example of instead doing this using std::transform:

#include <iostream>
#include <algorithm>        // std::transform
#include <functional>       // std::ptr_fun
#include <string>           // std::string
#include <ctype.h>          // ::tolower
#include <locale.h>         // ::setlocale
#include <stddef.h>         // ::ptrdiff_t

typedef unsigned char   UChar;

char toLowerCase( char c )
{
    return char( ::tolower( UChar( c ) ) );     // Cast to unsigned important.
}

std::string toLowerCase( std::string const& s )
{
    using namespace std;
    string          result( s.length(), '\0' );

    transform( s.begin(), s.end(), result.begin(), ptr_fun<char>( toLowerCase ) );
    return result;
}

int main()
{
    using namespace std;
    setlocale( LC_ALL, "" );                    // Setting locale important.
    cout << toLowerCase( "SARAH CONNER LIKES BLÅBÆRSYLTETØY" ) << endl;
}

For an example of using the C++ level locale stuff instead of C locale, see Johannes' answer.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

You can simply perform the following loop to convert a string, character-by-character, to lower case:

const int lineLen = strlen( line );
for ( int i = 0; i < lineLen; i++ )
{
     line[i] = static_cast< char >( ::tolower( static_cast< unsigned char >( line[i] ) ) );
}

Edit: furthermore the following code is even easier (assuming the string is null terminated).

_strlwr( line );
Goz
  • 61,365
  • 24
  • 124
  • 204
  • yeah I think i'm going to have to iterate through the character string to do this, I appreciate your time and help. –  Dec 05 '10 at 22:26
  • 1
    -1 for incorrect use of `tolower` (its argument must be non-negative or EOF). – Cheers and hth. - Alf Dec 05 '10 at 23:10
  • Changed a program with undefined behavior into a program with ill-formed b – Johannes Schaub - litb Dec 06 '10 at 08:08
  • @Goz oh wait, i *did* post that comment? I thought i pressed on cancel :) Anyway, that should havebeen " behavior", but i removed it because "ill-formed behavior" sounds like nonsense, but I did let the "b" alone. I certainly haven't slept or 15 hours. You really can see the first effects! – Johannes Schaub - litb Dec 06 '10 at 08:50
  • @Johannes: Fair enough ... whats the ill-formed behaviour? – Goz Dec 06 '10 at 08:51
  • @Goz: the `reinterpret_cast` you added can't perform that conversion. but, don't feel bad about that: one of the most well-known C++ experts did the same (just slightly different types) once. ;-) So, I just fixed it for you: a `static_cast` does the job. Personally I'd just a C style cast for this expressed in C++ notation. But since a C style cast is ungood in most other contexts, people might get the wrong impression from that, so, `static_cast` it is (though I used a C style cast in my own answer, I didn't think about the association effect where people might pick up Bad Habits). Cheers, – Cheers and hth. - Alf Dec 06 '10 at 11:59
  • @Goz: I'm leaving my downvote because there's one more thing that's Bad (but not directly wrong) about this answer: that due to the repeated evaluation of `strlen` it uses time quadratic in the string length, i.e., string length *n*, time *n^2*. As I'm writing this, with `reinterpret_cast` replaced with `static_cast`, the answer is now technically correct, but needlessly inefficient. – Cheers and hth. - Alf Dec 06 '10 at 12:05
  • @Alf: TBH I'm surprised noone commented on that strlen earlier ;) I woukld have done! – Goz Dec 06 '10 at 12:17
  • 1
    @Goz: i'm just a bit lacking in courage, i don't want to sound like i'm bashing. so i just pointed out the main technically incorrect thing. after all, speed is secondary concern (correctness first!), and supporting non-ASCII characters in general (using `setlocale`) third concern, or perhaps no concern at all, depending on context; e.g. I think the code as-is will work for Windows ANSI Western with non-national but non-ASCII chars like euro sign '€'. Cheers, – Cheers and hth. - Alf Dec 06 '10 at 12:25
0

yes and no. you can get a whole line but print it char after char (and using tolower) using a for loop.

tomermes
  • 22,950
  • 16
  • 43
  • 67
-1

tolower() only work on one character at a time. You should do something like

for(int i = 0; i < LENGTH; ++i)
  line[i] = tolower(line[i]);
adl
  • 15,627
  • 6
  • 51
  • 65