我想将一些文件(大约1000个)拆分为单词并删除数字和标点符号。然后,我将相应地处理这些标记化的单词...但是,这些文件主要是的德语,并且以不同的类型编码:
我遇到的问题是我无法找到正确的方法来应用tolower()
std::cout
,当我使用Ubuntu linux
时,我也会在终端中获得一些奇怪的图标französische
。
例如,在非UTF-8文件中,单词franz�sische
显示为für
,f�r
为
Örebro
等... Österreich
忽略了tolower()
或"Unicode replacement character" � (U+FFFD)
等字词。据我所知,在尝试处理Unicode时,程序无法正确解码的任何字符都会插入Ö
。
当我打开UTF-8文件时,我没有得到任何奇怪的字符,但我仍然无法将大写字母特殊字符(如std::setlocale(LC_ALL, "de_DE.iso88591");
)转换为小写字母...我使用tolower()
和其他一些选项我在stackoverflow上找到了,但我仍然没有得到所需的输出。
我对如何解决这个问题的猜测是:
algorithm
等以上-locale -a
是否可行或复杂性是否会飙升?
这个问题的正确方法是什么?如何使用某种编码选项打开文件?
1。我的操作系统是否应该启用相应的区域设置作为全局变量来处理(不用打扰控制台如何显示它)文本?(例如在linux中我没有启用de_DE,当我使用LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=el_GR.UTF-8
LC_TIME=el_GR.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=el_GR.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=el_GR.UTF-8
LC_NAME=el_GR.UTF-8
LC_ADDRESS=el_GR.UTF-8
LC_TELEPHONE=el_GR.UTF-8
LC_MEASUREMENT=el_GR.UTF-8
LC_IDENTIFICATION=el_GR.UTF-8
LC_ALL=
C
C.UTF-8
el_GR.utf8
en_AG
en_AG.utf8
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IN
en_IN.utf8
en_NG
en_NG.utf8
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZM
en_ZM.utf8
en_ZW.utf8
POSIX
时)
2。此问题仅在终端默认编码时可见吗?在通常用c ++处理提取的字符串之前,是否需要采取任何进一步的步骤?
我的linux语言环境:
void processFiles() {
std::string filename = "17454-8.txt";
std::ifstream inFile;
inFile.open(filename);
if (!inFile) {
std::cerr << "Failed to open file" << std::endl;
exit(1);
}
//calculate file size
std::string s = "";
s.reserve(filesize(filename) + std::ifstream::pos_type(1));
std::string line;
while( (inFile.good()) && std::getline(inFile, line) ) {
s.append(line + "\n");
}
inFile.close();
std::cout << s << std::endl;
//remove punctuation, numbers, tolower,
//TODO encoding detection and specific transformation (cannot catch Ö, Ä etc) will add too much complexity...
std::setlocale(LC_ALL, "de_DE.iso88591");
for (unsigned int i = 0; i < s.length(); ++i) {
if (std::ispunct(s[i]) || std::isdigit(s[i]))
s[i] = ' ';
if (std::isupper(s[i]))
s[i]=std::tolower(s[i]);
}
//std::cout << s << std::endl;
//tokenize string
std::istringstream iss(s);
tokens.clear();
tokens = {std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};
for (auto & i : tokens)
std::cout << i << std::endl;
//PROCESS TOKENS
return;
}
以下是我编写的一些示例代码,它不像我想要的那样工作。
modal
答案 0 :(得分:2)
Unicode为字符定义“代码点”。 代码点是32位值。
有一些类型的编码。 ASCII仅使用7位,这给出了128个不同的字符。 Microsoft使用第8位来定义另外128个字符,具体取决于语言环境,并称为“代码页”。如今MS使用UTF-16 2字节编码。因为这对于整个Unicode集来说还不够,所以UTF-16也依赖于语言环境,其名称与Unicode的名称“Latin-1”或“ISO-8859-1”等相匹配。
在Linux(通常用于文件)中使用的最多是UTF-8,它为每个字符使用可变数量的字节。前128个字符与ASCII字符完全相同,每个字符只有一个字节。要表示字符,UTF8最多可以使用4个字节。 Wikipedia中的更多信息。
虽然MS对文件和RAM使用UTF-16,但Linux可能会将UFT-32用于RAM。
要读取文件,您需要知道其编码。试图发现它是一个真正的噩梦,可能不会成功。使用std::basic_ios::imbue可以为您的流设置所需的区域设置,例如this SO answer
tolower此类函数可以使用区域设置,例如
#include <iostream>
#include <locale>
int main() {
wchar_t s = L'\u00D6'; //latin capital 'o' with diaeresis, decimal 214
wchar_t sL = std::tolower(s, std::locale("en_US.UTF-8")); //hex= 00F6, dec= 246
std::cout << "s = " << s << std::endl;
std::cout << "sL= " << sL << std::endl;
return 0;
}
输出:
s = 214
sL= 246
在这个other SO answer中,您可以找到好的解决方案,例如使用iconv Linux或iconv W32库。
在Linux中,终端可以设置为在LC_ALL
,LANG
和LANGUAGE
的帮助下使用区域设置,例如:
//Deutsch
LC_ALL="de_DE.UTF-8"
LANG="de_DE.UTF-8"
LANGUAGE="de_DE:de:en_US:en"
//English
LC_ALL="en_US.UTF-8"
LANG="en_US.UTF-8"
LANGUAGE="en_US:en"