Программа для чтения текстовых файлов

Программа запрашивает имя или путь к файлу, должна его прочитать и подсчитать кол-во символов.
Все работает до тех пор пока имя пути или файла на английском языке.
Путь к заранее созданному файлу D:\Мои документы\программирование\...\файл.txt на кириллице в массиве filename сохраняется как набор «кракозяблов» и естественно открыть файл не удается.
Подскажите как решить эту проблему?!? Как оптимизировать код?

// чтение файла
#include <iostream>
#include <fstream>
#include <cstdlib>

const int SIZE = 100;

int main()
{
    using namespace std;
    char filename[SIZE];
    ifstream fin;
    cout << "Enter name of data file: ";    // запрос имени файла
    cin.getline(filename, SIZE);
    fin.open(filename); // ассоциация fin с файлом
    if (!fin.is_open()){    // проверка если не удалось открыть файл
        cout << "Couldn't open the file " << filename << endl
            << "Program terminating.\n";
        exit(EXIT_FAILURE);
    }
    char ch;    // для символов
    int i = 0;      // счетчик символов
    fin.get(ch);    // ввод первого значения
    while (fin.good()){ // пока ввод успешен и не достигнут EOF
        ++i;    // считать символы
        cout << ch; // отображать символы
        fin.get(ch);    // получение следующего значения
    }
    if (fin.eof())                      // достигнут конец файла
        cout << "End of file reached.\n";
    else if (fin.fail())    // ввод прекращен из-за несоответствия типа данных
        cout << "Input terminated by data mismatch.\n";
    else                            // ввод прекращен по неизвестной причине
        cout << "Input terminated for unknown reason.\n";
    if (0 == i)                     // данные для обработки отсутствуют
        cout << "No data processed.\n";
    else
        cout << i << " characters read from " << filename << " file.\n";
    fin.close();
    return 0;
}

В общем программа нормальная. Даже оптимизировать почти нечего (но я всё-таки немного накопал). Проблема с локализованными именами файлов — из-за различия в кодировках в консоли (cp-866) и в файловой системе (cp-1251 aka windows-1251). Боремся стандартным способом.

// чтение файла
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <windows.h>

const int BUFSIZE = 256;

int main()
{
    using namespace std;
    char filename[BUFSIZE];
    char safefilename[BUFSIZE];
    ifstream fin;

    cout << "Enter name of data file: ";    // запрос имени файла
    cin.getline(filename, BUFSIZE);
    OemToCharBuff(filename, safefilename, BUFSIZE);

    fin.open(safefilename); // ассоциация fin с файлом
    if (!fin.is_open()){    // проверка если не удалось открыть файл
        cout << "Couldn't open the file " << filename << endl
            << "Program terminated.\n";
        exit(EXIT_FAILURE);
    }

    char ch;    // для символов
    int i = 0;      // счетчик символов

    while (fin.get(ch)) {
        ++i;
        cout << ch;
    }

    if (fin.eof())                      // достигнут конец файла
        cout << "End of file reached.\n";
    else if (fin.fail())    // ввод прекращен из-за несоответствия типа данных
        cout << "Input terminated by data mismatch.\n";
    else                            // ввод прекращен по неизвестной причине
        cout << "Input terminated for unknown reason.\n";
    if (0 == i)                     // данные для обработки отсутствуют
        cout << "No data processed.\n";
    else
        cout << i << " characters read from " << filename << " file.\n";
    fin.close();

    return 0;
}

Ещё увеличил размер буфера под имя файла: 256 — минимально для Винды. А лучше, наверное, использовать класс string, что бы не было проблем с усечением строк.

Можно и string все дело в том, что метод open() требует строку в массиве.

В классе string есть метод c_str(), который возвращает константный указатель на строку в стиле С. То, что доктор прописал ))

Алан, у меня твой код не компилируется. В этой строке OemToCharBuff(filename, safefilename, BUFSIZE);выдает ошибку подобного рода error C2664:'OemToCharBuffW':cannot convert parameter 2 from 'char[256]' to 'LPWSTR'. Если можешь объясни в чем дело.
И как конкретно применить c_str() к моему коду?
Заранее СПАСИБО :)))

Зависит от компилятора.
OemToCharBuff — это макрос, который разворачивается либо в вызов OemToCharBuffA(), либо в вызов OemToCharBuffW() в зависимости от определения символа UNICODE. Для программ с поддержкой юникода символ, естественно, определён и будет использована функция OemToCharBuffW().

Решений несколько:

  1. Отказаться от поддержки юникода на уровне компиляции (указанием соотв. ключа компилятора).
  2. Удалить символ UNICODE: #undef UNICODE. Эта строка должна быть до всех директив #include.
  3. Явно указать вызов ANSI-версии функции: OemToCharBuffA().
  4. Для массива safefilename использовать тип wchar_t вместо char.

Если оставить юникод и ещё подтянуть string, получится так:

// чтение файла
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <windows.h>
#include <string>

using namespace std;

int main()
{
    string filename;
    wchar_t *safefilename;
    ifstream fin;

    cout << "Enter name of data file: ";    // запрос имени файла
    getline(cin, filename);
    int filename_size = filename.length() + 1;
    safefilename = new wchar_t[filename_size];
    OemToCharBuff(filename.c_str(), safefilename, filename_size);

    fin.open(safefilename); // ассоциация fin с файлом

    delete [] safefilename;

    if (!fin.is_open()){    // проверка если не удалось открыть файл
        cout << "Couldn't open the file " << filename << endl
            << "Program terminated.\n";
        exit(EXIT_FAILURE);
    }

    char ch;    // для символов
    int i = 0;      // счетчик символов

    while (fin.get(ch)) {
        ++i;
        cout << ch;
    }

    if (fin.eof())                      // достигнут конец файла
        cout << "End of file reached.\n";
    else if (fin.fail())    // ввод прекращен из-за несоответствия типа данных
        cout << "Input terminated by data mismatch.\n";
    else                            // ввод прекращен по неизвестной причине
        cout << "Input terminated for unknown reason.\n";
    if (0 == i)                     // данные для обработки отсутствуют
        cout << "No data processed.\n";
    else
        cout << i << " characters read from " << filename << " file.\n";
    fin.close();

    return 0;
}

Юрий, по твоей просьбе ещё раз посмотрел твой исходный вариант и предложенные корректировки. Мне собственно добавить особенно нечего. С товарищем Аланом согласен.

Единственно, могу добавить: если целью является совпадение вычисленной длины файла с длиной файла, полученной от операционки, то файл надо открывать в двоичном режиме (ios::in | ios::binary).

СПАСИБО, Алан, Череп!!!
С типом wchar_t safefilename; все заработало.

The headline of the article is “Diseases of the skin”.
The article begins with the general discussion. The two layers that form the skin are the epidermis and the derma. I’d like to mention that the cells of epidermis are of two kinds and this layer varies in thickness in the different species. The derma is composed of muscular fibres and the connective-tissue fibres. I’d like to say briefly that external surface is covered by the vascular and nervous papillae, inner surface is united to the muscular or underlying tissue by a layer of fibro-fatty tissue.
Besides, skin may be covered with the hair and horny productions, such as the horns, chestnuts, ergots, claws and hoofs.
Then the author outlines that the hair varies in length, thickness in the different species. He stresses that breeding, care, heat and cold may cause changes in the thickness of the coat.
In conclusion there are diseases of the skin classified as parasitic and non-parasitic. It is reported that parasitic skin diseases are caused by animal and vegetable parasites, non-parasitic skin diseases are caused by irritation to the skin and internal causes.
I have found the article important from the point of view anatomy study.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.