PDA

View Full Version : خوندن و نوشتن رشته فارسي از/در فايل



Pari_Programmer
پنج شنبه 13 اسفند 1388, 21:45 عصر
سلام دوستان.

من يه تابع نوشتم براي كار با كاراكترهاي فارسي. ورودي اون رو يك رشته wstring گرفتم. خروجيش هم از همون نوعه. براي تست اين تابع يك رشته از همون نوع گرفتم و مقداردهي اوليه كردم، همه چيز درست كار ميكرد. اما وقتي ميخوام اون رشته رو از يك فايل بخونم نميتونم. كسي ميتونه راهنمايي كنه؟ در حقيقت من دنبال اين هستم كه ببينم چه جوري ميشه يك رشته فارسي رو از يك فايل خوند و در يك متغير از نوع wstring ذخيره كرد.
اگه ممكنه بگيد در انتها براي كپي كردن اين رشته توي فايل چي كار بايد بكنم؟
اين رو هم بگم كه من توي C++ استاندارد كد ميزنم.
ممنونم.

Nima_NF
جمعه 14 اسفند 1388, 01:00 صبح
لطفا قطعه کد خود را قرار دهید تا بررسی کنم که از چه شیوه ای بری خواندن استفاده می کنید و تغییرات مورد نیاز را اعمال کنم.
در کل فرق چندانی بین رشته فارسی و انگلیسی نیست، فقط باید کارکتر ها را wchar_t به جای char در نظر بگیرید و از معادل های یونیکد توابع استفاده کنید.

Pari_Programmer
جمعه 14 اسفند 1388, 09:06 صبح
سلام. اين قطعه برنامه ايه كه من نوشتم:





std::wstring AnalyzeString(std::wstring wstrIn)
{
int len = wstrIn.length();
for(int index = 0; index < len; index++)
{
wstrIn[index] = AnalyzeChars(wstrIn[index]);
}

return wstrIn;

}


تابع AnalyzeChars‌هم تابع ديگه ايه كه كاراكترها رو بررسي ميكنه و يك سري كارها رو اونها انجام ميده. اين توابع درست كار ميكنه چون وقتي برنامه رو بدون خوندن از فايل تست ميكنم درست جواب ميگيرم. يعني ميام يه متغير wstring ميگيرم، بهش توي برنامه مقدار ميدم و ميفرستمش به تابعAnalyzeString همه چي خوب كار ميكنه. گير من وقتيه كه ميخوام از فايل بخونم.:افسرده::ناراحت:
من هم فكر ميكنم كه نبايد فرق چنداني بين خوندن فارسي و انگليسي نباشه اما نميدونم چرا جواب نميگيرم.
من بايد در تابع main خودم يك رشته wstring رو از يه فايل بگيرم، بدمش به تابعي كه بالا گفتم و بعد رشته خروجي رو توي يه فايل بنويسم. اما هركاري كردم نتونستم.

لطفا كمكم كنيد. خيلي عجله دارم. بايد هرچه زودتر اين برنامه رو بنويسم.
ممنونT

Nima_NF
جمعه 14 اسفند 1388, 13:47 عصر
منظور بنده قطعه کد خواندن و نوشتن از فایل بود که فرمودید درست عمل نمی کند، شما آن را قرار ندادید!

شما اگر فایل را به شکل باینری می خوانید مشکلی نباید باشد و طول هر کارمتر را 2 بایت باید در نظر بگیرید.

ضمنا هنگام نوشتن در فایل شما نباید رشته wstring را مستقیم به خروجی بفرستید، چرا که این یک کلاس هست. شما باید آن را از طریق متد c_str به رشته ای از char یا wchar_t تبدیل کنید و سپس در فایل بریزید یا بخوانید.

مثلا برای نوشتن:



const wchar_t *c_str1 = wstrIn.c_str ( );
cout << c_str1 ;

// or
// MyFile << c_str1 ;


مثلا برای خواندن:



wchar_t strTemp[1024];
MyFile >> strTemp ;

wstring wstr1( strTemp );


البته موارد فوق فقط یک مثال هست، توصیه می کنم از خواندن و نوشتن باینری برای یونیکد ها استفاده کنید.


ضمنا اگر می خواهید فایل txt به شکل یونیکد (فارسی) داشته باشید باید کارکتر 0xFEFF را که همان UTF-16 هست را در اول فایل بریزید.
لینک مورد نظر (http://barnamenevis.org/forum/showthread.php?t=148286)

Pari_Programmer
جمعه 14 اسفند 1388, 20:37 عصر
چرا من نميتونم اين برنامه رو بنويسم؟:افسرده:
آقا نيما ميشه يه برنامه كامل برام بزارين كه محتويات يك فايل فارسي رو بگيره بريزه توي يك متغير wstring وبعد محتويات اون رشته رو بريزه توي يه فايل ديگه؟:خجالت:
من شرمندم. اما نميدونم چرا در اين مورد خنگ شدم و نميتونم يه تيكه كد بنويسم كه درست كار كنه...
من برنامم رو اينجوري نوشتم:




int main()
{
wifstream inFile("in.txt",ios::in,ios::binary);
wofstream outFile("out.txt",ios::out,ios::binary);

wstring wstrIn=L"سلام";
const wchar_t *c_str1 = wstrIn.c_str ( );
outFile << c_str1 ;

getch();

return 0;
}

وقتي اين برنامه رو اجرا ميكنم توي فايل خروجي هيچي وجود نداره و سايزش رو 0 ميزنه. اما اگه به جاي رشته سلام بنويسم salam مشكل حل ميشه و برنامه درست كار ميكنه.
واسه خوندن هم مشكل دارم. وقتي محتويات متغير wstrIn رو چك ميكنم (بعد از اينكه محتويات فايل ريخته شد توي اون) يه مشت كاراكتر بي معني رو مشاهده ميكنم. حسابي گيج شدم. در ضمن من ميخوام فايل رو خط به خط بخونم. ولي ديگه نميدونم اين رو كجاي دلم بزارم...؟:ناراحت::عصبانی++:

فقط تو رو خدا زود جواب بديد. قضيه خيلي فوريه.
ممنون.

amin1softco
جمعه 14 اسفند 1388, 21:32 عصر
اینجوری یک فایل تکست یونیکد رو می خونند البته کارکتر به کارکتر


#include <iostream>
#include <fstream>

using namespace std;

int main ()
{
wchar_t ch;

wifstream infile;

wfilebuf * inbuf;

infile.open("I:\\test-unicode.txt");

inbuf = infile.rdbuf();

ch = inbuf->sgetc();

while(ch != 0xffff)
{
wcout.put(ch);
ch = inbuf->snextc();
}

infile.close();

system("pause");

return 0;
}

Pari_Programmer
جمعه 14 اسفند 1388, 21:42 عصر
و اگه بخوام موارد خونده شده رو بريزم توي يك متغير wstring چيكار بايد بكنم؟

amin1softco
جمعه 14 اسفند 1388, 22:09 عصر
اینجوری به رشته تبدیل میشه کل محتویات فایل


#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main ()
{
wchar_t ch;
wstringstream o;

wifstream infile;

wfilebuf * inbuf;

infile.open("c:\\test-unicode.txt");

inbuf = infile.rdbuf();

ch = inbuf->sgetc();

while(ch != 0xffff)
{
o<<ch;
ch = inbuf->snextc();
}
wstring wstrIn=o.str();
wcout<<wstrIn;
infile.close();

system("pause");

return 0;
}

Pari_Programmer
جمعه 14 اسفند 1388, 23:05 عصر
چرا اين برنامه اينقدر گير شده
آقا اين كل كديه كه من نوشتم:


int main()
{
int i=0;
wstring wstr(L"سلام");
wchar_t c;
int len = wstr.length();
wstrIn=ConvertString(wstr);
return 0;
}

wstring ConvertString(wstring wstrIn)
{
int len = wstrIn.length();
for(int index = 0; index < len; index++)
{
wstrIn[index] = ConvertChar(wstrIn[index]);
}

return wstrIn;

}
}

wchar_t ConvertChar(wchar_t c)
{
switch(c)
{
// persian to English exchange
case 0xFEB3: //س

return 0053;// S
case ساير موارد
return معادل انگليسي آنها به ترتيب فوق
}

return c;
}

تا اينجا همه چيز درسته و درست كار ميكنه. البته كل تابع رو من اينجا نيوردم. فقط خواستم بدونيد تابع قراره چيكار كنه. (قراره رشته رو كاراكتر كاراكتر بخونه و اونها رو به يه كاراكتر ديگه تبديل كنه.)
حالا اگه بخوام اين رشته رو از فايل بخونم و بعد نتيجه رو تو فايل بنويسم چي كنم؟ (ممكنه خروجي نهايي به زبان فارسي باشه).

هر چي دوستان تا حالا گفتن تست كردم ولي از هيچ كدوم نتونستم جواب بگيرم. نميدونم مشكل از كجاست؟
آقا امين اون روشي كه شما گفتي رو تست كردم. محتويات فايل رو انگار به درستي نميخوند. چرا كه هيچ كدوم از شرايطي كه توي تابع ConvertChar‌نوشتم محقق نميشن. اين در صورتيه كه من همون رشته "سلام" رو كه توي قطعه كد بالا ميبينيد توي فايل وروديم نوشتم.
حسابي گيج شدم. وقتي متغير رو تو برنامه مقدار ميدم خيلي خوب كار ميكنه و چك كه ميكنم ميبينيم توي حلقه for كاراكترها بدرستي جدا ميشن. (با استفاده از watch محتوياتشون رو نگاه ميكنم.). اما وقتي از فايل ميخوام بخونم نميشه....
:ناراحت::عصبانی++::عصبانی++::ع بانی++::عصبانی++::عصبانی++::عص انی++::عصبانی++::ناراحت:

يكي كمك كنه..........

Nima_NF
شنبه 15 اسفند 1388, 02:21 صبح
همیشه برای کارهای یونیکد از API های ویندوز استفاده می کردم به همین خاطر یادم رفته بود که fstream چقدر با یونیکد مشکل دارد.

متاسفانه حق با شماست، راحت نمی توانید فارسی را با iostream ذخیره کنید، چون basic_ofstream کارکترهای یونیکد را به MBCS تبدیل می کند، در نتیجه رشته ای ذخیره نمی شود.

یک راه حل برای این کار هست که کلاس NullCodecvt و تعاریف ذکر شده در لینک زیر را پیاده سازی کنید تا این مشکل بر طرف شود:
لینک مقاله (کلیک کنید) (http://www.codeproject.com/KB/stl/upgradingstlappstounicode.aspx)

در هر حال اگر بخواهید از API های ویندوز استفاده کنید (بدون iostream ) در پست دوم لینک تاپیک به همراه مثال قرار داده ام.

amin1softco
شنبه 15 اسفند 1388, 06:57 صبح
والا من که نمی دونم مشکل کجاست چون من می تونم یک فایل یونیکد رو بخونم و دوباره مثل خودشو با این روش بنویسم می تونم تغیرشم بدم


#include <iostream>
#include <fstream>
#include <sstream>
#include <windows.h>


using namespace std;

int main ()
{
wchar_t ch;
wstringstream o;

wifstream infile;
wofstream outfile;

wfilebuf * inbuf;

infile.open("c:\\read-unicode.txt");
outfile.open("c:\\write-unicode.txt");

inbuf = infile.rdbuf();


ch = inbuf->sgetc();

while(ch != 0xffff)
{
o<<ch;
ch = inbuf->snextc();
}
wstring wstrIn=o.str();
wcout<<wstrIn;
for (int i=0;i<wstrIn.length();i++){
//wstrIn[i]=wstrIn[i+1];
}
outfile<<wstrIn;
infile.close();
outfile.close();

system("pause");

return 0;
}

http://up.iranblog.com/37261/1267931831.jpg

Pari_Programmer
شنبه 15 اسفند 1388, 08:31 صبح
آقا امين، فايلي كه شما ميخونيد داراي كاراكترهاي فارسيه يا انگليسي؟
من با فايلي كه همه محتوياتش كاراكترهاي انگليسي باشند مشكلي ندارم. گير من وقتيه كه ميخوام كاراكترهاي فارسي رو بخونم...
هرچي هم گشتم تا حالا نتونستم يه راه استاندارد پيدا كنم. چون من ميخوام كدم قابليت حمل داشته باشه يعني هم تو ويندوز اجرا شه هم تو لينوكس. واسه اينه كه نميخوام از APIهاي ويندوز استفاده كنم.
حسابي گيج شدم.
چندتا كتابخونه open source هم كه پيدا كردم(مثل UTF_8، fridibi، ICU و ...)، يا بايد روي سيستم نصب ميشد يا اينكه مثالي از چگونگي استفادشون نذاشته بودن، من هم نميخوام كتابخونه رو نصب كنم، بايد كدش رو داخل كد برنامم بذارم كه براحتي بتونم حملش كنم. خيلي هم سوادم به اون كتابخونه هاي فاقد مثال قد نميداد.
لطفا اگه كسي ميتونه راهنمايي كنه.
ممنون.

amin1softco
شنبه 15 اسفند 1388, 13:19 عصر
اون تابعه احتیاجی به سر فایل ویندوز نداره داشتم تست می کردم یادم رفت پاکش کنم
در ضمن یگ روتین ورودی خروجی رو تو لینک زیر دیدم که باید با برنامه حملش به قول شما شاید بدرد بخوره پیوستش کردم
http://www.codeproject.com/KB/files/EZUTF.aspx

Pari_Programmer
شنبه 15 اسفند 1388, 13:40 عصر
ممنون. دانلودش كردم، تست ميكنم ببينم درست ميشه يا نه.
يه چيز به ذهنم رسيد. اگه فايلم رو با فرمت utf8 ذخيره كنم، ميشه راحتتر محتوياتش رو از ورودي خوند يا نه؟
راستي آقا امين نفرموديد كه برنامتون رو با فايل فارسي تست كرديد يا نه؟ محتوياتش رشته هاي فارسي بودن يا انگليسي؟ شرمنده كه اين سوال رو پرسيدم آخه خودم برنامتون رو با كاراكترهاي انگليسي تست كردم خيلي خوب جواب ميداد آما كاراكترهاي فارسي....

amin1softco
شنبه 15 اسفند 1388, 15:09 عصر
من برنامه خودم رو توی سون با کارکتر های فارسی یونیکد تست کردم عکسش اون بالا گذاشتم نمی دونم مشکل شما از کجاست؟؟؟؟؟؟؟ در ضمن در سایت رسمی مایکروسافت نوشته که کنسول قابلیت نمایش کارکتر های عربی و فارسی را نداره پس باید فایل خروجی رو شما مشاهده کنید

Nima_NF
شنبه 15 اسفند 1388, 16:07 عصر
والا من که نمی دونم مشکل کجاست چون من می تونم یک فایل یونیکد رو بخونم و دوباره مثل خودشو با این روش بنویسم می تونم تغیرشم بدم

چون شما داخل خود سورس کدهای برنامه فارسی نمی نویسید و در متغیر رشته ای ذخیره نمی کنید. شما مستقیم از فایل می خوانید و سپس از redbuf استفاده می کنید و کارکتر به کارکتر عمل می کنید، لذا کارکترها درست تبدیل می شوند و ذخیره می شوند.
اما اگر به روش معمول، یعنی نوشتن مستقیم فارسی در سورس کد ها عمل کنید، درست عمل نمی کند.


هرچي هم گشتم تا حالا نتونستم يه راه استاندارد پيدا كنم. چون من ميخوام كدم قابليت حمل داشته باشه يعني هم تو ويندوز اجرا شه هم تو لينوكس. واسه اينه كه نميخوام از APIهاي ويندوز استفاده كنم.
حسابي گيج شدم.تست کردم و دیدم بدون توابع یونیکد خواندن / نوشتن راحت می توان به شکل binary اطلاعات کارکترهای یونیکد را به شکل استاندارد با fstream دریافت کرد.( همانند کاری که با API ها انجام می دهیم.)
پس می توانید به شکل زیر عمل کنید، متغیر های رشته ای فارسی نیز درست عمل می کنند.

برای خواندن از فایل:




wstring wstrFarsi(L"فارسی");
wchar_t wstr2[30];

ofstream myfile;

myfile.open(_T("example.txt"), ios::out | ios::binary );

if (myfile.is_open())
{
// write it:
myfile.write("\xFF\xFE",2);
myfile.write((char*)wstrFarsi.c_str(), 10);

myfile.close();
}
else cout << "Cannot open file";

دقت کنید که:
- از fstream استفاده کردم و از wfstream استفاده نکردم تا به شکل باینری و بایتی بخوانم و بنویسم.
- در write اول 2 بایتی که نوشتم همان BOM هست که باعث می شود فایل txt شما به یونیکد تبدیل شود و در notepad و سایر سیستم عامل ها به شکل درست باز شود تا فارسی نمایش داده شود.
- در write دوم، رشته ما چون یونیکد هست، اما نوشتن و خواندن 1 بایتی هست، به شکل صریح آن را به char* تبدیل کردم.
- پس 5 کارکتر فارسی (10 بایت) و 2 بایت هم برای BOM هست.



و برای خواندن هم به همین شکل:




wchar_t wstr2[30];

fstream myfile2;
myfile2.open(_T("example.txt"), ios::in | ios::binary);

if (myfile2.is_open())
{
// read it:
myfile2.read( (char*)wstr2, 10+2); //12 byte = 1 BOM + 5 chars

// The end of farsi string
wstr2[6] = L'\0';
wstr2[6+1] = L'\0';

// convert to wstring if you need
wstring wstrFarsi2(wstr2);

myfile2.close();
}
else cout << "Cannot open file";

دقت کنید که:
- انتهای رشته را خودتان باید با 0\ پر کنید تا رشته درست بسته شود.
- در حالت باینری برای فهمیدن اینکه طول رشته شما چقدر هست، باید از توابع مورد نظر استفاده کنید که اندازه فایل را می دهند. همین طور می توانید با seekg به انتها بروید و ببینید چند بایت در فایل هست.
- 2 بایت اول BOM هست و بایت های بعدی رشته شما.

amin1softco
شنبه 15 اسفند 1388, 16:23 عصر
خوب من با استفاده از همون چیزی که شما نوشتی نیما جون یک رشته ام به فایل خروجی اضافه می کنم اینجوری



#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main ()
{
wchar_t ch;
wstringstream o;

wifstream infile;
wofstream outfile;

wfilebuf * inbuf;

infile.open("c:\\read-unicode.txt");
outfile.open("c:\\write-unicode.txt");

inbuf = infile.rdbuf();


ch = inbuf->sgetc();

while(ch != 0xffff)
{
o<<ch;
ch = inbuf->snextc();
}
wstring op=L"ایران";/*یک رشته دلخواه*/
o<<(char*)op.c_str();
wstring wstrIn=o.str();
wcout<<wstrIn;
outfile<<wstrIn;
infile.close();
outfile.close();

system("pause");

return 0;
}

Pari_Programmer
شنبه 15 اسفند 1388, 18:24 عصر
سلام به همه دوستان عزيز كه وقت گذاشتن رو مشكل بوجود اومده.
واقعا از صميم قلب از آقا نيما و آقا امين متشكرم و نميدونم چه جوري زحمتاشون رو جبران كنم.
من بالاخره موفق شدم اين مشكل رو حل كنم.
:متفکر: :لبخند: :لبخند: :لبخند: :لبخند: :لبخند: :لبخند: :متفکر:
اين هم از كدي كه در نهايت جواب داد و تونستم با استفاده از اون محتويات فايل رو بخونم:



#include <iostream>
#include <conio.h>
#include<string.h>
#include<fstream>
#include <sstream>
#include<wchar.h>

using namespace std;

int main( void )
{

char ch;
wchar_t c;
wstringstream o;
wstring wstrIn;
const char* pFile="test.txt";
FILE* in;
in=fopen (pFile,"rb");
if(!in)
{
cout<<"Error."<<endl;
exit(0);
}
int i=0;
int len=sizeof(wchar_t);
c=fgetwc(in);
while(c!=0xFFFF)
{
c=fgetwc(in);
o<<c;
}
wstrIn=o.str();

fclose(in);
getch();
}



يك نكته هم بگم در مورد كدي كه اقا امين زحمتش رو كشيدن. كد شما درست كار ميكنه ولي به شرطي كه نخواين رشته بدست اومده از فايل رو دستكاري كنيد. به عبارت ديگه من فكر ميكنم كه داره محتويات فايل رو بايت به بايت از فايل ميخونه و دوباره بايت به بايت توي يه فايل ديگه مينويسه. در صورتي كه كاراكترهاي ما هر كدوم 2 بايتي هستن. بنابراين جواب نميده.
اقا نيما با شرمندگي من اونقدر ذوق زده شدم كه ديگه كد شما رو تست نكردم. ولي حتما اون رو هم تست ميكنم و نتيجه رو بتون ميگم.
باز هم از همتون ممنونم.:قلب:
با اين حساب ديگه هيچ نيازي به كتابخونه جداگونه اي نداريم و با همون كتابخونه استاندارد C++‎ ميتونيم رشته هاي فارسي رو از فايل بخونيم.
خيلي خوشحالم.

دوستان من با اين سواد داغوني كه دارم مطمئنم كه تا چند روز ديگه دوباره به يه مشكل ديگه برمي خورم.و با عرض معذرت دوباره بايد مزاحمتون شم.
فعلا خدانگهدار.