• 关于

    字符串操作会出现哪些问题

    的搜索结果

问题

深入分析字符编码之五-常见问题分析

夏天的日子 2019-12-01 21:13:23 4211 浏览量 回答数 0

回答

不依赖微软的库和WindowsAPI,没能试验成功!######问题已解决,谢谢。######看这篇文章,讲的很清楚(:)这是从其他地方拷贝过来的) UNICODE环境设置 在安装Visual Studio时,在选择VC++时需要加入unicode选项,保证相关的库文件可以拷贝到system32下。 UNICODE编译设置: C/C++, Preprocessor difinitions 去除_MBCS,加_UNICODE,UNICODE 在ProjectSetting/link/output 中设置Entry为wWinMainCRTStartup 反之为MBCS(ANSI)编译。 Unicode :宽字节字符集 1. 如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数? 可以调用Microsoft Visual C++的运行期库包含函数_mbslen来操作多字节(既包括单字节也包括双字节)字符串。 调用strlen函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。 size_t strlen( const char *string ); size_t wcslen( const wchar_t *string ); size_t _mbslen( const unsigned char *string ); size_t _mbstrlen( const char *string ); 2. 如何对DBCS(双字节字符集)字符串进行操作? 函数 描述 PTSTR CharNext ( LPCTSTR ); 返回字符串中下一个字符的地址 PTSTR CharPrev ( LPCTSTR, LPCTSTR ); 返回字符串中上一个字符的地址 BOOL IsDBCSLeadByte( BYTE ); 如果该字节是DBCS字符的第一个字节,则返回非0值 3. 为什幺要使用Unicode? (1) 可以很容易地在不同语言之间进行数据交换。 (2) 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。 (3) 提高应用程序的运行效率。 Windows 2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那幺系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序更加有效地运行。 Windows CE 本身就是使用Unicode的一种操作系统,完全不支持ANSI Windows函数 Windows 98 只支持ANSI,只能为ANSI开发应用程序。 Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。 4. 如何编写Unicode源代码?   Microsoft公司为Unicode设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。   _UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常必须同时定义这两个宏。     5. Windows定义的Unicode数据类型有哪些?   数据类型 说明   WCHAR Unicode字符   PWSTR 指向Unicode字符串的指针   PCWSTR 指向一个恒定的Unicode字符串的指针   对应的ANSI数据类型为CHAR,LPSTR和LPCSTR。   ANSI/Unicode通用数据类型为TCHAR,PTSTR,LPCTSTR。     6. 如何对Unicode进行操作?   字符集 特性 实例   ANSI 操作函数以str开头 strcpy   Unicode 操作函数以wcs开头 wcscpy   MBCS 操作函数以_mbs开头 _mbscpy   ANSI/Unicode 操作函数以_tcs开头 _tcscpy(C运行期库)   ANSI/Unicode 操作函数以lstr开头 lstrcpy(Windows函数)   所有新的和未过时的函数在Windows2000中都同时拥有ANSI和Unicode两个版本。ANSI版本函数结尾以A表示;Unicode版本函数结尾以W表示。Windows会如下定义:   #ifdef UNICODE   #define CreateWindowEx CreateWindowExW   #else   #define CreateWindowEx CreateWindowExA   #endif // !UNICODE     7. 如何表示Unicode字符串常量?   字符集 实例   ANSI “string”   Unicode L“string”   ANSI/Unicode T(“string”)或_TEXT(“string”)   if( szError[0] == _TEXT(‘J’) ){ }     8. 为什幺应当尽量使用操作系统函数?   这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。   如:StrCat,StrChr,StrCmp和StrCpy等。     9. 如何编写符合ANSI和Unicode的应用程序?   (1) 将文本串视为字符数组,而不是chars数组或字节数组。   (2) 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。   (3) 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。   (4) 将TEXT宏用于原义字符和字符串。   (5) 执行全局性替换(例如用PTSTR替换PSTR)。   (6) 修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那幺请记住要按字节来分配内存。这就是说,应该调用   malloc(nCharacters *sizeof(TCHAR)),而不是调用malloc(nCharacters)。     10. 如何对字符串进行有选择的比较?   通过调用CompareString来实现。   int CompareString(    LCID Locale, // locale identifier DWORD dwCmpFlags, // comparison-style options LPCTSTR lpString1, // pointer to first string int cchCount1, // size, in bytes or characters, of first string LPCTSTR lpString2, // pointer to second string int cchCount2 // size, in bytes or characters, of second string   ); Locale 本地比较的定义    LOCALE_USER_DEFAULT    LOCALE_SYSTEM_DEFAULT     标志 含义   NORM_IGNORECASE 忽略字母的大小写   NORM_IGNOREKANATYPE 不区分平假名与片假名字符   NORM_IGNORENONSPACE 忽略无间隔字符   NORM_IGNORESYMBOLS 忽略符号   NORM_IGNOREWIDTH 不区分单字节字符与作为双字节字符的同一个字符   SORT_STRINGSORT 将标点符号作为普通符号来处理     11. 如何判断一个文本文件是ANSI还是Unicode?   判断如果文本文件的开头两个字节是0xFF和0xFE,那幺就是Unicode,否则是ANSI。     12. 如何判断一段字符串是ANSI还是Unicode?   用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。     13. 如何在Unicode与ANSI之间转换字符串?   Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。     14. Unicode和DBCS之间的区别    Unicode使用(特别在C程序设计语言环境里)“宽字符集”。「Unicode中的每个字符都是16位宽而不是8位宽。」在Unicode中,没有单单使用8位数值的意义存在。相比之下,在“双位组字符集”中我们仍然处理8位数值。有些位组自身定义字符,而某些位组则显示需要和另一个位组共同定义一个字符。     处理DBCS字符串非常杂乱,但是处理Unicode文字则像处理有秩序的文字。您也许会高兴地知道前128个Unicode字符(16位代码从0x0000到0x007F)就是ASCII字符,而接下来的128个Unicode字符(代码从0x0080到0x00FF)是ISO 8859-1对ASCII的扩展。Unicode中不同部分的字符都同样基于现有的标准。这是为了便于转换。希腊字母表使用从0x0370到0x03FF的代码,斯拉夫语使用从0x0400到0x04FF的代码,美国使用从0x0530到0x058F的代码,希伯来语使用从0x0590到0x05FF的代码。中国、日本和韩国的象形文字(总称为CJK)占用了从0x3000到0x9FFF的代码。Unicode的最大好处是这里只有一个字符集,没有一点含糊。         15.衍生标准     Unicode是一个标准。UTF-8是其概念上的子集,UTF-8是具体的编码标准。而UNICODE是所有想达到世界统一编码标准的标准。UTF-8标准就是Unicode(ISO10646)标准的一种变形方式,      UTF的全称是:Unicode/UCS Transformation Format,其实有两种UTF,一种是UTF-8,一种是UTF-16,      不过UTF-16使用较少,其对应关系如下:      在Unicode中编码为 0000 - 007F 的 UTF-8 中编码形式为: 0xxxxxxx      在Unicode中编码为 0080 - 07FF 的 UTF-8 中编码形式为: 110xxxxx 10xxxxxx      在Unicode中编码为 0000 - 007F 的 UTF-8 中编码形式为: 1110xxxx 10xxxxxx 10xxxxxx           utf-8是unicode的一个新的编码标准,其实unicode有过好几个标准.我们知道一直以来使用的unicode字符内码都是16位,它实际上还不能把全世界的所有字符编在一个平面系统,比如中国的藏文等小语种,所以utf-8扩展到了32位,也就是说理论在utf-8中可容纳二的三十二次方个字符. UNICODE的思想就是想把所有的字符统一编码,实现一个统一的标准.big5、gb都是独立的字符集,这也叫做远东字符集,把它拿到德文版的WINDOWS上可能将会引起字符编码的冲突....早期的WINDOWS默认的字符集是ANSI.notepad中输入的汉字是本地编码,但在NT/2000内部是可以直接支持UNICODE的。notepad.exe在WIN95和98中都是ANSI字符,在NT中则是UNICODE.ANSI和UNICODE可以方便的实现对应映射,也就是转换 ASCII是8位范围内的字符集,对于范围之外的字符如汉字它是无法表达的。unicode是16位范围内的字符集,对于不同地区的字符分区分配,unicode是多个IT巨头共同制定的字符编码标准。如果在unicode环境下比如WINDOWS NT上,一个字符占两字节16位,而在ANSI环境下如WINDOWS98下一个字符占一个字节8位.Unicode字符是16位宽,最多允许65,535字符,数据类型被称为WCHAR。       对于已有的ANSI字符,unicode简单的将其扩展为16位:比如ANSI"A"=0x43,则对应的UNICODE为       "A"= 0x0043        而ASCII用七存放128个字符,ASCII是一个真正的美国标准,所以它不能满足其他国家的需要,例如斯拉夫语的字母和汉字于是出现了Windows ANSI字符集,是一种扩展的ASCII码,用8位存放字符,低128位仍然存放原来的ASCII码,        而高128位加入了希腊字母等        if def UNICODE        TCHAR = wchar        else        TCHAR = char        你需要在Project\Settings\C/C++\Preprocesser definitions中添加UNICODE和_UNICODE        UINCODE,_UNICODE都要定义。不定义_UNICODE的话,用SetText(HWND,LPCTSTR),将被解释为SetTextA(HWND,LPTSTR),这时API将把你给的Unicode字符串看作ANSI字符串,显示乱码。因为windows API是已经编译好存在于dll中的,由于不管UNICODE还是ANSI字符串,都被看作一段buffer,如"0B A3 00 35 24 3C 00 00"如果按ANSI读,因为ANSI字串是以'\0'结束的,所以只能读到两字节"0B A3 \0",如果按UNICODE读,将完整的读到'\0\0'结束。         由于UNICODE没有额外的指示位,所以系统必须知道你提供的字串是哪种格式。此外,UNICODE好象是ANSI C++规定的,_UNICODE是windows SDK提供的。如果不编写windows程序,可以只定义UNICODE。 开发过程:         围绕着文件读写、字符串处理展开。文件主要有两种:.txt和.ini文件        在unicode和非unicode环境下字符串做不同处理的,那么需要参考以上9,10两条,以适应不同环境得字符串处理要求。         对文件读写也一样。只要调用相关接口函数时,参数中的字符串前都加上_TEXT等相关宏。如果写成的那个文件需要是unicode格式保存的,那么在创建文件时需要加入一个字节头。          CFile file;           WCHAR szwBuffer[128];           WCHAR *pszUnicode = L"Unicode string\n"; // unicode string           CHAR pszAnsi = "Ansi string\n"; // ansi string           WORD wSignature = 0xFEFF;           file.Open(TEXT("Test.txt"), CFile::modeCreate|CFile::modeWrite);           file.Write(&wSignature, 2);           file.Write(pszUnicode, lstrlenW(pszUnicode) * sizeof(WCHAR));           // explicitly use lstrlenW function           MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, szwBuffer, 128);           file.Write(szwBuffer, lstrlenW(szwBuffer) * sizeof(WCHAR));            file.Close();            //以上这段代码在unicode和非unicode环境下都有效。这里显式的指明用Unicode来进行操作。           在非unicode环境下,缺省调用的都是ANSI格式的字符串,此时TCHAR转换为CHAR类型的,除非显式定义WCHAR。所以在这个环境下,如果读取unicode文件,那么首先需要移动2个字节,然后读取得字符串需要用MultiByteToWideChar来转换,转换后字符串信息才代表unicode数据。          在unicode环境下,缺省调用得都是unicode格式得字符串,也就是宽字符,此时TCHAR转换为WCHAR,相关得API函数也都调用宽字符类型的函数。此时读取unicode文件也和上面一样,但是读取得数据是WCHAR的,如果要转换成ANSI格式,需要调用WideCharToMultiByte。如果读取ANSI的,则不用移动两个字节,直接读取然后视需要转换即可。                    某些语言(如韩语)必须在unicode环境下才能显示,这种情况下,在非unicode环境下开发,就算用字符串函数转换也不能达到显示文字的目的,因为此时调用得API函数是用ANSI的(虽然底层都是用UNICODE处理但是处理结果是按照程序员调用的API来显示的)。所以必须用unicode来开发。###### 用WideCharToMultiByte这个API: #include <stdio.h> #include <windows.h> int main() { FILE fp; wchar_t utf[1000], *p = utf; char ansi[2000]; fp = _wfopen(L"C:\1.txt", L"rb"); while(!feof(fp)) fread(p++, 1, 2, fp); *--p = L'\0'; fclose(fp); // utf+1剔除UTf-16标记 WideCharToMultiByte(CP_ACP, 0, utf + 1, -1, ansi, sizeof(ansi), NULL, NULL); puts(ansi); } ###### 楼上的给个链接就好,不用大篇幅复制。 卤煮的意思是说把“\u6C49\u5B57” 这个ASCII字符串转成两个汉字对吧~ ######不用别人的库,查unicode编码表?lz解决了说说方法呀######C++没解决,后来这个模块改用C#写了。###### 按二进制读,先读出0xFF 0xFE,后面数据的两个字节表示一个字,自己想办法读到wstring中 显示,用API的话,一个wcstombs ,一个WideCharToMultiByte 不用API的话自己查表,嵌入式程序可以查表,x86程序完全没那个必要 ###### 干嘛不用std::wstring ###### 用std::wstring吧,自己没有试过……,你可以去尝试下

kun坤 2020-06-07 13:49:51 0 浏览量 回答数 0

问题

【精品问答】Python实战100例 - 附源码

珍宝珠 2019-12-01 22:01:18 7689 浏览量 回答数 8

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

Java基础

游客pklijor6gytpx 2019-12-01 22:02:53 69 浏览量 回答数 1

问题

【精品问答】前端开发必懂之JS技术二百问

茶什i 2019-12-01 22:05:04 146 浏览量 回答数 0

问题

【精品问答】python技术1000问(1)

问问小秘 2019-12-01 21:57:48 454222 浏览量 回答数 19

问题

搜索引擎背后的经典数据结构和算法 6月10日 【今日算法】

游客ih62co2qqq5ww 2020-06-15 07:32:11 0 浏览量 回答数 0

回答

Java基本类型及其包装有哪些? Java基本类型-整数类型byte、short、int和long byte:字节型;short:短整型;int:整型;long:长整型。 刚开始学习时有种疑惑,为啥弄这么多整数类型? 从字节型到长整型,整数的范围总是在不断增加,不够用了自然就要扩展范围,添加新的类型;若范围小的整数浪费空间太多,又会影响程序,所以在使用时就有了类型供我们选择。 Java基本类型-- 浮点型float和double https://developer.aliyun.com/ask/280806 Java基本类型-字符型char Java采用的是Unicode编码格式,字符类型的变量不仅可以接收单字符常量,还可以接收0~65535之间的整数。 转义字符表(有些特殊含义的字符需要通过\来协助) Java基本类型-布尔型boolean Java中的布尔型只表示字面true和false,不同C++之处,如果将一个整数值赋值给布尔类型变量,编译器将报错。 Java基本类型- Java常量默认类型有哪些? 在Java中整型常量默认int类型,如果想要使用long类型常量,需要在常量后添加L/l字符,例如:15L 浮点数默认为double类型,如果想要float类型需要添加F/f,例如:3.14F Java基本类型-- 基本类型的范围包括哪些? 我们可以利用基本类型的包装类中的关键字MAX_VALUE和MIN_VALUE,打印出基本类型的范围。 public class PrintRange { public static void main(String[] args) { System.out.println("byte: "+Byte.MIN_VALUE+" ~ "+Byte.MAX_VALUE); System.out.println("short: "+Short.MIN_VALUE+" ~ "+Short.MAX_VALUE); System.out.println("int: "+Integer.MIN_VALUE+" ~ "+Integer.MAX_VALUE); System.out.println("long: "+Long.MIN_VALUE+" ~ "+Long.MAX_VALUE); System.out.println("float: "+Float.MIN_VALUE+" ~ "+Float.MAX_VALUE); System.out.println("double: "+Double.9+" ~ "+Double.MAX_VALUE); System.out.println("char: "+Character.MIN_VALUE+" ~ "+Character.MAX_VALUE); } } Java基本类型-基本类型间的相互转换- 隐式转换、显式转化 隐式转换 范围小的类型可以自动转换为范围大的类型,大范围类型能容纳小范围类型(也称显示转换为扩展转换)这些转换都是兼容的。 显式转换 大的范围转换为小范围类型,会造成信息丢失(也称隐式转换为窄化转换),不兼容性转换。如图,利用类型转换操作符(cast)进行显示转换。 浮点数转换为整数的小数问题 float和double类型转换为整数时,总是舍去小数点后面的部分,称此行为为截尾。 如果对小数点后面的部分进行四舍五入,即舍入操作,需要利用java.lang.Math中的round()方法 Java基本类型-基本类型和包装类之间的转换- 自动装箱和拆箱 Java5出现的特性,基本类型和封装类之间可以进行自动拆箱和装箱进行类型转换(由基类转换为包装类)。 ** 包装类中还包含很多将包装类装换为其他基类的接口,详细的可以查看Java API文档** java基本类型-- 基本类型和String类型之间的转换 在我们进行Java GUI界面开发时,通常会获取文本输入框的值,这就可能涉及到String类型转换为基本类型的问题。 public class PrintRange { public static void main(String[] args) { //基类转换为String类型 int a = 125; String s = String.valueOf(a); //String类型转换为基类 a = Integer.parseInt(s); } } String转换为基类利用的是基类对应包装类的parse···方法,基类转换字符串用的是String类中的valueOf方法,这些在Java API中都有介绍。 Java基本类型作为方法参数传递中的转换

问问小秘 2020-03-30 14:41:23 0 浏览量 回答数 0

回答

类型转换的规则有哪些? 在if语句、逻辑语句、数学运算逻辑、==等情况下都可能出现隐⼠类型转换。 类型转换的原理是什么? 类型转换指的是将⼀种类型转换为另⼀种类型,例如: var b = 2; var a = String(b); console.log(typeof a); //string 当然,类型转换分为显式和隐式,但是不管是隐式转换还是显式转换,都会遵循⼀定的原理,由于JavaScript是⼀⻔动态类型的语⾔,可以随时赋予任意值,但是各种运算符或条件判断中是需要特定类型的,因此JavaScript引擎会在运算时为变量设定类型. 这看起来很美好,JavaScript引擎帮我们搞定了 类型 的问题,但是引擎毕竟不是ASI(超级⼈⼯智能),它的很多动作会跟我们预期相去甚远,我们可以从⼀到⾯试题开始. {}+[] //0 答案是0 是什么原因造成了上述结果呢?那么我们得从ECMA-262中提到的转换规则和抽象操作说起,有兴趣的童鞋可以仔细阅读下这浩如烟海的语⾔规范,如果没这个耐⼼还是往下看. 这是JavaScript种类型转换可以从原始类型转为引⽤类型,同样可以将引⽤类型转为原始类型,转为原始类型的抽象操作为 ToPrimitive ,⽽后续更加细分的操作为: ToNumber ToString ToBoolean 。 为了更深⼊的探究JavaScript引擎是如何处理代码中类型转换问题的,就需要看 ECMA-262详细的规范,从⽽探究其内部原理,我们从这段内部原理示意代码开始. // ECMA-262, section 9.1, page 30. Use null/undefined for no hint, // (1) for number hint, and (2) for string hint. function ToPrimitive(x, hint) { // Fast case check. if (IS_STRING(x)) return x; // Normal behavior. if (!IS_SPEC_OBJECT(x)) return x; if (IS_SYMBOL_WRAPPER(x)) throw MakeTypeError(kSymbolToPrimitive); if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT; return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x); } // ECMA-262, section 8.6.2.6, page 28. function DefaultNumber(x) { if (!IS_SYMBOL_WRAPPER(x)) { var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = %_CallFunction(x, valueOf); if (IsPrimitive(v)) return v; } var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = %_CallFunction(x, toString); if (IsPrimitive(s)) return s; } } throw MakeTypeError(kCannotConvertToPrimitive); } // ECMA-262, section 8.6.2.6, page 28. function DefaultString(x) { if (!IS_SYMBOL_WRAPPER(x)) { var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = %_CallFunction(x, toString); if (IsPrimitive(s)) return s; } var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = %_CallFunction(x, valueOf); if (IsPrimitive(v)) return v; } } throw MakeTypeError(kCannotConvertToPrimitive); } 上⾯代码的逻辑是这样的: 如果变量为字符串,直接返回.如果 !IS_SPEC_OBJECT(x) ,直接返回.如果 IS_SYMBOL_WRAPPER(x) ,则抛出异常.否则会根据传⼊的 hint 来调⽤ DefaultNumber 和 DefaultString ,⽐如如果为 Date 对象,会调DefaultString .DefaultNumber :⾸ 先x.valueOf ,如果为 primitive ,则返回 valueOf 后的值,否则继续调⽤ x.toString,如果为 primitive ,则返回 toString 后的值,否则抛出异常DefaultString :和 DefaultNumber 正好相反,先调⽤ toString ,如果不是 primitive 再调⽤ valueOf . 那讲了实现原理,这个 ToPrimitive 有什么⽤呢?实际很多操作会调⽤ ToPrimitive ,⽐如加、相等或⽐较操。在进⾏加操作时会将左右操作数转换为 primitive ,然后进⾏相加。 下⾯来个实例,({}) + 1(将{}放在括号中是为了内核将其认为⼀个代码块)会输出啥?可能⽇常写代码并不会这样写,不过⽹上出过类似的⾯试题。 加操作只有左右运算符同时为 String或Number 时会执⾏对应的 %_StringAdd或%NumberAdd ,下⾯看下 ({}) + 1 内部会经过哪些步骤: {} 和 1 ⾸先会调⽤ToPrimitive {} 会⾛到 DefaultNumber ,⾸先会调⽤ valueOf ,返回的是 Object {} ,不是primitive类型,从⽽继续⾛到 toString ,返回 [object Object] ,是 String 类型 最后加操作,结果为 [object Object]1 再⽐如有⼈问你 [] + 1 输出啥时,你可能知道应该怎么去计算了,先对 [] 调⽤ ToPrimitive ,返回空字符串,最后结果为"1"。

前端问答 2019-12-19 07:36:28 0 浏览量 回答数 0

回答

1 js 的基本数据类型? 2 JavaScript 有几种类型的值? 3 什么是堆?什么是栈?它们之间有什么区别和联系? 4 内部属性 [Class] 是什么? 5 介绍 js 有哪些内置对象? 6 undefined 与 undeclared 的区别? 7 null 和 undefined 的区别? 8 如何获取安全的 undefined 值? 9 说几条写 JavaScript 的基本规范? 10 JavaScript 原型,原型链? 有什么特点? 11 js 获取原型的方法? 12 在 js 中不同进制数字的表示方式? 13 js 中整数的安全范围是多少? 14 typeof NaN 的结果是什么? 15 isNaN 和 Number.isNaN 函数的区别? 16 Array 构造函数只有一个参数值时的表现? 17 其他值到字符串的转换规则? 18 其他值到数字值的转换规则? 19 其他值到布尔类型的值的转换规则? 20 {} 和 [] 的 valueOf 和 toString 的结果是什么? 21 什么是假值对象? 22 ~ 操作符的作用? 23 解析字符串中的数字和将字符串强制类型转换为数字的返回结果都是数字,它们之间的区别是什么? 24 + 操作符什么时候用于字符串的拼接? 25 什么情况下会发生布尔值的隐式强制类型转换? 26 || 和 && 操作符的返回值? 27 Symbol 值的强制类型转换? 28 == 操作符的强制类型转换规则? 29 如何将字符串转化为数字,例如 '12.3b'? 30 如何将浮点数点左边的数每三位添加一个逗号,如 12000000.11 转化为『12,000,000.11』? 31 常用正则表达式? 32 生成随机数的各种方法? 33 如何实现数组的随机排序? 34 javascript 创建对象的几种方式? 35 JavaScript 继承的几种实现方式? 36 寄生式组合继承的实现? 37 Javascript 的作用域链? 38 谈谈 This 对象的理解。 39 eval 是做什么的? 40 什么是 DOM 和 BOM? 41 写一个通用的事件侦听器函数。 42 事件是什么?IE 与火狐的事件机制有什么区别? 如何阻止冒泡? 43 三种事件模型是什么? 44 事件委托是什么? 45 ['1', '2', '3'].map(parseInt) 答案是多少? 46 什么是闭包,为什么要用它? 47 javascript 代码中的 'use strict'; 是什么意思 ? 使用它区别是什么? 48 如何判断一个对象是否属于某个类? 49 instanceof 的作用? 50 new 操作符具体干了什么呢?如何实现? 51 Javascript 中,有一个函数,执行时对象查找时,永远不会去查找原型,这个函数是? 52 对于 JSON 的了解? 53 [].forEach.call($$(''),function(a){a.style.outline='1px solid #'+(~~(Math.random()(1<<24))).toString(16)}) 能解释一下这段代码的意思吗? 54 js 延迟加载的方式有哪些? 55 Ajax 是什么? 如何创建一个 Ajax? 56 谈一谈浏览器的缓存机制? 57 Ajax 解决浏览器缓存问题? 58 同步和异步的区别? 59 什么是浏览器的同源政策? 60 如何解决跨域问题? 61 服务器代理转发时,该如何处理 cookie? 62 简单谈一下 cookie ? 63 模块化开发怎么做? 64 js 的几种模块规范? 65 AMD 和 CMD 规范的区别? 66 ES6 模块与 CommonJS 模块、AMD、CMD 的差异。 67 requireJS 的核心原理是什么?(如何动态加载的?如何避免多次加载的?如何 缓存的?) 68 JS 模块加载器的轮子怎么造,也就是如何实现一个模块加载器? 69 ECMAScript6 怎么写 class,为什么会出现 class 这种东西? 70 documen.write 和 innerHTML 的区别? 71 DOM 操作——怎样添加、移除、移动、复制、创建和查找节点? 72 innerHTML 与 outerHTML 的区别? 73 .call() 和 .apply() 的区别? 74 JavaScript 类数组对象的定义? 75 数组和对象有哪些原生方法,列举一下? 76 数组的 fill 方法? 77 [,,,] 的长度? 78 JavaScript 中的作用域与变量声明提升? 79 如何编写高性能的 Javascript ? 80 简单介绍一下 V8 引擎的垃圾回收机制 81 哪些操作会造成内存泄漏? 82 需求:实现一个页面操作不会整页刷新的网站,并且能在浏览器前进、后退时正确响应。给出你的技术实现方案? 83 如何判断当前脚本运行在浏览器还是 node 环境中?(阿里) 84 把 script 标签放在页面的最底部的 body 封闭之前和封闭之后有什么区别?浏览器会如何解析它们? 85 移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时? 86 什么是“前端路由”?什么时候适合使用“前端路由”?“前端路由”有哪些优点和缺点? 87 如何测试前端代码么? 知道 BDD, TDD, Unit Test 么? 知道怎么测试你的前端工程么(mocha, sinon, jasmin, qUnit..)? 88 检测浏览器版本版本有哪些方式? 89 什么是 Polyfill ? 90 使用 JS 实现获取文件扩展名? 91 介绍一下 js 的节流与防抖? 92 Object.is() 与原来的比较操作符 '==='、'==' 的区别? 93 escape,encodeURI,encodeURIComponent 有什么区别? 94 Unicode 和 UTF-8 之间的关系? 95 js 的事件循环是什么? 96 js 中的深浅拷贝实现? 97 手写 call、apply 及 bind 函数 98 函数柯里化的实现 99 99. 为什么 0.1 + 0.2 != 0.3?如何解决这个问题? 100 原码、反码和补码的介绍 101 toPrecision 和 toFixed 和 Math.round 的区别? 102 什么是 XSS 攻击?如何防范 XSS 攻击? 103 什么是 CSP? 104 什么是 CSRF 攻击?如何防范 CSRF 攻击? 105 什么是 Samesite Cookie 属性? 106 什么是点击劫持?如何防范点击劫持? 107 SQL 注入攻击? 108 什么是 MVVM?比之 MVC 有什么区别?什么又是 MVP ? 109 vue 双向数据绑定原理? 110 Object.defineProperty 介绍? 111 使用 Object.defineProperty() 来进行数据劫持有什么缺点? 112 什么是 Virtual DOM?为什么 Virtual DOM 比原生 DOM 快? 113 如何比较两个 DOM 树的差异? 114 什么是 requestAnimationFrame ? 115 谈谈你对 webpack 的看法 116 offsetWidth/offsetHeight,clientWidth/clientHeight 与 scrollWidth/scrollHeight 的区别? 117 谈一谈你理解的函数式编程? 118 异步编程的实现方式? 119 Js 动画与 CSS 动画区别及相应实现 120 get 请求传参长度的误区 121 URL 和 URI 的区别? 122 get 和 post 请求在缓存方面的区别 123 图片的懒加载和预加载 124 mouseover 和 mouseenter 的区别? 125 js 拖拽功能的实现 126 为什么使用 setTimeout 实现 setInterval?怎么模拟? 127 let 和 const 的注意点? 128 什么是 rest 参数? 129 什么是尾调用,使用尾调用有什么好处? 130 Symbol 类型的注意点? 131 Set 和 WeakSet 结构? 132 Map 和 WeakMap 结构? 133 什么是 Proxy ? 134 Reflect 对象创建目的? 135 require 模块引入的查找方式? 136 什么是 Promise 对象,什么是 Promises/A+ 规范? 137 手写一个 Promise 138 如何检测浏览器所支持的最小字体大小? 139 怎么做 JS 代码 Error 统计? 140 单例模式模式是什么? 141 策略模式是什么? 142 代理模式是什么? 143 中介者模式是什么? 144 适配器模式是什么? 145 观察者模式和发布订阅模式有什么不同? 146 Vue 的生命周期是什么? 147 Vue 的各个生命阶段是什么? 148 Vue 组件间的参数传递方式? 149 computed 和 watch 的差异? 150 vue-router 中的导航钩子函数 151 两个router 的区别? 152 vue 常用的修饰符? 153 computed 和 watch 区别? 154 keep-alive 组件有什么作用? 155 vue 中 mixin 和 mixins 区别? 156 开发中常用的几种 Content-Type ? 157 如何封装一个 javascript 的类型判断函数? 158 如何判断一个对象是否为空对象? 159 使用闭包实现每隔一秒打印 1,2,3,4 160 手写一个 jsonp 161 手写一个观察者模式? 162 EventEmitter 实现 163 一道常被人轻视的前端 JS 面试题 164 如何确定页面的可用性时间,什么是 Performance API? 165 js 中的命名规则 166 js 语句末尾分号是否可以省略? 167 Object.assign() 168 Math.ceil 和 Math.floor 169 js for 循环注意点 170 一个列表,假设有 100000 个数据,这个该怎么办? 171 js 中倒计时的纠偏实现? 172 进程间通信的方式? 173 如何查找一篇英文文章中出现频率最高的单词? 174 174道 JavaScript 面试题,合集

剑曼红尘 2020-04-02 14:05:35 0 浏览量 回答数 0

回答

1.字符串转义序列转义字符 描述(在行尾时) 续行符\ 反斜杠符号' 单引号" 双引号a 响铃b 退格(Backspace)e 转义000 空n 换行v 纵向制表符t 横向制表符r 回车f 换页oyy 八进制数yy代表的字符,例如:o12代表换行xyy 十进制数yy代表的字符,例如:x0a代表换行other 其它的字符以普通格式输出 2.字符串格式化 3.操作符 一、算术运算符 注意: 双斜杠 // 除法总是向下取整。 从符点数到整数的转换可能会舍入也可能截断,建议使用math.floor()和math.ceil()明确定义的转换。 Python定义pow(0, 0)和0 ** 0等于1。 二、比较运算符 运算符 描述< 小于<= 小于或等于 大于= 大于或等于== 等于 != 不等于is 判断两个标识符是不是引用自一个对象is not 判断两个标识符是不是引用自不同对象注意: 八个比较运算符优先级相同。 Python允许x < y <= z这样的链式比较,它相当于x < y and y <= z。 复数不能进行大小比较,只能比较是否相等。 三、逻辑运算符 运算符 描述 备注x or y if x is false, then y, elsex x andy if x is false, then x, elsey not x if x is false, then True,elseFalse 注意: or是个短路运算符,它只有在第一个运算数为False时才会计算第二个运算数的值。 and也是个短路运算符,它只有在第一个运算数为True时才会计算第二个运算数的值。 not的优先级比其他类型的运算符低,所以not a == b相当于not (a == b),而 a == not b是错误的。 四、位运算符 运算符 描述 备注x | y 按位或运算符 x ^ y 按位异或运算符 x & y 按位与运算符 x << n 左移动运算符 x >> n 右移动运算符 ~x 按位取反运算符 五、赋值运算符 复合赋值运算符与算术运算符是一一对应的: 六、成员运算符 Python提供了成员运算符,测试一个元素是否在一个序列(Sequence)中。 运算符 描述in 如果在指定的序列中找到值返回True,否则返回False。not in 如果在指定的序列中没有找到值返回True,否则返回False。 4.关键字总结 Python中的关键字包括如下: and del from not while as elif global or with assert else if pass yield break except import print class exec in raise continue finally is return def for lambda try你想看看有哪些关键字?OK,打开一个终端,就像这样~ long@zhouyl:~$ pythonPython 2.7.3 (default, Jan 2 2013, 16:53:07) [GCC 4.7.2] on linux2Type "help", "copyright", "credits" or "license" for more information. import keywordkeyword.kwlist ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] ============================== 华丽的 正文分隔符 ======================================== 看到这些关键字你还能记得多少?你不妨自己一个一个对照想想它的用法,下面是我总结的,我根据前面的学习笔记将上述关键字分为以下几类: 1.判断、循环 对于Python的循环及判断主要包括这些关键字: if elif else for while break continue and or is not in 这几个关键字在前面介绍 if 语法、while语法、for语法以及and...or语法中已有介绍,下面再一笔带过: 1.1 if 语法 if语法与C语言、shell脚本之下的非常类似,最大的区别就是冒号以及严格的缩进,当然这两点也是Python区别于其他语言的地方: if condition1: do something elif condition2: do another thing else: also do something 1.2 while 语法 Python的while语法区别于C、shell下的while除了冒号及缩进之外,还有一点就是while可以携带一个可选的else语句: while condition: do something else: do something 注:else语句是可选的,但是使用while语句时一定要注意判断语句可以跳出! 1.3 for 语法 与while类似,Python的for循环也包括一个可选的else语句(跳出for循环时执行,但是如果是从break语句跳出则不执行else语句块中的代码!),而且for 加上 关键字in就组成了最常见的列表解析用法(以后会写个专门的博客)。 下面是for的一般用法: for i in range(1,10,2): do something if condition: break else: do something for的列表解析用法: for items in list: print items 1.4 and...or 语法 Python的and/or操作与其他语言不同的是它的返回值是参与判断的两个值之一,所以我们可以通过这个特性来实现Python下的 a ? b : c ! 有C语言基础的知道 “ a ? b : c ! ” 语法是判断 a,如果正确则执行b,否则执行 c! 而Python下我们可以这么用:“ a and b or c ”(此方法中必须保证b必须是True值),python自左向右执行此句,先判断a and b :如果a是True值,a and b语句仍需要执行b,而此时b是True值!所以a and b的值是b,而此时a and b or c就变成了b or c,因b是True值,所以b or c的结果也是b;如果a是False值,a and b语句的结果就是a,此时 a and b or c就转化为a or c,因为此时a是 False值,所以不管c是True 还是Flase,a or c的结果就是c!!!捋通逻辑的话,a and b or c 是不是就是Python下的a ? b : c ! 用法? 1.5 is ,not is 和 is not 是Python下判断同一性的关键字,通常用来判断 是 True 、False或者None(Python下的NULL)! 比如 if alue is True : ... (不记得本节的童鞋罚复习:python 学习笔记 2 -- 判断语句) 2.函数、模块、类 对于Python的函数及模块主要包括这些关键字: from import as def pass lambda return class 那么你还能记得它们么?下面简单介绍一下: 2.1 模块 Python的编程通常大量使用标准库中的模块,使用方法就是使用import 、from以及as 关键字。 比如: import sys # 导入sys模块 from sys import argv # 从sys模块中导入argv ,这个在前面介绍脚本传参数时使用到 import cPickle as p # 将cPickle模块导入并在此将它简单命名为p,此后直接可以使用p替代cPickle模块原名,这个在介绍文件输入输出时的存储器中使用到 2.2 函数 Python中定义函数时使用到def关键字,如果你当前不想写入真实的函数操作,可以使用pass关键字指代不做任何操作: def JustAFunction: pass 当然,在需要给函数返回值时就用到了return关键字,这里简单提一下Python下的函数返回值可以是多个(接收返回值时用相应数量的变量接收!)! 此外Python下有个神奇的Lambda函数,它允许你定义单行的最小函数,这是从Lisp中借用来的,可以用在任何需要函数的地方。比如: g = lambda x : x*2 # 定义一个Lambda函数用来计算参数的2倍并返回! print g(2) # 使用时使用lambda函数返回的变量作为这个函数的函数名,括号中带入相应参数即可! (不记得本节的童鞋罚复习:python 学习笔记 4 -- 函数篇) 3.异常 对于Python的异常主要包括这些关键字: try except finally raise 异常这一节还是比较简单的,将可能出现的异常放在 try: 后面的语句块中,使用except关键字捕获一定的异常并在接下来的语句块中做相应操作,而finally中接的是无论出现什么异常总在执行最后做finally: 后面的语句块(比如关闭文件等必要的操作!) raise关键字是在一定的情况下引发异常,通常结合自定义的异常类型使用。 (不记得本节的童鞋罚复习:python 学习笔记 6 -- 异常处理) 4.其他 上面的三类过后,还剩下这些关键字: print del global with assert yield exec 首先print 在前面的笔记或者任何地方你都能见到,所以还是比较熟悉的,此处就不多介绍了!del 关键字在前面的笔记中已有所涉及,比如删除列表中的某项,我们使用 “ del mylist[0] ” 可能这些剩下来的关键字你比较陌生,所以下面来介绍一下: 4.1.global 关键字 当你在函数定义内声明变量的时候,它们与函数外具有相同名称的其他变量没有任何关系,即变量名称对于函数来说是 局部 的。这称为变量的 作用域 。所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开始。 eg. ? 1 2 3 4 5 6 7 8 9 10 11 !/usr/bin/python Filename: func_local.py def func(x): print'x is', x x = 2 print'Changed local x to', x x = 50 func(x) print'x is still', x 运行的结果是这样的:? 1 2 3 4 $ python func_local.py x is 50 # 运行func函数时,先打印x的值,此时带的值是作为参数带入的外部定义的50,所以能正常打印 x=50 Changed local x to 2 # 在func函数中将x赋2,并打印 x is still 50 # 运行完func函数,打印x的值,此时x的值仍然是之前赋给的50,而不是func函数中修改过的2,因为在函数中修改的只是函数内的局部变量 那么为什么我们要在这提到局部变量呢?bingo,聪明的你一下就猜到这个global就是用来定义全局变量的。也就是说如果你想要为一个在函数外定义的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是 全局 的。我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。eg.? 1 2 3 4 5 6 7 8 9 10 11 12 !/usr/bin/python Filename: func_global.py def func(): global x print'x is', x x = 2 print'Changed local x to', x x = 50 func() print'Value of x is', x 运行的结果是这样的:? 1 2 3 4 $ python func_global.py x is 50 Changed global x to 2 Value of x is 2 # global语句被用来声明x是全局的——因此,当我们在函数内把值赋给x的时候,这个变化也反映在我们在主块中使用x的值的时候。 你可以使用同一个global语句指定多个全局变量。例如global x, y, z。 4.2.with 关键字 有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。如果不用with语句,打开一个文件并读文件的代码如下:? 1 2 3 file = open("/tmp/foo.txt") data = file.read() file.close() 当然这样直接打开有两个问题:一是可能忘记关闭文件句柄;二是文件读取数据发生异常,没有进行任何处理。下面是添加上异常处理的版本:? 1 2 3 4 5 file = open("/tmp/foo.txt") try: data = file.read() finally: file.close() 虽然这段代码运行良好,但是太冗余了。这时候就是with一展身手的时候了。除了有更优雅的语法,with还可以很好的处理上下文环境产生的异常。下面是with版本的代码:? 1 2 with open("/tmp/foo.txt") as file: data = file.read() 这看起来充满魔法,但不仅仅是魔法,Python对with的处理还很聪明。基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。with语句的执行逻辑如下:紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。 下面例子可以具体说明with如何工作:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 !/usr/bin/python with_example01.py classSample: def __enter__(self): print"In __enter__()" return"Foo" def __exit__(self, type, value, trace): print"In __exit__()" def get_sample(): returnSample() with get_sample() as sample: print"sample:", sample 运行代码,输出如下? 1 2 3 4 $python with_example01.py In __enter__() # __enter__()方法被执行 sample: Foo # __enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量'sample',执行代码块,打印变量"sample"的值为"Foo" In __exit__() # __exit__()方法被调用 4.3.assert 关键字 assert语句是一种插入调试断点到程序的一种便捷的方式。assert语句用来声明某个条件是真的,当assert语句失败的时候,会引发一AssertionError,所以结合try...except我们就可以处理这样的异常。 mylist # 此时mylist是有三个元素的列表['a', 'b', 'c']assert len(mylist) is not None # 用assert判断列表不为空,正确无返回assert len(mylist) is None # 用assert判断列表为空 Traceback (most recent call last): File "", line 1, in AssertionError # 引发AssertionError异常 4.4.yield 关键字 我们先看一个示例:? 1 2 3 4 5 6 7 8 def fab(max): n, a, b = 0,0,1 whilen < max: yield b # print b a, b = b, a + b n = n + 1 ''' 使用这个函数:? 1 2 3 4 5 6 7 8 forn in fab(5): ... print n ... 1 1 2 3 5 简单地讲,yield 的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable(可迭代的)对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 f = fab(5) f.next() 1 f.next() 1 f.next() 2 f.next() 3 f.next() 5 f.next() Traceback (most recent call last): File"", line 1, in StopIteration 当函数执行结束时,generator 自动抛出 StopIteration 异常,表示迭代完成。在 for 循环里,无需处理 StopIteration 异常,循环会正常结束。 我们可以得出以下结论:一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。 yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。 注:如果看完此段你还未明白yield,没问题,因为yield是初学者的一个难点,那么你下一步需要做的就是……看一看下面参考资料中给的关于yield的博文! 4.5.exec 关键字 官方文档对于exec的解释: "This statement supports dynamic execution of Python code."也就是说使用exec可以动态执行Python代码(也可以是文件)。? 1 2 3 4 5 6 7 8 9 10 11 12 13 longer = "print "Hello World ,my name is longer"" # 比如说我们定义了一个字符串 longer 'print "Hello World ,my name is longer"' exec(longer) # 使用exec 动态执行字符串中的代码 Hello World ,my name is longer exec(sayhi) # 使用exec直接打开文件名(指定sayhi,sayhi.py以及"sayhi.py"都会报一定的错,但是我觉得直接带sayhi报错非常典型) Traceback (most recent call last): File"", line 1, in TypeError: exec: arg 1must be a string, file, or code object # python IDE报错,提示exec的第一个参 数必须是一个字符串、文件或者一个代码对象 f = file("sayhi.py") # 使用file打开sayhi.py并创建f实例 exec(f) # 使用exec直接运行文件描述符f,运行正常!! Hi,thisis [''] script 上述给的例子比较简单,注意例子中exec语句的用法和eval_r(), execfile()是不一样的. exec是一个关键字(要不然我怎么会在这里介绍呢~~~), 而eval_r()和execfile()则是内建函数。更多关于exec的使用请详看引用资料或者Google之 在需要在字符中使用特殊字符时,python用反斜杠()转义字符。 原始字符串 有时我们并不想让转义字符生效,我们只想显示字符串原来的意思,这就要用r和R来定义原始字符串。如: print r’tr’ 实际输出为“tr”。 转义字符 描述 (在行尾时) 续行符 反斜杠符号 ’ 单引号 ” 双引号 a 响铃 b 退格(Backspace) e 转义 000 空 n 换行 v 纵向制表符 t 横向制表符 r 回车 f 换页 oyy 八进制数yy代表的字符,例如:o12代表换行 xyy 十进制数yy代表的字符,例如:x0a代表换行 other 其它的字符以普通格式输出

xuning715 2019-12-02 01:10:21 0 浏览量 回答数 0

问题

【精品问答】Java必备核心知识1000+(附源码)

问问小秘 2019-12-01 22:00:28 870 浏览量 回答数 1

回答

详细解答可以参考官方帮助文档 发送访问OSS的请求 您可以直接使用OSS提供的RESTful API接口访问或者使用对API接口进行完整封装的SDK开发包。而每一次向OSS的请求根据当前Bucket权限和操作不同要求用户进行身份验证或者直接匿名访问。对OSS的资源访问的分类如下: 按访问者的角色可分为拥有者访问和第三方用户访问。这里的拥有者指的是Bucket的Owner,也称为开发者。第三方用户是指访问Bucket里资源的用户。 按访问者的身份信息可分为匿名访问和带签名访问。对于OSS来说,如果请求中没有携带任何和身份相关的信息即为匿名访问。带签名访问指的是按照OSS API文档中规定的在请求头部或者在请求URL中携带签名的相关信息。 AccessKey 类型 目前访问 OSS 使用的 AK(AccessKey)有三种类型,具体如下: 阿里云账号AccessKey 阿里云账号AK特指Bucket拥有者的AK,每个阿里云账号提供的AccessKey对拥有的资源有完全的权限。每个阿里云账号能够同时拥有不超过5个active或者inactive的AK对(AccessKeyId和AccessKeySecret)。 用户可以登录AccessKey管理控制台,申请新增或删除AK对。 每个AK对都有active/inactive两种状态。 Active 表明用户的 AK 处于激活状态,可以在身份验证的时候使用。 Inactive 表明用户的 AK 处于非激活状态,不能在身份验证的时候使用。 说明 请避免直接使用阿里云账户的 AccessKey。 RAM子账号AccessKey RAM (Resource Access Management) 是阿里云提供的资源访问控制服务。RAM账号AK指的是通过RAM被授权的AK。这组AK只能按照RAM定义的规则去访问Bucket里的资源。通过RAM,您可以集中管理您的用户(比如员工、系统或应用程序),以及控制用户可以访问您名下哪些资源的权限。比如能够限制您的用户只拥有对某一个Bucket的读权限。子账号是从属于主账号的,并且这些账号下不能拥有实际的任何资源,所有资源都属于主账号。 STS账号AccessKey STS(Security Token Service)是阿里云提供的临时访问凭证服务。STS账号AK指的是通过STS颁发的AK。这组AK只能按照STS定义的规则去访问Bucket里的资源。 身份验证具体实现 目前主要有三种身份验证方式: AK验证 RAM验证 STS验证 当用户以个人身份向OSS发送请求时,其身份验证的实现如下: 用户将发送的请求按照OSS指定的格式生成签名字符串。 用户使用AccessKeySecret对签名字符串进行加密产生验证码。 OSS收到请求以后,通过AccessKeyId找到对应的AccessKeySecret,以同样的方法提取签名字符串和验证码。 如果计算出来的验证码和提供的一样即认为该请求是有效的。 否则,OSS将拒绝处理这次请求,并返回HTTP 403错误。 对于用户来说可以直接使用OSS提供的SDK,配合不同类型的AccessKey即可实现不同的身份验证。 权限控制 针对存放在Bucket的Object的访问,OSS提供了多种权限控制,主要有: Bucket级别权限 Object级别权限 账号级别权限(RAM) 临时账号权限(STS) Bucket级别权限 Bucket权限类型 OSS提供ACL(Access Control List)权限控制方法,OSS ACL提供Bucket级别的权限访问控制,Bucket目前有三种访问权限:public-read-write,public-read和private,它们的含义如下: 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 任何人(包括匿名访问)都可以对该Bucket中的Object进行读/写/删除操作;所有这些操作产生的费用由该Bucket的Owner承担,请慎用该权限。 public-read 公共读,私有写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行写/删除操作;任何人(包括匿名访问)可以对Object进行读操作。 private 私有读写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行读/写/删除操作;其他人在未经授权的情况下无法访问该Bucket内的Object。 Bucket权限设定和读取方法 功能使用参考: API:Put BucketACL SDK:Java SDK-设置Bucket ACL 控制台:创建Bucket权限设置 API:Get BucketACL SDK:Java SDK-获取Bucket ACL Object级别权限 Object权限类型 OSS ACL也提供Object级别的权限访问控制。目前Object有四种访问权限:private, public-read, public-read-write, default。Put Object ACL操作通过Put请求中的“x-oss-object-acl”头来设置,这个操作只有Bucket Owner有权限执行。 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 该ACL表明某个Object是公共读写资源,即所有用户拥有对该Object的读写权限。 public-read 公共读,私有写 该ACL表明某个Object是公共读资源,即非Object Owner只有该Object的读权限,而Object Owner拥有该Object的读写权限。 private 私有读写 该ACL表明某个Object是私有资源,即只有该Object的Owner拥有该Object的读写权限,其他的用户没有权限操作该Object。 default 默认权限 该ACL表明某个Object是遵循Bucket读写权限的资源,即Bucket是什么权限,Object就是什么权限。 说明 如果没有设置Object的权限,即Object的ACL为default,Object的权限和Bucket权限一致。 如果设置了Object的权限,Object的权限大于Bucket权限。举个例子,如果设置了Object的权限是public-read,无论Bucket是什么权限,该Object都可以被身份验证访问和匿名访问。 Object权限设定和读取方法 功能使用参考: API:Put Object ACL SDK:Java SDK-ObjectACL 中设定Object ACL API:Get Object ACL SDK:Java SDK-ObjectACL 中读取Object ACL 账号级别权限(RAM) 使用场景 如果您购买了云资源,您的组织里有多个用户需要使用这些云资源,这些用户只能共享使用您的云账号AccessKey。这里有两个问题: 您的密钥由多人共享,泄露的风险很高。 您无法控制特定用户能访问哪些资源(比如Bucket)的权限。 解决方法:在您的阿里云账号下面,通过RAM可以创建具有自己AccessKey的子用户。您的阿里云账号被称为主账号,创建出来的账号被称为子账号,使用子账号的AccessKey只能使用主账号授权的操作和资源。 具体实现 有关RAM详情,请参考RAM用户手册。 对于授权中需要的Policy的配置方式可以参考本章最后一节:RAM和STS授权策略(Policy)配置。 临时账号权限(STS) 使用场景 对于您本地身份系统所管理的用户,比如您的App的用户、您的企业本地账号、第三方App,也有直接访问OSS资源的可能,将这部分用户称为联盟用户。此外,用户还可以是您创建的能访问您的阿里云资源的应用程序。 对于这部分联盟用户,通过阿里云STS (Security Token Service) 服务为阿里云账号(或RAM用户)提供短期访问权限管理。您不需要透露云账号(或RAM用户)的长期密钥(如登录密码、AccessKey),只需要生成一个短期访问凭证给联盟用户使用即可。这个凭证的访问权限及有效期限都可以由您自定义。您不需要关心权限撤销问题,访问凭证过期后会自动失效。 用户通过STS生成的凭证包括安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)。使用AccessKey方法与您在使用阿里云账户或RAM用户AccessKey发送请求时的方法相同。此外还需要注意的是在每个向OSS发送的请求中必须携带安全令牌。 具体实现 STS安全令牌、角色管理和使用相关内容详情,请参考RAM用户指南中的角色管理。关键是调用STS服务接口AssumeRole来获取有效访问凭证即可,也可以直接使用STS SDK来调用该方法。 RAM和STS应用场景实践 对于不同的应用场景,涉及到的访问身份验证方式可能存在差异。下面以几种典型的应用场景来说明访问身份验证中几种使用方式。 以一个移动App举例。假设您是一个移动App开发者,打算使用阿里云OSS服务来保存App的终端用户数据,并且要保证每个App用户之间的数据隔离,防止一个App用户获取到其它App用户的数据。 方式一:使用AppServer来做数据中转和数据隔离如上图所示,您需要开发一个AppServer。只有AppServer能访问云服务,ClientApp的每次读写数据都需要通过AppServer,AppServer来保证不同用户数据的隔离访问。 对于该种使用方式,使用阿里云账号或者RAM账号提供的密钥来进行签名验证访问。建议您尽量不要直接使用阿里云账号(主账号)的密钥访问OSS,避免出现安全问题。 方式二:使用STS让用户直接访问OSS STS方案描述如下图所示:方案的详细描述如下: App用户登录。App用户和云账号无关,它是App的终端用户,AppServer支持App用户登录。对于每个有效的App用户来说,需要AppServer能定义出每个App用户的最小访问权限。 AppServer请求STS服务获取一个安全令牌(SecurityToken)。在调用STS之前,AppServer需要确定App用户的最小访问权限(用Policy语法描述)以及授权的过期时间。然后通过扮演角色(AssumeRole)来获取一个代表角色身份的安全令牌。 STS返回给AppServer一个有效的访问凭证,包括一个安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)以及过期时间。 AppServer将访问凭证返回给ClientApp。ClientApp可以缓存这个凭证。当凭证失效时,ClientApp需要向AppServer申请新的有效访问凭证。比如,访问凭证有效期为1小时,那么ClientApp可以每30分钟向AppServer请求更新访问凭证。 ClientApp使用本地缓存的访问凭证去请求Aliyun Service API。云服务会感知STS访问凭证,并会依赖STS服务来验证访问凭证,正确响应用户请求。 RAM和STS授权策略(Policy)配置 对于RAM或者STS授权中使用Policy,详细规则如下。 示例 先看下面的一个Policy示例: { "Version": "1", "Statement": [ { "Action": [ "oss:GetBucketAcl", "oss:ListObjects" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket" ], "Effect": "Allow", "Condition": { "StringEquals": { "acs:UserAgent": "java-sdk", "oss:Prefix": "foo" }, "IpAddress": { "acs:SourceIp": "192.168.0.1" } } }, { "Action": [ "oss:PutObject", "oss:GetObject", "oss:DeleteObject" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket/file*" ], "Effect": "Allow", "Condition": { "IpAddress": { "acs:SourceIp": "192.168.0.1" } } } ] } 这是一个授权的Policy,用户用这样的一个Policy通过RAM或STS服务向其他用户授权。Policy当中有一个Statement(一条Policy当中可以有多条Statement)。Statement里面规定了相应的Action、Resource、Effect和Condition。 这条Policy把用户自己名下的mybucket和mybucket/file*这些资源授权给相应的用户,并且支持GetBucketAcl、GetBucket、PutObject、GetObject和DeleteObject这几种操作。Condition中的条件表示UserAgent为java-sdk,源IP为192.168.0.1的时候鉴权才能通过,被授权的用户才能访问相关的资源。Prefix这个Condition是在GetBucket(ListObjects)的时候起作用的,关于这个字段的解释详见OSS的API文档。 配置细则 Version Version定义了Policy的版本,本文档中sw2q的配置方式,设置为1。 Statement 通过Statement描述授权语义,其中可以根据业务场景包含多条语义,每条包含对Action、Effect、Resource和Condition的描述。每次请求系统会逐条依次匹配检查,所有匹配成功的Statement会根据Effect的设置不同分为通过(Allow)、禁止(Deny),其中禁止(Deny)的优先。如果匹配成功的都为通过,该条请求即鉴权通过。如果匹配成功有一条禁止,或者没有任何条目匹配成功,该条请求被禁止访问。 Action Action分为三大类:Service级别操作,对应的是GetService操作,用来列出所有属于该用户的Bucket列表。 Bucket级别操作,对应类似于oss:PutBucketAcl、oss:GetBucketLocation之类的操作,操作的对象是Bucket,它们的名称和相应的接口名称一一对应。 Object级别操作,分为oss:GetObject、oss:PutObject、oss:DeleteObject和oss:AbortMultipartUpload,操作对象是Object。 如想授权某一类的Object的操作,可以选择这几种的一种或几种。另外,所有的Action前面都必须加上oss:,如上面例子所示。Action是一个列表,可以有多个Action。具体的Action和API接口的对应关系如下: Service级别 API Action GetService(ListBuckets) oss:ListBuckets Bucket级别 API Action PutBucket oss:PutBucket GetBucket(ListObjects) oss:ListObjects PutBucketAcl oss:PutBucketAcl DeleteBucket oss:DeleteBucket GetBucketLocation oss:GetBucketLocation GetBucketAcl oss:GetBucketAcl GetBucketLogging oss:GetBucketLogging PutBucketLogging oss:PutBucketLogging DeleteBucketLogging oss:DeleteBucketLogging GetBucketWebsite oss:GetBucketWebsite PutBucketWebsite oss:PutBucketWebsite DeleteBucketWebsite oss:DeleteBucketWebsite GetBucketReferer oss:GetBucketReferer PutBucketReferer oss:PutBucketReferer GetBucketLifecycle oss:GetBucketLifecycle PutBucketLifecycle oss:PutBucketLifecycle DeleteBucketLifecycle oss:DeleteBucketLifecycle ListMultipartUploads oss:ListMultipartUploads PutBucketCors oss:PutBucketCors GetBucketCors oss:GetBucketCors DeleteBucketCors oss:DeleteBucketCors PutBucketReplication oss:PutBucketReplication GetBucketReplication oss:GetBucketReplication DeleteBucketReplication oss:DeleteBucketReplication GetBucketReplicationLocation oss:GetBucketReplicationLocation GetBucketReplicationProgress oss:GetBucketReplicationProgress Object级别 API Action GetObject oss:GetObject HeadObject oss:GetObject PutObject oss:PutObject PostObject oss:PutObject InitiateMultipartUpload oss:PutObject UploadPart oss:PutObject CompleteMultipart oss:PutObject DeleteObject oss:DeleteObject DeleteMultipartObjects oss:DeleteObject AbortMultipartUpload oss:AbortMultipartUpload ListParts oss:ListParts CopyObject oss:GetObject,oss:PutObject UploadPartCopy oss:GetObject,oss:PutObject AppendObject oss:PutObject GetObjectAcl oss:GetObjectAcl PutObjectAcl oss:PutObjectAcl Resource Resource指代的是OSS上面的某个具体的资源或者某些资源(支持*通配),resource的规则是acs:oss:{region}:{bucket_owner}:{bucket_name}/{object_name}。对于所有Bucket级别的操作来说不需要最后的斜杠和{object_name},即acs:oss:{region}:{bucket_owner}:{bucket_name}。Resource也是一个列表,可以有多个Resource。其中的region字段暂时不做支持,设置为*。 Effect Effect代表本条的Statement的授权的结果,分为Allow和Deny,分别指代通过和禁止。多条Statement同时匹配成功时,禁止(Deny)的优先级更高。 例如,期望禁止用户对某一目录进行删除,但对于其他文件有全部权限: { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:bucketname" ] }, { "Effect": "Deny", "Action": [ "oss:DeleteObject" ], "Resource": [ "acs:oss:*:*:bucketname/index/*", ] } ] } Condition Condition代表Policy授权的一些条件,上面的示例里面可以设置对于acs:UserAgent的检查、acs:SourceIp的检查、还有oss:Prefix这项用来在GetBucket的时候对资源进行限制。 OSS支持的Condition如下: condition 功能 合法取值 acs:SourceIp 指定ip网段 普通的ip,支持*通配 acs:UserAgent 指定http useragent头 字符串 acs:CurrentTime 指定合法的访问时间 ISO8601格式 acs:SecureTransport 是否是https协议 “true”或者”false” oss:Prefix 用作ListObjects时的prefix 合法的object name 更多示例 针对具体场景更多的授权策略配置示例,可以参考教程示例:控制存储空间和文件夹的访问权限和OSS授权常见问题。 Policy在线图形化便捷配置工具,请单击这里。 最佳实践 RAM和STS使用指南

2019-12-01 23:12:47 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 发送访问OSS的请求 您可以直接使用OSS提供的RESTful API接口访问或者使用对API接口进行完整封装的SDK开发包。而每一次向OSS的请求根据当前Bucket权限和操作不同要求用户进行身份验证或者直接匿名访问。对OSS的资源访问的分类如下: 按访问者的角色可分为拥有者访问和第三方用户访问。这里的拥有者指的是Bucket的Owner,也称为开发者。第三方用户是指访问Bucket里资源的用户。 按访问者的身份信息可分为匿名访问和带签名访问。对于OSS来说,如果请求中没有携带任何和身份相关的信息即为匿名访问。带签名访问指的是按照OSS API文档中规定的在请求头部或者在请求URL中携带签名的相关信息。 AccessKey 类型 目前访问 OSS 使用的 AK(AccessKey)有三种类型,具体如下: 阿里云账号AccessKey 阿里云账号AK特指Bucket拥有者的AK,每个阿里云账号提供的AccessKey对拥有的资源有完全的权限。每个阿里云账号能够同时拥有不超过5个active或者inactive的AK对(AccessKeyId和AccessKeySecret)。 用户可以登录AccessKey管理控制台,申请新增或删除AK对。 每个AK对都有active/inactive两种状态。 Active 表明用户的 AK 处于激活状态,可以在身份验证的时候使用。 Inactive 表明用户的 AK 处于非激活状态,不能在身份验证的时候使用。 说明 请避免直接使用阿里云账户的 AccessKey。 RAM子账号AccessKey RAM (Resource Access Management) 是阿里云提供的资源访问控制服务。RAM账号AK指的是通过RAM被授权的AK。这组AK只能按照RAM定义的规则去访问Bucket里的资源。通过RAM,您可以集中管理您的用户(比如员工、系统或应用程序),以及控制用户可以访问您名下哪些资源的权限。比如能够限制您的用户只拥有对某一个Bucket的读权限。子账号是从属于主账号的,并且这些账号下不能拥有实际的任何资源,所有资源都属于主账号。 STS账号AccessKey STS(Security Token Service)是阿里云提供的临时访问凭证服务。STS账号AK指的是通过STS颁发的AK。这组AK只能按照STS定义的规则去访问Bucket里的资源。 身份验证具体实现 目前主要有三种身份验证方式: AK验证 RAM验证 STS验证 当用户以个人身份向OSS发送请求时,其身份验证的实现如下: 用户将发送的请求按照OSS指定的格式生成签名字符串。 用户使用AccessKeySecret对签名字符串进行加密产生验证码。 OSS收到请求以后,通过AccessKeyId找到对应的AccessKeySecret,以同样的方法提取签名字符串和验证码。 如果计算出来的验证码和提供的一样即认为该请求是有效的。 否则,OSS将拒绝处理这次请求,并返回HTTP 403错误。 对于用户来说可以直接使用OSS提供的SDK,配合不同类型的AccessKey即可实现不同的身份验证。 权限控制 针对存放在Bucket的Object的访问,OSS提供了多种权限控制,主要有: Bucket级别权限 Object级别权限 账号级别权限(RAM) 临时账号权限(STS) Bucket级别权限 Bucket权限类型 OSS提供ACL(Access Control List)权限控制方法,OSS ACL提供Bucket级别的权限访问控制,Bucket目前有三种访问权限:public-read-write,public-read和private,它们的含义如下: 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 任何人(包括匿名访问)都可以对该Bucket中的Object进行读/写/删除操作;所有这些操作产生的费用由该Bucket的Owner承担,请慎用该权限。 public-read 公共读,私有写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行写/删除操作;任何人(包括匿名访问)可以对Object进行读操作。 private 私有读写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行读/写/删除操作;其他人在未经授权的情况下无法访问该Bucket内的Object。 Bucket权限设定和读取方法 功能使用参考: API:Put BucketACL SDK:Java SDK-设置Bucket ACL 控制台:创建Bucket权限设置 API:Get BucketACL SDK:Java SDK-获取Bucket ACL Object级别权限 Object权限类型 OSS ACL也提供Object级别的权限访问控制。目前Object有四种访问权限:private, public-read, public-read-write, default。Put Object ACL操作通过Put请求中的“x-oss-object-acl”头来设置,这个操作只有Bucket Owner有权限执行。 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 该ACL表明某个Object是公共读写资源,即所有用户拥有对该Object的读写权限。 public-read 公共读,私有写 该ACL表明某个Object是公共读资源,即非Object Owner只有该Object的读权限,而Object Owner拥有该Object的读写权限。 private 私有读写 该ACL表明某个Object是私有资源,即只有该Object的Owner拥有该Object的读写权限,其他的用户没有权限操作该Object。 default 默认权限 该ACL表明某个Object是遵循Bucket读写权限的资源,即Bucket是什么权限,Object就是什么权限。 说明 如果没有设置Object的权限,即Object的ACL为default,Object的权限和Bucket权限一致。 如果设置了Object的权限,Object的权限大于Bucket权限。举个例子,如果设置了Object的权限是public-read,无论Bucket是什么权限,该Object都可以被身份验证访问和匿名访问。 Object权限设定和读取方法 功能使用参考: API:Put Object ACL SDK:Java SDK-ObjectACL 中设定Object ACL API:Get Object ACL SDK:Java SDK-ObjectACL 中读取Object ACL 账号级别权限(RAM) 使用场景 如果您购买了云资源,您的组织里有多个用户需要使用这些云资源,这些用户只能共享使用您的云账号AccessKey。这里有两个问题: 您的密钥由多人共享,泄露的风险很高。 您无法控制特定用户能访问哪些资源(比如Bucket)的权限。 解决方法:在您的阿里云账号下面,通过RAM可以创建具有自己AccessKey的子用户。您的阿里云账号被称为主账号,创建出来的账号被称为子账号,使用子账号的AccessKey只能使用主账号授权的操作和资源。 具体实现 有关RAM详情,请参考RAM用户手册。 对于授权中需要的Policy的配置方式可以参考本章最后一节:RAM和STS授权策略(Policy)配置。 临时账号权限(STS) 使用场景 对于您本地身份系统所管理的用户,比如您的App的用户、您的企业本地账号、第三方App,也有直接访问OSS资源的可能,将这部分用户称为联盟用户。此外,用户还可以是您创建的能访问您的阿里云资源的应用程序。 对于这部分联盟用户,通过阿里云STS (Security Token Service) 服务为阿里云账号(或RAM用户)提供短期访问权限管理。您不需要透露云账号(或RAM用户)的长期密钥(如登录密码、AccessKey),只需要生成一个短期访问凭证给联盟用户使用即可。这个凭证的访问权限及有效期限都可以由您自定义。您不需要关心权限撤销问题,访问凭证过期后会自动失效。 用户通过STS生成的凭证包括安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)。使用AccessKey方法与您在使用阿里云账户或RAM用户AccessKey发送请求时的方法相同。此外还需要注意的是在每个向OSS发送的请求中必须携带安全令牌。 具体实现 STS安全令牌、角色管理和使用相关内容详情,请参考RAM用户指南中的角色管理。关键是调用STS服务接口AssumeRole来获取有效访问凭证即可,也可以直接使用STS SDK来调用该方法。 RAM和STS应用场景实践 对于不同的应用场景,涉及到的访问身份验证方式可能存在差异。下面以几种典型的应用场景来说明访问身份验证中几种使用方式。 以一个移动App举例。假设您是一个移动App开发者,打算使用阿里云OSS服务来保存App的终端用户数据,并且要保证每个App用户之间的数据隔离,防止一个App用户获取到其它App用户的数据。 方式一:使用AppServer来做数据中转和数据隔离如上图所示,您需要开发一个AppServer。只有AppServer能访问云服务,ClientApp的每次读写数据都需要通过AppServer,AppServer来保证不同用户数据的隔离访问。 对于该种使用方式,使用阿里云账号或者RAM账号提供的密钥来进行签名验证访问。建议您尽量不要直接使用阿里云账号(主账号)的密钥访问OSS,避免出现安全问题。 方式二:使用STS让用户直接访问OSS STS方案描述如下图所示:方案的详细描述如下: App用户登录。App用户和云账号无关,它是App的终端用户,AppServer支持App用户登录。对于每个有效的App用户来说,需要AppServer能定义出每个App用户的最小访问权限。 AppServer请求STS服务获取一个安全令牌(SecurityToken)。在调用STS之前,AppServer需要确定App用户的最小访问权限(用Policy语法描述)以及授权的过期时间。然后通过扮演角色(AssumeRole)来获取一个代表角色身份的安全令牌。 STS返回给AppServer一个有效的访问凭证,包括一个安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)以及过期时间。 AppServer将访问凭证返回给ClientApp。ClientApp可以缓存这个凭证。当凭证失效时,ClientApp需要向AppServer申请新的有效访问凭证。比如,访问凭证有效期为1小时,那么ClientApp可以每30分钟向AppServer请求更新访问凭证。 ClientApp使用本地缓存的访问凭证去请求Aliyun Service API。云服务会感知STS访问凭证,并会依赖STS服务来验证访问凭证,正确响应用户请求。 RAM和STS授权策略(Policy)配置 对于RAM或者STS授权中使用Policy,详细规则如下。 示例 先看下面的一个Policy示例: { "Version": "1", "Statement": [ { "Action": [ "oss:GetBucketAcl", "oss:ListObjects" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket" ], "Effect": "Allow", "Condition": { "StringEquals": { "acs:UserAgent": "java-sdk", "oss:Prefix": "foo" }, "IpAddress": { "acs:SourceIp": "192.168.0.1" } } }, { "Action": [ "oss:PutObject", "oss:GetObject", "oss:DeleteObject" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket/file*" ], "Effect": "Allow", "Condition": { "IpAddress": { "acs:SourceIp": "192.168.0.1" } } } ] } 这是一个授权的Policy,用户用这样的一个Policy通过RAM或STS服务向其他用户授权。Policy当中有一个Statement(一条Policy当中可以有多条Statement)。Statement里面规定了相应的Action、Resource、Effect和Condition。 这条Policy把用户自己名下的mybucket和mybucket/file*这些资源授权给相应的用户,并且支持GetBucketAcl、GetBucket、PutObject、GetObject和DeleteObject这几种操作。Condition中的条件表示UserAgent为java-sdk,源IP为192.168.0.1的时候鉴权才能通过,被授权的用户才能访问相关的资源。Prefix这个Condition是在GetBucket(ListObjects)的时候起作用的,关于这个字段的解释详见OSS的API文档。 配置细则 Version Version定义了Policy的版本,本文档中sw2q的配置方式,设置为1。 Statement 通过Statement描述授权语义,其中可以根据业务场景包含多条语义,每条包含对Action、Effect、Resource和Condition的描述。每次请求系统会逐条依次匹配检查,所有匹配成功的Statement会根据Effect的设置不同分为通过(Allow)、禁止(Deny),其中禁止(Deny)的优先。如果匹配成功的都为通过,该条请求即鉴权通过。如果匹配成功有一条禁止,或者没有任何条目匹配成功,该条请求被禁止访问。 Action Action分为三大类:Service级别操作,对应的是GetService操作,用来列出所有属于该用户的Bucket列表。 Bucket级别操作,对应类似于oss:PutBucketAcl、oss:GetBucketLocation之类的操作,操作的对象是Bucket,它们的名称和相应的接口名称一一对应。 Object级别操作,分为oss:GetObject、oss:PutObject、oss:DeleteObject和oss:AbortMultipartUpload,操作对象是Object。 如想授权某一类的Object的操作,可以选择这几种的一种或几种。另外,所有的Action前面都必须加上oss:,如上面例子所示。Action是一个列表,可以有多个Action。具体的Action和API接口的对应关系如下: Service级别 API Action GetService(ListBuckets) oss:ListBuckets Bucket级别 API Action PutBucket oss:PutBucket GetBucket(ListObjects) oss:ListObjects PutBucketAcl oss:PutBucketAcl DeleteBucket oss:DeleteBucket GetBucketLocation oss:GetBucketLocation GetBucketAcl oss:GetBucketAcl GetBucketLogging oss:GetBucketLogging PutBucketLogging oss:PutBucketLogging DeleteBucketLogging oss:DeleteBucketLogging GetBucketWebsite oss:GetBucketWebsite PutBucketWebsite oss:PutBucketWebsite DeleteBucketWebsite oss:DeleteBucketWebsite GetBucketReferer oss:GetBucketReferer PutBucketReferer oss:PutBucketReferer GetBucketLifecycle oss:GetBucketLifecycle PutBucketLifecycle oss:PutBucketLifecycle DeleteBucketLifecycle oss:DeleteBucketLifecycle ListMultipartUploads oss:ListMultipartUploads PutBucketCors oss:PutBucketCors GetBucketCors oss:GetBucketCors DeleteBucketCors oss:DeleteBucketCors PutBucketReplication oss:PutBucketReplication GetBucketReplication oss:GetBucketReplication DeleteBucketReplication oss:DeleteBucketReplication GetBucketReplicationLocation oss:GetBucketReplicationLocation GetBucketReplicationProgress oss:GetBucketReplicationProgress Object级别 API Action GetObject oss:GetObject HeadObject oss:GetObject PutObject oss:PutObject PostObject oss:PutObject InitiateMultipartUpload oss:PutObject UploadPart oss:PutObject CompleteMultipart oss:PutObject DeleteObject oss:DeleteObject DeleteMultipartObjects oss:DeleteObject AbortMultipartUpload oss:AbortMultipartUpload ListParts oss:ListParts CopyObject oss:GetObject,oss:PutObject UploadPartCopy oss:GetObject,oss:PutObject AppendObject oss:PutObject GetObjectAcl oss:GetObjectAcl PutObjectAcl oss:PutObjectAcl Resource Resource指代的是OSS上面的某个具体的资源或者某些资源(支持*通配),resource的规则是acs:oss:{region}:{bucket_owner}:{bucket_name}/{object_name}。对于所有Bucket级别的操作来说不需要最后的斜杠和{object_name},即acs:oss:{region}:{bucket_owner}:{bucket_name}。Resource也是一个列表,可以有多个Resource。其中的region字段暂时不做支持,设置为*。 Effect Effect代表本条的Statement的授权的结果,分为Allow和Deny,分别指代通过和禁止。多条Statement同时匹配成功时,禁止(Deny)的优先级更高。 例如,期望禁止用户对某一目录进行删除,但对于其他文件有全部权限: { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:bucketname" ] }, { "Effect": "Deny", "Action": [ "oss:DeleteObject" ], "Resource": [ "acs:oss:*:*:bucketname/index/*", ] } ] } Condition Condition代表Policy授权的一些条件,上面的示例里面可以设置对于acs:UserAgent的检查、acs:SourceIp的检查、还有oss:Prefix这项用来在GetBucket的时候对资源进行限制。 OSS支持的Condition如下: condition 功能 合法取值 acs:SourceIp 指定ip网段 普通的ip,支持*通配 acs:UserAgent 指定http useragent头 字符串 acs:CurrentTime 指定合法的访问时间 ISO8601格式 acs:SecureTransport 是否是https协议 “true”或者”false” oss:Prefix 用作ListObjects时的prefix 合法的object name 更多示例 针对具体场景更多的授权策略配置示例,可以参考教程示例:控制存储空间和文件夹的访问权限和OSS授权常见问题。 Policy在线图形化便捷配置工具,请单击这里。 最佳实践 RAM和STS使用指南

2019-12-01 23:12:47 0 浏览量 回答数 0

回答

详细解答可以参考官方帮助文档 发送访问OSS的请求 您可以直接使用OSS提供的RESTful API接口访问或者使用对API接口进行完整封装的SDK开发包。而每一次向OSS的请求根据当前Bucket权限和操作不同要求用户进行身份验证或者直接匿名访问。对OSS的资源访问的分类如下: 按访问者的角色可分为拥有者访问和第三方用户访问。这里的拥有者指的是Bucket的Owner,也称为开发者。第三方用户是指访问Bucket里资源的用户。 按访问者的身份信息可分为匿名访问和带签名访问。对于OSS来说,如果请求中没有携带任何和身份相关的信息即为匿名访问。带签名访问指的是按照OSS API文档中规定的在请求头部或者在请求URL中携带签名的相关信息。 AccessKey 类型 目前访问 OSS 使用的 AK(AccessKey)有三种类型,具体如下: 阿里云账号AccessKey 阿里云账号AK特指Bucket拥有者的AK,每个阿里云账号提供的AccessKey对拥有的资源有完全的权限。每个阿里云账号能够同时拥有不超过5个active或者inactive的AK对(AccessKeyId和AccessKeySecret)。 用户可以登录AccessKey管理控制台,申请新增或删除AK对。 每个AK对都有active/inactive两种状态。 Active 表明用户的 AK 处于激活状态,可以在身份验证的时候使用。 Inactive 表明用户的 AK 处于非激活状态,不能在身份验证的时候使用。 说明 请避免直接使用阿里云账户的 AccessKey。 RAM子账号AccessKey RAM (Resource Access Management) 是阿里云提供的资源访问控制服务。RAM账号AK指的是通过RAM被授权的AK。这组AK只能按照RAM定义的规则去访问Bucket里的资源。通过RAM,您可以集中管理您的用户(比如员工、系统或应用程序),以及控制用户可以访问您名下哪些资源的权限。比如能够限制您的用户只拥有对某一个Bucket的读权限。子账号是从属于主账号的,并且这些账号下不能拥有实际的任何资源,所有资源都属于主账号。 STS账号AccessKey STS(Security Token Service)是阿里云提供的临时访问凭证服务。STS账号AK指的是通过STS颁发的AK。这组AK只能按照STS定义的规则去访问Bucket里的资源。 身份验证具体实现 目前主要有三种身份验证方式: AK验证 RAM验证 STS验证 当用户以个人身份向OSS发送请求时,其身份验证的实现如下: 用户将发送的请求按照OSS指定的格式生成签名字符串。 用户使用AccessKeySecret对签名字符串进行加密产生验证码。 OSS收到请求以后,通过AccessKeyId找到对应的AccessKeySecret,以同样的方法提取签名字符串和验证码。 如果计算出来的验证码和提供的一样即认为该请求是有效的。 否则,OSS将拒绝处理这次请求,并返回HTTP 403错误。 对于用户来说可以直接使用OSS提供的SDK,配合不同类型的AccessKey即可实现不同的身份验证。 权限控制 针对存放在Bucket的Object的访问,OSS提供了多种权限控制,主要有: Bucket级别权限 Object级别权限 账号级别权限(RAM) 临时账号权限(STS) Bucket级别权限 Bucket权限类型 OSS提供ACL(Access Control List)权限控制方法,OSS ACL提供Bucket级别的权限访问控制,Bucket目前有三种访问权限:public-read-write,public-read和private,它们的含义如下: 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 任何人(包括匿名访问)都可以对该Bucket中的Object进行读/写/删除操作;所有这些操作产生的费用由该Bucket的Owner承担,请慎用该权限。 public-read 公共读,私有写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行写/删除操作;任何人(包括匿名访问)可以对Object进行读操作。 private 私有读写 只有该Bucket的Owner或者授权对象可以对存放在其中的Object进行读/写/删除操作;其他人在未经授权的情况下无法访问该Bucket内的Object。 Bucket权限设定和读取方法 功能使用参考: API:Put BucketACL SDK:Java SDK-设置Bucket ACL 控制台:创建Bucket权限设置 API:Get BucketACL SDK:Java SDK-获取Bucket ACL Object级别权限 Object权限类型 OSS ACL也提供Object级别的权限访问控制。目前Object有四种访问权限:private, public-read, public-read-write, default。Put Object ACL操作通过Put请求中的“x-oss-object-acl”头来设置,这个操作只有Bucket Owner有权限执行。 权限值 中文名称 权限对访问者的限制 public-read-write 公共读写 该ACL表明某个Object是公共读写资源,即所有用户拥有对该Object的读写权限。 public-read 公共读,私有写 该ACL表明某个Object是公共读资源,即非Object Owner只有该Object的读权限,而Object Owner拥有该Object的读写权限。 private 私有读写 该ACL表明某个Object是私有资源,即只有该Object的Owner拥有该Object的读写权限,其他的用户没有权限操作该Object。 default 默认权限 该ACL表明某个Object是遵循Bucket读写权限的资源,即Bucket是什么权限,Object就是什么权限。 说明 如果没有设置Object的权限,即Object的ACL为default,Object的权限和Bucket权限一致。 如果设置了Object的权限,Object的权限大于Bucket权限。举个例子,如果设置了Object的权限是public-read,无论Bucket是什么权限,该Object都可以被身份验证访问和匿名访问。 Object权限设定和读取方法 功能使用参考: API:Put Object ACL SDK:Java SDK-ObjectACL 中设定Object ACL API:Get Object ACL SDK:Java SDK-ObjectACL 中读取Object ACL 账号级别权限(RAM) 使用场景 如果您购买了云资源,您的组织里有多个用户需要使用这些云资源,这些用户只能共享使用您的云账号AccessKey。这里有两个问题: 您的密钥由多人共享,泄露的风险很高。 您无法控制特定用户能访问哪些资源(比如Bucket)的权限。 解决方法:在您的阿里云账号下面,通过RAM可以创建具有自己AccessKey的子用户。您的阿里云账号被称为主账号,创建出来的账号被称为子账号,使用子账号的AccessKey只能使用主账号授权的操作和资源。 具体实现 有关RAM详情,请参考RAM用户手册。 对于授权中需要的Policy的配置方式可以参考本章最后一节:RAM和STS授权策略(Policy)配置。 临时账号权限(STS) 使用场景 对于您本地身份系统所管理的用户,比如您的App的用户、您的企业本地账号、第三方App,也有直接访问OSS资源的可能,将这部分用户称为联盟用户。此外,用户还可以是您创建的能访问您的阿里云资源的应用程序。 对于这部分联盟用户,通过阿里云STS (Security Token Service) 服务为阿里云账号(或RAM用户)提供短期访问权限管理。您不需要透露云账号(或RAM用户)的长期密钥(如登录密码、AccessKey),只需要生成一个短期访问凭证给联盟用户使用即可。这个凭证的访问权限及有效期限都可以由您自定义。您不需要关心权限撤销问题,访问凭证过期后会自动失效。 用户通过STS生成的凭证包括安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)。使用AccessKey方法与您在使用阿里云账户或RAM用户AccessKey发送请求时的方法相同。此外还需要注意的是在每个向OSS发送的请求中必须携带安全令牌。 具体实现 STS安全令牌、角色管理和使用相关内容详情,请参考RAM用户指南中的角色管理。关键是调用STS服务接口AssumeRole来获取有效访问凭证即可,也可以直接使用STS SDK来调用该方法。 RAM和STS应用场景实践 对于不同的应用场景,涉及到的访问身份验证方式可能存在差异。下面以几种典型的应用场景来说明访问身份验证中几种使用方式。 以一个移动App举例。假设您是一个移动App开发者,打算使用阿里云OSS服务来保存App的终端用户数据,并且要保证每个App用户之间的数据隔离,防止一个App用户获取到其它App用户的数据。 方式一:使用AppServer来做数据中转和数据隔离如上图所示,您需要开发一个AppServer。只有AppServer能访问云服务,ClientApp的每次读写数据都需要通过AppServer,AppServer来保证不同用户数据的隔离访问。 对于该种使用方式,使用阿里云账号或者RAM账号提供的密钥来进行签名验证访问。建议您尽量不要直接使用阿里云账号(主账号)的密钥访问OSS,避免出现安全问题。 方式二:使用STS让用户直接访问OSS STS方案描述如下图所示:方案的详细描述如下: App用户登录。App用户和云账号无关,它是App的终端用户,AppServer支持App用户登录。对于每个有效的App用户来说,需要AppServer能定义出每个App用户的最小访问权限。 AppServer请求STS服务获取一个安全令牌(SecurityToken)。在调用STS之前,AppServer需要确定App用户的最小访问权限(用Policy语法描述)以及授权的过期时间。然后通过扮演角色(AssumeRole)来获取一个代表角色身份的安全令牌。 STS返回给AppServer一个有效的访问凭证,包括一个安全令牌(SecurityToken)、临时访问密钥(AccessKeyId, AccessKeySecret)以及过期时间。 AppServer将访问凭证返回给ClientApp。ClientApp可以缓存这个凭证。当凭证失效时,ClientApp需要向AppServer申请新的有效访问凭证。比如,访问凭证有效期为1小时,那么ClientApp可以每30分钟向AppServer请求更新访问凭证。 ClientApp使用本地缓存的访问凭证去请求Aliyun Service API。云服务会感知STS访问凭证,并会依赖STS服务来验证访问凭证,正确响应用户请求。 RAM和STS授权策略(Policy)配置 对于RAM或者STS授权中使用Policy,详细规则如下。 示例 先看下面的一个Policy示例: { "Version": "1", "Statement": [ { "Action": [ "oss:GetBucketAcl", "oss:ListObjects" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket" ], "Effect": "Allow", "Condition": { "StringEquals": { "acs:UserAgent": "java-sdk", "oss:Prefix": "foo" }, "IpAddress": { "acs:SourceIp": "192.168.0.1" } } }, { "Action": [ "oss:PutObject", "oss:GetObject", "oss:DeleteObject" ], "Resource": [ "acs:oss:*:1775305056529849:mybucket/file*" ], "Effect": "Allow", "Condition": { "IpAddress": { "acs:SourceIp": "192.168.0.1" } } } ] } 这是一个授权的Policy,用户用这样的一个Policy通过RAM或STS服务向其他用户授权。Policy当中有一个Statement(一条Policy当中可以有多条Statement)。Statement里面规定了相应的Action、Resource、Effect和Condition。 这条Policy把用户自己名下的mybucket和mybucket/file*这些资源授权给相应的用户,并且支持GetBucketAcl、GetBucket、PutObject、GetObject和DeleteObject这几种操作。Condition中的条件表示UserAgent为java-sdk,源IP为192.168.0.1的时候鉴权才能通过,被授权的用户才能访问相关的资源。Prefix这个Condition是在GetBucket(ListObjects)的时候起作用的,关于这个字段的解释详见OSS的API文档。 配置细则 Version Version定义了Policy的版本,本文档中sw2q的配置方式,设置为1。 Statement 通过Statement描述授权语义,其中可以根据业务场景包含多条语义,每条包含对Action、Effect、Resource和Condition的描述。每次请求系统会逐条依次匹配检查,所有匹配成功的Statement会根据Effect的设置不同分为通过(Allow)、禁止(Deny),其中禁止(Deny)的优先。如果匹配成功的都为通过,该条请求即鉴权通过。如果匹配成功有一条禁止,或者没有任何条目匹配成功,该条请求被禁止访问。 Action Action分为三大类:Service级别操作,对应的是GetService操作,用来列出所有属于该用户的Bucket列表。 Bucket级别操作,对应类似于oss:PutBucketAcl、oss:GetBucketLocation之类的操作,操作的对象是Bucket,它们的名称和相应的接口名称一一对应。 Object级别操作,分为oss:GetObject、oss:PutObject、oss:DeleteObject和oss:AbortMultipartUpload,操作对象是Object。 如想授权某一类的Object的操作,可以选择这几种的一种或几种。另外,所有的Action前面都必须加上oss:,如上面例子所示。Action是一个列表,可以有多个Action。具体的Action和API接口的对应关系如下: Service级别 API Action GetService(ListBuckets) oss:ListBuckets Bucket级别 API Action PutBucket oss:PutBucket GetBucket(ListObjects) oss:ListObjects PutBucketAcl oss:PutBucketAcl DeleteBucket oss:DeleteBucket GetBucketLocation oss:GetBucketLocation GetBucketAcl oss:GetBucketAcl GetBucketLogging oss:GetBucketLogging PutBucketLogging oss:PutBucketLogging DeleteBucketLogging oss:DeleteBucketLogging GetBucketWebsite oss:GetBucketWebsite PutBucketWebsite oss:PutBucketWebsite DeleteBucketWebsite oss:DeleteBucketWebsite GetBucketReferer oss:GetBucketReferer PutBucketReferer oss:PutBucketReferer GetBucketLifecycle oss:GetBucketLifecycle PutBucketLifecycle oss:PutBucketLifecycle DeleteBucketLifecycle oss:DeleteBucketLifecycle ListMultipartUploads oss:ListMultipartUploads PutBucketCors oss:PutBucketCors GetBucketCors oss:GetBucketCors DeleteBucketCors oss:DeleteBucketCors PutBucketReplication oss:PutBucketReplication GetBucketReplication oss:GetBucketReplication DeleteBucketReplication oss:DeleteBucketReplication GetBucketReplicationLocation oss:GetBucketReplicationLocation GetBucketReplicationProgress oss:GetBucketReplicationProgress Object级别 API Action GetObject oss:GetObject HeadObject oss:GetObject PutObject oss:PutObject PostObject oss:PutObject InitiateMultipartUpload oss:PutObject UploadPart oss:PutObject CompleteMultipart oss:PutObject DeleteObject oss:DeleteObject DeleteMultipartObjects oss:DeleteObject AbortMultipartUpload oss:AbortMultipartUpload ListParts oss:ListParts CopyObject oss:GetObject,oss:PutObject UploadPartCopy oss:GetObject,oss:PutObject AppendObject oss:PutObject GetObjectAcl oss:GetObjectAcl PutObjectAcl oss:PutObjectAcl Resource Resource指代的是OSS上面的某个具体的资源或者某些资源(支持*通配),resource的规则是acs:oss:{region}:{bucket_owner}:{bucket_name}/{object_name}。对于所有Bucket级别的操作来说不需要最后的斜杠和{object_name},即acs:oss:{region}:{bucket_owner}:{bucket_name}。Resource也是一个列表,可以有多个Resource。其中的region字段暂时不做支持,设置为*。 Effect Effect代表本条的Statement的授权的结果,分为Allow和Deny,分别指代通过和禁止。多条Statement同时匹配成功时,禁止(Deny)的优先级更高。 例如,期望禁止用户对某一目录进行删除,但对于其他文件有全部权限: { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:bucketname" ] }, { "Effect": "Deny", "Action": [ "oss:DeleteObject" ], "Resource": [ "acs:oss:*:*:bucketname/index/*", ] } ] } Condition Condition代表Policy授权的一些条件,上面的示例里面可以设置对于acs:UserAgent的检查、acs:SourceIp的检查、还有oss:Prefix这项用来在GetBucket的时候对资源进行限制。 OSS支持的Condition如下: condition 功能 合法取值 acs:SourceIp 指定ip网段 普通的ip,支持*通配 acs:UserAgent 指定http useragent头 字符串 acs:CurrentTime 指定合法的访问时间 ISO8601格式 acs:SecureTransport 是否是https协议 “true”或者”false” oss:Prefix 用作ListObjects时的prefix 合法的object name 更多示例 针对具体场景更多的授权策略配置示例,可以参考教程示例:控制存储空间和文件夹的访问权限和OSS授权常见问题。 Policy在线图形化便捷配置工具,请单击这里。 最佳实践 RAM和STS使用指南

2019-12-01 23:12:47 0 浏览量 回答数 0

问题

19年BAT常问面试题汇总:JVM+微服务+多线程+锁+高并发性能

游客pklijor6gytpx 2020-01-09 10:31:29 1271 浏览量 回答数 3

问题

基础语言百问-Python

薯条酱 2019-12-01 20:12:27 56807 浏览量 回答数 30

问题

MaxCompute百问集锦(持续更新20171011)

隐林 2019-12-01 20:19:23 38430 浏览量 回答数 18

问题

第6篇 指针数组字符串(下)补充:报错

kun坤 2020-06-08 11:02:03 3 浏览量 回答数 1

问题

大数据时代——数据存储技术百问

yq传送门 2019-12-01 20:27:42 31965 浏览量 回答数 35

问题

你可能不知道的 Python 技巧有哪些?

游客bnlxddh3fwntw 2020-04-13 11:34:27 33 浏览量 回答数 1

问题

2018MySQL技术问答集锦,希望能给喜欢MySQL的同学一些帮助

技术小能手 2019-12-01 19:31:11 1856 浏览量 回答数 0

问题

OpenSearch的搜索处理是什么?

轩墨 2019-12-01 20:57:06 1661 浏览量 回答数 0

回答

首先“缓存”Cache这个东西是干什么的,我们应该先有些基本的了解。要是不太明白的可以看看网上的解释:http://baike.baidu.com/view/907.htm 简单讲,阿里云OCS提供的功能就是提供对热点数据的高速访问。在使用OCS之前(或者在使用任何一种缓存服务之前),我们都应该明白关于缓存的这么几点: 缓存里的数据不是持久化保存的,也就是说它像是电脑里的内存,而不像硬盘;我们不能指望OCS里的数据一直保存不丢失。如果你真的需要存储持久化的数据,也许你应该出门左转找阿里云OSS(开发存储服务); 缓存里存的应该是“热点”数据。遵循常常出现的“20-80法则”,通常程序应用中都有一定比例的数据常常被请求访问,这就是所谓的热点数据,OCS正是为这种数据设计存在的。假定我们的程序中有100个数据,每次访问这些数据的概率完全是均匀分布的1/100,那么使用缓存的效果就不会太好,因为这其中不存在热点数据。 数据逐出。我们可以决定哪些数据是热点数据被放到缓存当中,但是如果我们的缓存容量不够大,这些热点数据中某些最近较少被用到的数据还是会被“挤出去”,这种行为叫做数据逐出。如果想减少出现这种情况,我们可以购买更高容量的OCS。 -------------------------         在开始使用之前,关于阿里云OCS,我们还需要知道以下这些事: 阿里云OCS仅支持阿里云内网访问,不支持公网访问。也就是说,我们用办公室或者家里的电脑(都属于公网)是无法连上阿里云OCS的。为什么会这样呢?因为缓存服务的根本目标是要提供低延迟的高速访问,而从公网电脑来连接OCS服务器的场景下,公网的网络环境是不可控的,可能出现延迟很高甚至断连接的情况,这使得缓存服务无法保证“高速、低延迟”的基本特性,所以阿里云OCS是不支持公网直接访问的。如果觉得高延迟的情况对于我们的应用也能接受,那么我们应该去选择阿里云其他的产品(比如OSS开放存储服务),而不应该选择OCS缓存服务。 阿里云OCS需要与ECS(阿里云服务器)配合使用,而且只能与本地区节点的ECS连通。这一点与上一条相关。OCS只能从阿里云内网访问,也就是说我们只能从阿里云ECS上才能访问并使用OCS服务。所以我们在官网购买OCS的时候,会看到提示信息说需要至少有一台ECS才能买OCS。另外,阿里云ECS是分地区节点的,比如北京、杭州、青岛等,我们在购买OCS缓存的时候也要选相应的地区节点。北京的ECS只能访问北京的OCS,而不能访问杭州或青岛的OCS。 阿里云OCS是按购买量收费的,而不是按使用量收费。这点需要提醒新同学们注意,在我们购买了OCS缓存之后,计费就已经开始了,即使我们还没有真正使用缓存。也就是说,我们买了1G的OCS缓存后,即使目前使用量为0,系统也会按照1G的标准来计费。所以我们在购买OCS的时候,要选取适合我们业务数据需要的缓存档位。当然了,阿里云OCS也提供在线升降缓存容量的功能。也就是说,如果我们在使用了一段时间之后,发现购买的OCS缓存不够用了(或者缓存使用量太低),我们可以在线的对已有的OCS实例进行升档(或者降档),而OCS缓存服务不会被中断。 阿里云OCS对于存贮的对象大小是有限制的。缓存通常对其内部存储的数据尺寸是有限制的,阿里云OCS也一样。目前OCS支持存储的数据对象的上限是1,000,000Byte。如果要存的值超过这个限制,我们应该考虑把数据压缩,或从逻辑上分成不同键存储的几个值。 ------------------------- 现在我们开始在阿里云官网上购买OCS实例  http://buy.aliyun.com/ocs  首先我们需要已经有了一台阿里云ECS,否则我们无法在这个页面成功购买OCS。购买的第一步,我们先要确定选择买哪个地区的OCS;这个很重要,如上面所说,如果我们的ECS是属于北京,而我们在这里购买了杭州的OCS,那么这两者是无法配合协同工作的。所以,在购买OCS的时候一定要选择应用服务器ECS所在地区的OCS。下一步是要选择OCS缓存容量。我们要购买多大的缓存,这个取决于我们对自身业务应用中热点数据总量大小的判断。如果一时难以准确判断数据量,也不用担心:我们可以先买一个大致容量的OCS(比如1GB),随后在使用过程中,通过OCS控制台提供的监控功能,我们可以了解到目前OCS缓存的使用量等数据,然后可以自主的调整所需的缓存量,购买更大的缓存(比如升到5GB)或者减少已购的缓存量(比如降到512MB),阿里云会根据我们选择的新配置来调整对应的收费。此外在选择缓存容量的时候,要知道不同容量的缓存档位对应着不同的性能配额,具体来说包括两个指标:吞吐量带宽与每秒请求处理数(QPS)。比如以现在的配额标准,1GB的OCS缓存对应5MB/sec的吞吐量带宽和3000次/sec的请求处理峰值。当我们使用OCS的时候,如果数据量传输的带宽超过了5MB/s, 或者每秒的请求数超过了3000次,都会触发性能配额控制机制,导致某些请求无法返回正常结果。在确定了地区和缓存容量之后,我们就可以直接下单购买OCS了。 ------------------------- 在成功购买OCS之后,我们的联系邮箱和手机都会收到OCS创建成功的通知,里面会包括OCS的实例ID和初始密码(关于密码的用处后面会讲到)。我们现在登录OCS控制台, http://ocs.console.aliyun.com/ 就可以看到已经购买到的OCS实例列表。在列表页面上对应OCS实例的后面点击“管理”,就可以进入该OCS实例的详情页,看到更多的详细信息。 ------------------------- 我们现在已经有了一个OCS缓存实例,现在是时候试玩OCS了。要使用OCS就要写一点程序代码,不过不用担心,我们在这里采用“Happy-Path”的方法,从最简单的操作开始,让新上手的菜鸟们能马上就有一个能调用OCS缓存服务的程序。OCS提供缓存服务,它并不要求我们的程序是哪种语言来写的。我们这里先以Java程序为例,写一个最简单的“Hello World”。(其他编程语言的例子,我们随后附上。)第一步,登录你的阿里云ECS服务器,在上面安装Java JDK和你常用的IDE(比如Eclipse)。一定要记得我们之前说过的,只有在阿里云内网的ECS服务器上,才能访问我们的OCS实例。所以,用家里或是公司的电脑执行下面的代码示例是看不到结果的。 Java JDK和Eclipse都很容易从网上找到下载,比如 http://download.eclipse.org/ 或者 http://www.onlinedown.net/soft/32289.htm 第二步,在把Java开发环境准备好了之后,下载第一个代码示例(Sample-Code-1第三步,在Eclipse里面打开刚下载的OcsSample1.java,我们要根据自己的OCS实例信息修改几个地方。        我们每个人买到的OCS实例的ID都是不重复的,其对应的阿里云内网地址也是独一无二的,这些信息都在OCS控制台上显示出来。我们在同自己的OCS实例建立连接的时候,需要根据这些信息修改OcsSample1.java中的对应地方。         public static void main(String[] args) {                                        final String host = "b2fd2f89f49f11e3.m.cnqdalicm9pub001.ocs.aliyuncs.com"; //控制台上的“内网地址”                   final String port ="11211";       //默认端口 11211,不用改                   final String username = "b2fd2f89f49f11e3"; //控制台上的“访问账号”                   final String password = "my_password"; //邮件或短信中提供的“密码”                   …… …… ……       信息修改完毕,我们可以运行自己的程序了。运行main函数,我们会在Eclipse下面的console窗口看到下面这样的结果(请忽略可能出现的红色INFO调试信息): OCS Sample CodeSet操作完成!Get操作: Open Cache Service,  from www.Aliyun.com     OK,搞定!我们已经成功的连接上了阿里云的OCS并且调用缓存服务成功,就这么简单。-------------------------我们已经成功运行了第一个调用阿里云OCS缓存服务的Sample程序OcsSample1.java,现在我们看看这个程序里都做了什么。                                  …… …… ……                            System.out.println("OCS Sample Code");                                                        //向OCS中存一个key为"ocs"的数据,便于后面验证读取数据,                             //这个数据对应的value是字符串 Open Cache Service,  from www.Aliyun.com                            OperationFuture future = cache.set("ocs", 1000," Open Cache Service,  from www.Aliyun.com");                            //向OCS中存若干个数据,随后可以在OCS控制台监控上看到统计信息                            for(int i=0;i<100;i++){                                String key="key-"+i;                                String value="value-"+i;                                 //执行set操作,向缓存中存数据                                cache.set(key, 1000, value);                            }                             System.out.println("Set操作完成!");                             future.get();  //  确保之前(cache.set())操作已经结束                         //执行get操作,从缓存中读数据,读取key为"ocs"的数据                            System.out.println("Get操作:"+cache.get("ocs"));                            …… …… …… 从这些代码中可以看出: 1. 我们在建立与OCS缓存服务器的连接后,先是向缓存中存(set)了一个“key-value”(键值对)形式的数据,这个数据的key是字符串“ocs”,其对应的value也是字符串;2. 接着我们继续向缓存中存(set)了100个其他简单的“key-value”数据。3. 最后我们进行功能验证。根据之前给定的key,从缓存中获取(get)其对应的value:也就是输入字符串“ocs”,缓存给我们返回value对应的字符串。 以上的步骤中,1与3是相对应的,我们只有先向缓存中set了某个数据,后面才能从缓存中get到这个数据。步骤2中程序向缓存set了100个数据,是为了从另一个方面进行验证。我们回到阿里云OCS控制台,打开“实例详情”页,在“实例监控”的部分点击刷新,会看到其中一些监控项的值已经发生了变化(注:监控信息的刷新可能存在数秒的延迟), 其中的“Key的个数”已经变成了101,也就是说我们程序已经成功地向OCS缓存中存放了101个数据。-------------------------在写下一篇技术贴之前,列一些OCS用户在入门时问到的问题,方便其他刚认识OCS的同学:Question:买了1G的OCS,那就相当于这个1G是专门缓存用的,与ECS服务器的内存没关系是吧~Answer:是的,OCS的缓存容量与您ECS的内存容量是没关系的。Question:OCS 外网测试,怎么连接?有没有外网连接地址哦?Answer:OCS是不能从外网访问的。参照上面的文章。Question:我之前那个OCS可以正常使用,但现在换了一个OCS就不行了,怎么回事?Answer:经核实您的主机是属于杭州节点的,而现在这个OCS是青岛节点的,不同地域之间的产品内网不互通。Question:在设置一个value时,如果指定过期时间为0,会永久保留吗?Answer:指定过期时间为0,OCS就认为此数据不根据过期时间发生淘汰;但是,此数据仍有可能基于LRU被其他数据淘汰,或者由内存清理造成丢失 ,因此不能认为这个value会永久保留。 Question:对OCS的访问是否需要负载均衡? Answer:不需要。对访问请求的负载均衡都是在OCS服务器端来进行的,用户直接使用缓存服务即可,不用考虑负载均衡的事情。 Question:OCS是否会主动关闭闲置的连接? 如果会,请问连接闲置多久会被关闭?Answer:OCS不会主动关闭闲置的用户连接。但是用户的环境如果使用了SLB,则需要参考SLB连接关闭时间。Question:如何设置数据在OCS缓存中的过期时间 ?Answer:关于设置缓存数据的过期时间,可以参考Memcached官方说明: https://code.google.com/p/memcached/wiki/NewCommands An expiration time, in seconds. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date. 翻译过来就是:0~2592000表示从当前时刻算起的时间长度(以秒计算,最长2592000即30天);大于2592000表示UNIX时间戳。 此值设置为0表明此数据不会主动过期。------------------------- 回 12楼(村里一把手) 的帖子 谢谢,要让大家用得好才算数。 -------------------------缓存与数据库相结合使用,是常见的一种应用搭配场景。现在我们再看一个例子,是用OCS搭配MySQL数据库使用。Java示例代码在此(这个示例代码中,大部分与前几个例子类似。因为要与数据库结合,所以程序需要依赖一个JDBC的jar包才能运行。支持MySQL的JDBC jar包在此(在程序中添加MySQL数据库的连接信息:     …… …… ……            // JDBC driver name and database URL    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";    static final String DB_URL = "jdbc:mysql://xxxxxxx.mysql.rds.aliyuncs.com/testdb"; //MySQL数据库URL        //  Database用户名及密码    static final String DB_USER = "xxxxxx";    static final String DB_PASS = "xxxxxx";            我们设想这样一个场景:我们需要从数据库的tableone表中查找区域不属于北京的记录总数,用SQL表示就是:SELECT count(*)  FROM testdb.tableone where region != 'beijing'假定这个表中的数据如下,则这条SQL查询返回的结果就是7:如果这个查询被调用到的频率很高,多个用户反复不断的在数据库中查这个数据,我们就可以把这个查询结果放到OCS缓存中去。看下面的代码片段,我们用for循环模拟用户连续20次在数据库中查询上述SQL语句:              for (int i = 1; i <= 20; i++) {                String sql = "SELECT count(*)  FROM testdb.tableone where region != 'beijing'";                String key ="non-beijing"; //给SQL语句自定义一个key                //在OCS缓存里按key查找               String value =  (String) cache.get(key);                                if (value == null) {                    // 在OCS缓存里没有命中                    // step 1:从My SQL数据库中查询                    //Load MySQL Driver                      Class.forName(JDBC_DRIVER);                     con = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);                    ps = con.prepareStatement(sql);                    ResultSet result = ps.executeQuery(sql);                    result.next();                                        value=result.getString(1);                    System.out.println("从MySQL中查询数据.  Key= "+key+" Value="+value);                                       // step 2: 把数据库返回的数据作为value存放到OCS缓存中去                    cache.set(key, EXPIRE_TIME, value);                                    } else {                    // 在OCS缓存里命中                    System.out.println("从OCS中读取数据.     Key= "+key+" Value="+value);                }                            }// end of for在这段代码中我们可以看到,我们给这条SQL语句标记了一个key,当有用户要执行这条SQL的时候,我们首先按照key在OCS缓存中查找:如果没有对应的缓存数据,则连接MySQL数据库执行SQL查询,把结果返回给用户,并把这个查询结果存到OCS缓存中去;如果OCS中已经有了对应的缓存数据,则直接把缓存数据返回给用户。运行结果如下: 从MySQL中查询数据.  Key= non-beijing, Value=7从OCS中读取数据.     Key= non-beijing, Value=7从OCS中读取数据.     Key= non-beijing, Value=7从OCS中读取数据.     Key= non-beijing, Value=7…… …… 从结果可以看出,程序第1次是从MySQL数据库当中查询数据,后面的19次都是从OCS缓存中获取key对应的value直接返回。也就是说,OCS降低了程序去连接MySQL数据库执行SQL查询的次数,减轻了对数据库的负载压力。用户对热点数据访问的频率越高,OCS的这种优势就越明显。

唐翰 2019-12-01 23:41:23 0 浏览量 回答数 0

回答

遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么? 遍历方式有以下几种: for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止。 迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。 foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明 Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。 最佳实践:Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支持 Random Access。 如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读取元素的平均时间复杂度为 O(1),如ArrayList。如果没有实现该接口,表示不支持 Random Access,如LinkedList。 推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator 或 foreach 遍历。 说一下 ArrayList 的优缺点 ArrayList的优点如下: ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。ArrayList 在顺序添加一个元素的时候非常方便。 ArrayList 的缺点如下: 删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。插入元素的时候,也需要做一次元素复制操作,缺点同上。 ArrayList 比较适合顺序添加、随机访问的场景。 如何实现数组和 List 之间的转换? 数组转 List:使用 Arrays. asList(array) 进行转换。List 转数组:使用 List 自带的 toArray() 方法。 代码示例: ArrayList 和 LinkedList 的区别是什么? 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全; 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。 补充:数据结构基础之双向链表 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。 ArrayList 和 Vector 的区别是什么? 这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。性能:ArrayList 在性能方面要优于 Vector。扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。 Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。 Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。 插入数据时,ArrayList、LinkedList、Vector谁速度较快?阐述 ArrayList、Vector、LinkedList 的存储性能和特性? ArrayList、LinkedList、Vector 底层的实现都是使用数组方式存储数据。数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。 Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差。 LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快。 多线程场景下如何使用 ArrayList? ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样: 为什么 ArrayList 的 elementData 加上 transient 修饰? ArrayList 中的数组定义如下: private transient Object[] elementData; 再看一下 ArrayList 的定义: public class ArrayList extends AbstractList implements List<E>, RandomAccess, Cloneable, java.io.Serializable 可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject 实现: 每次序列化时,先调用 defaultWriteObject() 方法序列化 ArrayList 中的非 transient 元素,然后遍历 elementData,只序列化已存入的元素,这样既加快了序列化的速度,又减小了序列化之后的文件大小。 List 和 Set 的区别 List , Set 都是继承自Collection 接口 List 特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。 Set 特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。 另外 List 支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。 Set和List对比 Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变 Set接口 说一下 HashSet 的实现原理? HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为PRESENT,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。 HashSet如何检查重复?HashSet是如何保证数据不可重复的? 向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。 HashSet 中的add ()方法会使用HashMap 的put()方法。 HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复( HashMap 比较key是否相等是先比较hashcode 再比较equals )。 以下是HashSet 部分源码: hashCode()与equals()的相关规定: 如果两个对象相等,则hashcode一定也是相同的 两个对象相等,对两个equals方法返回true 两个对象有相同的hashcode值,它们也不一定是相等的 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 ** ==与equals的区别** ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 ==是指对内存地址进行比较 equals()是对字符串的内容进行比较3.==指引用是否相同 equals()指的是值是否相同 HashSet与HashMap的区别 Queue BlockingQueue是什么? Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。 在 Queue 中 poll()和 remove()有什么区别? 相同点:都是返回第一个元素,并在队列中删除返回的对象。 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。 代码示例: Queue queue = new LinkedList (); queue. offer("string"); // add System. out. println(queue. poll()); System. out. println(queue. remove()); System. out. println(queue. size()); Map接口 说一下 HashMap 的实现原理? HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 HashMap的数据结构: 在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 HashMap 基于 Hash 算法实现的 当我们往Hashmap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。 需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn) HashMap在JDK1.7和JDK1.8中有哪些不同?HashMap的底层实现 在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法的方式可以解决哈希冲突。 JDK1.8之前 JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 JDK1.8之后 相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 JDK1.7 VS JDK1.8 比较 JDK1.8主要解决或优化了一下问题: resize 扩容优化引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。 HashMap的put方法的具体流程? 当我们put的时候,首先计算 key的hash值,这里调用了 hash方法,hash方法实际是让key.hashCode()与key.hashCode()>>>16进行异或操作,高16bit补0,一个数和0异或不变,所以 hash 函数大概的作用就是:高16bit不变,低16bit和高16bit做了一个异或,目的是减少碰撞。按照函数注释,因为bucket数组大小是2的幂,计算下标index = (table.length - 1) & hash,如果不做 hash 处理,相当于散列生效的只有几个低 bit 位,为了减少散列的碰撞,设计者综合考虑了速度、作用、质量之后,使用高16bit和低16bit异或来简单处理减少碰撞,而且JDK8中用了复杂度 O(logn)的树结构来提升碰撞下的性能。 putVal方法执行流程图 ①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; ②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; ③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; ④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤; ⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; ⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。 HashMap的扩容操作是怎么实现的? ①.在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容; ②.每次扩展的时候,都是扩展2倍; ③.扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。 在putVal()中,我们看到在这个函数里面使用到了2次resize()方法,resize()方法表示的在进行第一次初始化时会对其进行扩容,或者当该数组的实际大小大于其临界值值(第一次为12),这个时候在扩容的同时也会伴随的桶上面的元素进行重新分发,这也是JDK1.8版本的一个优化的地方,在1.7中,扩容之后需要重新去计算其Hash值,根据Hash值对其进行分发,但在1.8版本中,则是根据在同一个桶的位置中进行判断(e.hash & oldCap)是否为0,重新进行hash分配后,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上 HashMap是怎么解决哈希冲突的? 答:在解决这个问题之前,我们首先需要知道什么是哈希冲突,而在了解哈希冲突之前我们还要知道什么是哈希才行; 什么是哈希? Hash,一般翻译为“散列”,也有直接音译为“哈希”的,这就是把任意长度的输入通过散列算法,变换成固定长度的输出,该输出就是散列值(哈希值);这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。 所有散列函数都有如下一个基本特性**:根据同一散列函数计算出的散列值如果不同,那么输入值肯定也不同。但是,根据同一散列函数计算出的散列值如果相同,输入值不一定相同**。 什么是哈希冲突? 当两个不同的输入值,根据同一散列函数计算出相同的散列值的现象,我们就把它叫做碰撞(哈希碰撞)。 HashMap的数据结构 在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做链地址法的方式可以解决哈希冲突: 这样我们就可以将拥有相同哈希值的对象组织成一个链表放在hash值所对应的bucket下,但相比于hashCode返回的int类型,我们HashMap初始的容量大小DEFAULT_INITIAL_CAPACITY = 1 << 4(即2的四次方16)要远小于int类型的范围,所以我们如果只是单纯的用hashCode取余来获取对应的bucket这将会大大增加哈希碰撞的概率,并且最坏情况下还会将HashMap变成一个单链表,所以我们还需要对hashCode作一定的优化 hash()函数 上面提到的问题,主要是因为如果使用hashCode取余,那么相当于参与运算的只有hashCode的低位,高位是没有起到任何作用的,所以我们的思路就是让hashCode取值出的高位也参与运算,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这样的操作称为扰动,在JDK 1.8中的hash()函数如下: static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);// 与自己右移16位进行异或运算(高低位异或) } 这比在JDK 1.7中,更为简洁,相比在1.7中的4次位运算,5次异或运算(9次扰动),在1.8中,只进行了1次位运算和1次异或运算(2次扰动); JDK1.8新增红黑树 通过上面的链地址法(使用散列表)和扰动函数我们成功让我们的数据分布更平均,哈希碰撞减少,但是当我们的HashMap中存在大量数据时,加入我们某个bucket下对应的链表有n个元素,那么遍历时间复杂度就为O(n),为了针对这个问题,JDK1.8在HashMap中新增了红黑树的数据结构,进一步使得遍历复杂度降低至O(logn); 总结 简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的: 使用链地址法(使用散列表)来链接拥有相同hash值的数据;使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;引入红黑树进一步降低遍历的时间复杂度,使得遍历更快; **能否使用任何类作为 Map 的 key? **可以使用任何类作为 Map 的 key,然而在使用之前,需要考虑以下几点: 如果类重写了 equals() 方法,也应该重写 hashCode() 方法。 类的所有实例需要遵循与 equals() 和 hashCode() 相关的规则。 如果一个类没有使用 equals(),不应该在 hashCode() 中使用它。 用户自定义 Key 类最佳实践是使之为不可变的,这样 hashCode() 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode() 和 equals() 在未来不会改变,这样就会解决与可变相关的问题了。 为什么HashMap中String、Integer这样的包装类适合作为K? 答:String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率 都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况 内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范(不清楚可以去上面看看putValue的过程),不容易出现Hash值计算错误的情况; 如果使用Object作为HashMap的Key,应该怎么办呢? 答:重写hashCode()和equals()方法 重写hashCode()是因为需要计算存储数据的存储位置,需要注意不要试图从散列码计算中排除掉一个对象的关键部分来提高性能,这样虽然能更快但可能会导致更多的Hash碰撞; 重写equals()方法,需要遵守自反性、对称性、传递性、一致性以及对于任何非null的引用值x,x.equals(null)必须返回false的这几个特性,目的是为了保证key在哈希表中的唯一性; HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标 答:hashCode()方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置; 那怎么解决呢? HashMap自己实现了自己的hash()方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均; 在保证数组长度为2的幂次方的时候,使用hash()运算之后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操作更加有效率,二来也是因为只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题; HashMap 的长度为什么是2的幂次方 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。 这个算法应该如何设计呢? 我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。 那为什么是两次扰动呢? 答:这样就是加大哈希值低位的随机性,使得分布更均匀,从而提高对应数组存储下标位置的随机性&均匀性,最终减少Hash冲突,两次就够了,已经达到了高位低位同时参与运算的目的; HashMap 与 HashTable 有什么区别? 线程安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!); 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它; 对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException。 **初始容量大小和每次扩充容量大小的不同 **: ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。 推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。 如何决定使用 HashMap 还是 TreeMap? 对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。 HashMap 和 ConcurrentHashMap 的区别 ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。) HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。 ConcurrentHashMap 和 Hashtable 的区别? ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; 实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。 两者的对比图: HashTable: JDK1.7的ConcurrentHashMap: JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点): 答:ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。HashMap 没有考虑同步,HashTable 考虑了同步的问题。但是 HashTable 在每次同步执行时都要锁住整个结构。 ConcurrentHashMap 锁的方式是稍微细粒度的。 ConcurrentHashMap 底层具体实现知道吗?实现原理是什么? JDK1.7 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 在JDK1.7中,ConcurrentHashMap采用Segment + HashEntry的方式进行实现,结构如下: 一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。 该类包含两个静态内部类 HashEntry 和 Segment ;前者用来封装映射表的键值对,后者用来充当锁的角色;Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。 JDK1.8 在JDK1.8中,放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。 结构如下: 如果该节点是TreeBin类型的节点,说明是红黑树结构,则通过putTreeVal方法往红黑树中插入节点;如果binCount不为0,说明put操作对数据产生了影响,如果当前链表的个数达到8个,则通过treeifyBin方法转化为红黑树,如果oldVal不为空,说明是一次更新操作,没有对元素个数产生影响,则直接返回旧值;如果插入的是一个新节点,则执行addCount()方法尝试更新元素个数baseCount; 辅助工具类 Array 和 ArrayList 有何区别? Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。 如何实现 Array 和 List 之间的转换? Array 转 List: Arrays. asList(array) ;List 转 Array:List 的 toArray() 方法。 comparable 和 comparator的区别? comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用来排序 一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort(). 方法如何比较元素? TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进 行排 序。 Collections 工具类的 sort 方法有两种重载的形式, 第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较; 第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。

剑曼红尘 2020-03-24 14:41:57 0 浏览量 回答数 0

回答

流处理,听起来很高大上啊,其实就是分块读取。有这么一些情况,有一个很大的几个G的文件,没办法一次处理,那么就分批次处理,一次处理1百万行,接着处理下1百万行,慢慢地总是能处理完的。 使用类似迭代器的方式 data=pd.read_csv(file, chunksize=1000000)for sub_df in data: print('do something in sub_df here') 1234索引 Series和DataFrame都是有索引的,索引的好处是快速定位,在涉及到两个Series或DataFrame时可以根据索引自动对齐,比如日期自动对齐,这样可以省去很多事。 缺失值 pd.isnull(obj)obj.isnull()12将字典转成数据框,并赋予列名,索引 DataFrame(data, columns=['col1','col2','col3'...], index = ['i1','i2','i3'...]) 12查看列名 DataFrame.columns 查看索引 DataFrame.index 重建索引 obj.reindex(['a','b','c','d','e'...], fill_value=0] 按给出的索引顺序重新排序,而不是替换索引。如果索引没有值,就用0填充 就地修改索引 data.index=data.index.map(str.upper)12345列顺序重排(也是重建索引) DataFrame.reindex[columns=['col1','col2','col3'...])` 也可以同时重建index和columns DataFrame.reindex[index=['a','b','c'...],columns=['col1','col2','col3'...])12345重建索引的快捷键 DataFrame.ix[['a','b','c'...],['col1','col2','col3'...]]1重命名轴索引 data.rename(index=str.title,columns=str.upper) 修改某个索引和列名,可以通过传入字典 data.rename(index={'old_index':'new_index'}, columns={'old_col':'new_col'}) 12345查看某一列 DataFrame['state'] 或 DataFrame.state1查看某一行 需要用到索引 DataFrame.ix['index_name']1添加或删除一列 DataFrame['new_col_name'] = 'char_or_number' 删除行 DataFrame.drop(['index1','index2'...]) 删除列 DataFrame.drop(['col1','col2'...],axis=1) 或 del DataFrame['col1']1234567DataFrame选择子集 类型 说明obj[val] 选择一列或多列obj.ix[val] 选择一行或多行obj.ix[:,val] 选择一列或多列obj.ix[val1,val2] 同时选择行和列reindx 对行和列重新索引icol,irow 根据整数位置选取单列或单行get_value,set_value 根据行标签和列标签选择单个值针对series obj[['a','b','c'...]]obj['b':'e']=512针对dataframe 选择多列 dataframe[['col1','col2'...]] 选择多行 dataframe[m:n] 条件筛选 dataframe[dataframe['col3'>5]] 选择子集 dataframe.ix[0:3,0:5]1234567891011dataframe和series的运算 会根据 index 和 columns 自动对齐然后进行运算,很方便啊 方法 说明add 加法sub 减法div 除法mul 乘法 没有数据的地方用0填充空值 df1.add(df2,fill_value=0) dataframe 与 series 的运算 dataframe - series 规则是: -------- v 指定轴方向 dataframe.sub(series,axis=0)规则是:-------- --- | | | | ----->| | | | | | | | | | | | -------- ---12345678910111213141516171819202122apply函数 f=lambda x:x.max()-x.min() 默认对每一列应用 dataframe.apply(f) 如果需要对每一行分组应用 dataframe.apply(f,axis=1)1234567排序和排名 默认根据index排序,axis = 1 则根据columns排序 dataframe.sort_index(axis=0, ascending=False) 根据值排序 dataframe.sort_index(by=['col1','col2'...]) 排名,给出的是rank值 series.rank(ascending=False) 如果出现重复值,则取平均秩次 在行或列上面的排名 dataframe.rank(axis=0)12345678910111213描述性统计 方法 说明count 计数describe 给出各列的常用统计量min,max 最大最小值argmin,argmax 最大最小值的索引位置(整数)idxmin,idxmax 最大最小值的索引值quantile 计算样本分位数sum,mean 对列求和,均值mediam 中位数mad 根据平均值计算平均绝对离差var,std 方差,标准差skew 偏度(三阶矩)Kurt 峰度(四阶矩)cumsum 累积和Cummins,cummax 累计组大致和累计最小值cumprod 累计积diff 一阶差分pct_change 计算百分数变化唯一值,值计数,成员资格 obj.unique()obj.value_count()obj.isin(['b','c'])123处理缺失值 过滤缺失值 只要有缺失值就丢弃这一行 dataframe.dropna() 要求全部为缺失才丢弃这一行 dataframe.dropna(how='all') 根据列来判断 dataframe.dropna(how='all',axis=1) 填充缺失值 1.用0填充 df.fillna(0) 2.不同的列用不同的值填充 df.fillna({1:0.5, 3:-1}) 3.用均值填充 df.fillna(df.mean()) 此时axis参数同前面, 123456789101112131415161718192021将列转成行索引 df.set_index(['col1','col2'...])1数据清洗,重塑 合并数据集 取 df1,df2 都有的部分,丢弃没有的 默认是inner的连接方式 pd.merge(df1,df2, how='inner') 如果df1,df2的连接字段名不同,则需要特别指定 pd.merge(df1,df2,left_on='l_key',right_on='r_key') 其他的连接方式有 left,right, outer等。 如果dataframe是多重索引,根据多个键进行合并 pd.merge(left, right, on=['key1','key2'],how = 'outer') 合并后如果有重复的列名,需要添加后缀 pd.merge(left, right, on='key1', suffixes=('_left','_right'))1234567891011121314索引上的合并 针对dataframe中的连接键不是列名,而是索引名的情况。 pd.merge(left, right, left_on = 'col_key', right_index=True) 即左边的key是列名,右边的key是index。 多重索引 pd.merge(left, right, left_on=['key1','key2'], right_index=True)123456dataframe的join方法 实现按索引合并。 其实这个join方法和数据库的join函数是以一样的理解 left.join(right, how='outer') 一次合并多个数据框 left.join([right1,right2],how='outer')123456轴向连接(更常用) 连接:concatenation 绑定:binding 堆叠:stacking列上的连接 np.concatenation([df1,df2],axis=1) #np包pd.concat([df1,df2], axis=1) #pd包 和R语言中的 cbind 是一样的 如果axis=0,则和 rbind 是一样的 索引对齐,没有的就为空 join='inner' 得到交集 pd.concat([df1,df2], axis=1, join='innner') keys 参数,还没看明白 ignore_index=True,如果只是简单的合并拼接而不考虑索引问题。 pd.concat([df1,df2],ignore_index=True)123456789101112131415合并重复数据 针对可能有索引全部或者部分重叠的两个数据集 填充因为合并时索引赵成的缺失值 where函数 where即if-else函数 np.where(isnull(a),b,a)12combine_first方法 如果a中值为空,就用b中的值填补 a[:-2].combine_first(b[2:]) combine_first函数即对数据打补丁,用df2的数据填充df1中的缺失值 df1.combine_first(df2)12345重塑层次化索引 stact:将数据转为长格式,即列旋转为行 unstack:转为宽格式,即将行旋转为列result=data.stack()result.unstack()12长格式转为宽格式 pivoted = data.pivot('date','item','value') 前两个参数分别是行和列的索引名,最后一个参数则是用来填充dataframe的数据列的列名。如果忽略最后一个参数,得到的dataframe会带有层次化的列。 123透视表 table = df.pivot_table(values=["Price","Quantity"], index=["Manager","Rep"], aggfunc=[np.sum,np.mean], margins=True)) values:需要对哪些字段应用函数 index:透视表的行索引(row) columns:透视表的列索引(column) aggfunc:应用什么函数 fill_value:空值填充 margins:添加汇总项 然后可以对透视表进行筛选 table.query('Manager == ["Debra Henley"]')table.query('Status == ["pending","won"]')123456789101112131415移除重复数据 判断是否重复 data.duplicated()` 移除重复数据 data.drop_duplicated() 对指定列判断是否存在重复值,然后删除重复数据 data.drop_duplicated(['key1'])123456789交叉表 是一种用于计算分组频率的特殊透视表. 注意,只对离散型的,分类型的,字符型的有用,连续型数据是不能计算频率这种东西的。 pd.crosstab(df.col1, df.col2, margins=True)1类似vlookup函数 利用函数或映射进行数据转换 1.首先定义一个字典 meat_to_animal={ 'bacon':'pig', 'pulled pork':'pig', 'honey ham':'cow' } 2.对某一列应用一个函数,或者字典,顺便根据这一列的结果创建新列 data['new_col']=data['food'].map(str.lower).map(meat_to_animal)123456789替换值 data.replace(-999,np.na) 多个值的替换 data.replace([-999,-1000],np.na) 对应替换 data.replace([-999,-1000],[np.na,0]) 对应替换也可以传入一个字典 data.replace({-999:np.na,-1000:0})123456789离散化 定义分割点 简单分割(等宽分箱) s=pd.Series(range(100))pd.cut(s, bins=10, labels=range(10)) bins=[20,40,60,80,100] 切割 cats = pd.cut(series,bins) 查看标签 cats.labels 查看水平(因子) cats.levels 区间计数 pd.value_count(cats) 自定义分区的标签 group_names=['youth','youngAdult','MiddleAge','Senior']pd.cut(ages,bins,labels=group_names)1234567891011121314151617181920212223分位数分割 data=np.random.randn(1000)pd.qcut(data,4) #四分位数 自定义分位数,包含端点 pd.qcut(data,[0,0.3,0.5,0.9,1])12345异常值 查看各个统计量 data.describe() 对某一列 col=data[3]col[np.abs(col)>3] 选出全部含有“超过3或-3的值的行 data[(np.abs(data)>3).any(1)] 异常值替换 data[np.abs(data)>3]=np.sign(data)*312345678910111213抽样 随机抽取k行 df.take(np.random.permutation(len(df))[:k]) 随机抽取k行,但是k可能大于df的行数 可以理解为过抽样了 df.take(np.random.randint(0,len(df),size=k))1234567数据摊平处理 相当于将类别属性转成因子类型,比如是否有车,这个字段有3个不同的值,有,没有,过段时间买,那么将会被编码成3个字段,有车,没车,过段时间买车,每个字段用0-1二值填充变成数值型。 对摊平的数据列增加前缀 dummies = pd.get_dummies(df['key'],prefix='key') 将摊平产生的数据列拼接回去 df[['data1']].join(dummies)12345字符串操作 拆分 strings.split(',') 根据正则表达式切分 re.split('s+',strings) 连接 'a'+'b'+'c'...或者'+'.join(series) 判断是否存在 's' in strings`strings.find('s') 计数 strings.count(',') 替换 strings.replace('old','new') 去除空白字符 s.strip()12345678910111213141516171819202122232425正则表达式 正则表达式需要先编译匹配模式,然后才去匹配查找,这样能节省大量的CPU时间。 re.complie:编译 findall:匹配所有 search:只返回第一个匹配项的起始和结束地址 match:值匹配字符串的首部 sub:匹配替换,如果找到就替换 原始字符串 strings = 'sdf@153.com,dste@qq.com,sor@gmail.com' 编译匹配模式,IGNORECASE可以在使用的时候对大小写不敏感 pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'regex = re.compile(pattern,flags=re.IGNORECASE) 匹配所有 regex.findall(strings) 使用search m = regex.search(strings) #获取匹配的地址strings[m.start():m.end()] 匹配替换 regex.sub('new_string', strings)12345678910111213141516根据模式再切分 将模式切分,也就是将匹配到的进一步切分,通过pattern中的括号实现. pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'regex = re.compile(pattern)regex.findall(strings) 如果使用match m=regex.match(string)m.groups() 效果是这样的 suzyu123@163.com --> [(suzyu123, 163, com)] 获取 list-tuple 其中的某一列 matches.get(i)12345678910111213分组聚合,计算 group_by技术 根据多个索引分组,然后计算均值 means = df['data1'].groupby([df['index1'],df['index2']).mean() 展开成透视表格式 means.unstack()12345分组后价将片段做成一个字典 pieces = dict(list(df.groupby('index1'))) pieces['b']123groupby默认是对列(axis=0)分组,也可以在行(axis=1)上分组 语法糖,groupby的快捷函数 df.groupby('index1')['col_names']df.groupby('index1')[['col_names']] 是下面代码的语法糖 df['col_names'].groupby(df['index1']) df.groupby(['index1','index2'])['col_names'].mean()1234567通过字典或series进行分组 people = DataFrame(np.random.randn(5, 5), columns=['a', 'b', 'c', 'd', 'e'], index=['Joe', 'Steve', 'Wes', 'Jim','Travis']) 选择部分设为na people.ix[2:3,['b','c']]=np.na mapping = {'a': 'red', 'b': 'red', 'c': 'blue', 'd': 'blue', 'e': 'red', 'f' : 'orange'} people.groupby(mapping,axis=1).sum()1234567891011通过函数进行分组 根据索引的长度进行分组 people.groupby(len).sum()12数据聚合 使用自定义函数 对所有的数据列使用自定义函数 df.groupby('index1').agg(myfunc) 使用系统函数 df.groupby('index1')['data1']describe()12345根据列分组应用多个函数 分组 grouped = df.groupby(['col1','col2']) 选择多列,对每一列应用多个函数 grouped['data1','data2'...].agg(['mean','std','myfunc'])12345对不同列使用不同的函数 grouped = df.groupby(['col1','col2']) 传入一个字典,对不同的列使用不同的函数 不同的列可以应用不同数量的函数 grouped.agg({'data1':['min','max','mean','std'], 'data2':'sum'}) 123456分组计算后重命名列名 grouped = df.groupby(['col1','col2']) grouped.agg({'data1':[('min','max','mean','std'),('d_min','d_max','d_mean','d_std')], 'data2':'sum'}) 1234返回的聚合数据不要索引 df.groupby(['sex','smoker'], as_index=False).mean()1分组计算结果添加前缀 对计算后的列名添加前缀 df.groupby('index1').mean().add_prefix('mean_')12将分组计算后的值替换到原数据框 将函数应用到各分组,再将分组计算的结果代换原数据框的值 也可以使用自定义函数 df.groupby(['index1','index2'...]).transform(np.mean)123更一般化的apply函数 df.groupby(['col1','col2'...]).apply(myfunc) df.groupby(['col1','col2'...]).apply(['min','max','mean','std'])123禁用分组键 分组键会跟原始对象的索引共同构成结果对象中的层次化索引 df.groupby('smoker', group_keys=False).apply(mean)1分组索引转成df的列 某些情况下,groupby的as_index=False参数并没有什么用,得到的还是一个series,这种情况一般是尽管分组了,但是计算需要涉及几列,最后得到的还是series,series的index是层次化索引。这里将series转成dataframe,series的层次化索引转成dataframe的列。 def fmean(df): """需要用两列才能计算最后的结果""" skus=len(df['sku'].unique()) sums=df['salecount'].sum() return sums/skus 尽管禁用分组键,得到的还是series salemean=data.groupby(by=['season','syear','smonth'],as_index=False).apply(fmean) 将series转成dataframe,顺便设置索引 sub_df = pd.DataFrame(salemean.index.tolist(),columns=salemean.index.names,index=salemean.index) 将groupby的结果和sub_df合并 sub_df['salemean']=salemean12345678910111213桶分析与分位数 对数据切分段,然后对每一分段应用函数 frame = DataFrame({'col1':np.random.randn(1000), 'col2':np.random.randn(1000)}) 数据分段,创建分段用的因子 返回每一元素是属于哪一分割区间 factor = pd.cut(frame.col1, 4) 分组计算,然后转成数据框形式 grouped = frame.col2.groupby(factor)grouped.apply(myfunc).unstack()12345678910用分组的均值填充缺失值 自定义函数 fill_mean= lambda x:x.fillna(x.mean()) 分组填充 df.groupby(group_key).apply(fill_mean)12345分组后不同的数据替换不同的值 定义字典 fill_value = {'east':0.5, 'west':-1} 定义函数 fill_func = lambda x:x.fillna(fill_value(x.name)) 分组填充 df.groupby(['index1','index2'...]).apply(fill_func)12345678sql操作 有时候觉得pandas很方便,但是有时候却很麻烦,不如SQL方便。因此pandas中也有一些例子,用pandas实现SQL的功能,简单的就不说了,下面说些复杂点的操作。 之所以说这个复杂的语句,是因为不想将这些数据操作分写在不同的语句中,而是从头到尾连续编码实现一个功能。 SQL复杂操作用到的主要函数是assign,简单说其实和join的功能是一样的,根据df1,df2的索引值来将df2拼接到df1上。 两个函数是query,也听方便的。 有一批销量数据,筛选出那些有2个月以上的销量产品的数据,说白了就是剔除那些新上市产品的数据 方法是先统计每个产品的数据量,然后选出那些数据量>2的产品,再在数据表中选择这些产品 sku smonth a 1 a 2 a 3 a 4 b 5 b 6 b 7 b 8 c 9 c 10 按sku分组,统计smonth的次数,拼接到salecount中,然后查询cnt>2的 salecount.assign(cnt=salecount.groupby(['sku'])['smonth'].count()).query('cnt>2')

xuning715 2019-12-02 01:10:39 0 浏览量 回答数 0

问题

【精品问答】Python二级考试题库

珍宝珠 2019-12-01 22:03:38 1146 浏览量 回答数 2

回答

简介 ES是一个基于RESTful web接口并且构建在Apache Lucene之上的开源分布式搜索引擎。 同时ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向扩展至数以百计的服务器存储以及处理PB级的数据。 可以在极短的时间内存储、搜索和分析大量的数据。通常作为具有复杂搜索场景情况下的核心发动机。 ES就是为高可用和可扩展而生的。一方面可以通过升级硬件来完成系统扩展,称为垂直或向上扩展(Vertical Scale/Scaling Up)。 另一方面,增加更多的服务器来完成系统扩展,称为水平扩展或者向外扩展(Horizontal Scale/Scaling Out)。尽管ES能够利用更强劲的硬件,但是垂直扩展毕竟还是有它的极限。真正的可扩展性来自于水平扩展,通过向集群中添加更多的节点来分担负载,增加可靠性。ES天生就是分布式的,它知道如何管理多个节点来完成扩展和实现高可用性。意味应用不需要做任何的改动。 Gateway,代表ES索引的持久化存储方式。在Gateway中,ES默认先把索引存储在内存中,然后当内存满的时候,再持久化到Gateway里。当ES集群关闭或重启的时候,它就会从Gateway里去读取索引数据。比如LocalFileSystem和HDFS、AS3等。 DistributedLucene Directory,它是Lucene里的一些列索引文件组成的目录。它负责管理这些索引文件。包括数据的读取、写入,以及索引的添加和合并等。 River,代表是数据源。是以插件的形式存在于ES中。  Mapping,映射的意思,非常类似于静态语言中的数据类型。比如我们声明一个int类型的变量,那以后这个变量只能存储int类型的数据。比如我们声明一个double类型的mapping字段,则只能存储double类型的数据。 Mapping不仅是告诉ES,哪个字段是哪种类型。还能告诉ES如何来索引数据,以及数据是否被索引到等。 Search Moudle,搜索模块,支持搜索的一些常用操作 Index Moudle,索引模块,支持索引的一些常用操作 Disvcovery,主要是负责集群的master节点发现。比如某个节点突然离开或进来的情况,进行一个分片重新分片等。这里有个发现机制。 发现机制默认的实现方式是单播和多播的形式,即Zen,同时也支持点对点的实现。另外一种是以插件的形式,即EC2。 Scripting,即脚本语言。包括很多,这里不多赘述。如mvel、js、python等。    Transport,代表ES内部节点,代表跟集群的客户端交互。包括 Thrift、Memcached、Http等协议 RESTful Style API,通过RESTful方式来实现API编程。 3rd plugins,代表第三方插件。 Java(Netty),是开发框架。 JMX,是监控。 使用案例 1、将ES作为网站的主要后端系统 比如现在搭建一个博客系统,对于博客帖子的数据可以直接在ES上存储,并且使用ES来进行检索,统计。ES提供了持久化的存储、统计和很多其他数据存储的特性。 注意:但是像其他的NOSQL数据存储一样,ES是不支持事务的,如果要事务机制,还是考虑使用其他的数据库做真实库。 2、将ES添加到现有系统 有些时候不需要ES提供所有数据的存储功能,只是想在一个数据存储的基础之上使用ES。比如已经有一个复杂的系统在运行,但是现在想加一个搜索的功能,就可以使用该方案。 3、将ES作为现有解决方案的后端部分 因为ES是开源的系统,提供了直接的HTTP接口,并且现在有一个大型的生态系统在支持他。比如现在我们想部署大规模的日志框架、用于存储、搜索和分析海量的事件,考虑到现有的工具可以写入和读取ES,可以不需要进行任何开发,配置这些工具就可以去运作。 设计结构 1、逻辑设计 文档 文档是可以被索引的信息的基本单位,它包含几个重要的属性: 是自我包含的。一篇文档同时包含字段和他们的取值。 是层次型的。文档中还可以包含新的文档,一个字段的取值可以是简单的,例如location字段的取值可以是字符串,还可以包含其他字段和取值,比如可以同时包含城市和街道地址。 拥有灵活的结构。文档不依赖于预先定义的模式。也就是说并非所有的文档都需要拥有相同的字段,并不受限于同一个模式 {   "name":"meeting",   "location":"office",   "organizer":"yanping" } {   "name":"meeting",   "location":{     "name":"sheshouzuo",        "date":"2019-6-28"   },   "memebers":["leio","shiyi"] } 类型 类型是文档的逻辑容器,类似于表格是行的容器。在不同的类型中,最好放入不同的结构的文档。 字段 ES中,每个文档,其实是以json形式存储的。而一个文档可以被视为多个字段的集合。 映射 每个类型中字段的定义称为映射。例如,name字段映射为String。 索引 索引是映射类型的容器一个ES的索引非常像关系型世界中的数据库,是独立的大量文档集合。   关系型数据库与ES的结构上的对比 2、物理设计 节点 一个节点是一个ES的实例,在服务器上启动ES之后,就拥有了一个节点,如果在另一个服务器上启动ES,这就是另一个节点。甚至可以在一台服务器上启动多个ES进程,在一台服务器上拥有多个节点。多个节点可以加入同一个集群。 当ElasticSearch的节点启动后,它会利用多播(multicast)(或者单播,如果用户更改了配置)寻找集群中的其它节点,并与之建立连接。这个过程如下图所示: 节点主要有3种类型,第一种类型是client_node,主要是起到请求分发的作用,类似路由。第二种类型是master_node,是主的节点,所有的新增,删除,数据分片都是由主节点操作(elasticsearch底层是没有更新数据操作的,上层对外提供的更新实际上是删除了再新增),当然也能承担搜索操作。第三种类型是date_node,该类型的节点只能做搜索操作,具体会分配到哪个date_node,就是由client_node决定,而data_node的数据都是从master_node同步过来的 分片 一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。   为了解决这个问题,ES提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。 分片之所以重要,主要有两方面的原因:   1、允许你水平分割/扩展你的内容容量 允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量 至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由ES管理的,对于作为用户的你来说,这些都是透明的。   2、在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了。这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,ES允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。 复制之所以重要,主要有两方面的原因: (1)在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。 (2)扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行 总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制数量,但是不能改变分片的数量。   默认情况下,ES中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。一个索引的多个分片可以存放在集群中的一台主机上,也可以存放在多台主机上,这取决于你的集群机器数量。主分片和复制分片的具体位置是由ES内在的策略所决定的。 3、插件HEAD elasticsearch-head是一个界面化的集群操作和管理工具 ● node:即一个 Elasticsearch 的运行实例,使用多播或单播方式发现 cluster 并加入。 ● cluster:包含一个或多个拥有相同集群名称的 node,其中包含一个master node。 ● index:类比关系型数据库里的DB,是一个逻辑命名空间。 ● alias:可以给 index 添加零个或多个alias,通过 alias 使用index 和根据index name 访问index一样,但是,alias给我们提供了一种切换index的能力,比如重建了index,取名● customer_online_v2,这时,有了alias,我要访问新 index,只需要把 alias 添加到新 index 即可,并把alias从旧的 index 删除。不用修改代码。 ● type:类比关系数据库里的Table。其中,一个index可以定义多个type,但一般使用习惯仅配一个type。 ● mapping:类比关系型数据库中的 schema 概念,mapping 定义了 index 中的 type。mapping 可以显示的定义,也可以在 document 被索引时自动生成,如果有新的 field,Elasticsearch 会自动推测出 field 的type并加到mapping中。 ● document:类比关系数据库里的一行记录(record),document 是 Elasticsearch 里的一个 JSON 对象,包括零个或多个field。 ● field:类比关系数据库里的field,每个field 都有自己的字段类型。 ● shard:是一个Lucene 实例。Elasticsearch 基于 Lucene,shard 是一个 Lucene 实例,被 Elasticsearch 自动管理。之前提到,index 是一个逻辑命名空间,shard 是具体的物理概念,建索引、查询等都是具体的shard在工作。shard 包括primary shard 和 replica shard,写数据时,先写到primary shard,然后,同步到replica shard,查询时,primary 和 replica 充当相同的作用。replica shard 可以有多份,也可以没有,replica shard的存在有两个作用,一是容灾,如果primary shard 挂了,数据也不会丢失,集群仍然能正常工作;二是提高性能,因为replica 和 primary shard 都能处理查询。另外,如上图右侧红框所示,shard数和replica数都可以设置,但是,shard 数只能在建立index 时设置,后期不能更改,但是,replica 数可以随时更改。但是,由于 Elasticsearch 很友好的封装了这部分,在使用Elasticsearch 的过程中,我们一般仅需要关注 index 即可,不需关注shard。   shard、node、cluster 在物理上构成了 Elasticsearch 集群,field、type、index 在逻辑上构成一个index的基本概念,在使用 Elasticsearch 过程中,我们一般关注到逻辑概念就好,就像我们在使用MySQL 时,我们一般就关注DB Name、Table和schema即可,而不会关注DBA维护了几个MySQL实例、master 和 slave 等怎么部署的一样。 ES中的索引原理 (1)传统的关系型数据库 二叉树查找效率是logN,同时插入新的节点不必移动全部节点,所以用树型结构存储索引,能同时兼顾插入和查询的性能。因此在这个基础上,再结合磁盘的读取特性(顺序读/随机读),传统关系型数据库采用了B-Tree/B+Tree这样的数据结构做索引 (2)ES 采用倒排索引 那么,倒排索引是个什么样子呢? 首先,来搞清楚几个概念,为此,举个例子: 假设有个user索引,它有四个字段:分别是name,gender,age,address。画出来的话,大概是下面这个样子,跟关系型数据库一样 Term(单词):一段文本经过分析器分析以后就会输出一串单词,这一个一个的就叫做Term Term Dictionary(单词字典):顾名思义,它里面维护的是Term,可以理解为Term的集合 Term Index(单词索引):为了更快的找到某个单词,我们为单词建立索引 Posting List(倒排列表):倒排列表记录了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。(PS:实际的倒排列表中并不只是存了文档ID这么简单,还有一些其它的信息,比如:词频(Term出现的次数)、偏移量(offset)等,可以想象成是Python中的元组,或者Java中的对象) (PS:如果类比现代汉语词典的话,那么Term就相当于词语,Term Dictionary相当于汉语词典本身,Term Index相当于词典的目录索引) 我们知道,每个文档都有一个ID,如果插入的时候没有指定的话,Elasticsearch会自动生成一个,因此ID字段就不多说了 上面的例子,Elasticsearch建立的索引大致如下: name字段: age字段: gender字段: address字段: Elasticsearch分别为每个字段都建立了一个倒排索引。比如,在上面“张三”、“北京市”、22 这些都是Term,而[1,3]就是Posting List。Posting list就是一个数组,存储了所有符合某个Term的文档ID。 只要知道文档ID,就能快速找到文档。可是,要怎样通过我们给定的关键词快速找到这个Term呢? 当然是建索引了,为Terms建立索引,最好的就是B-Tree索引(MySQL就是B树索引最好的例子)。 我们查找Term的过程跟在MyISAM中记录ID的过程大致是一样的 MyISAM中,索引和数据是分开,通过索引可以找到记录的地址,进而可以找到这条记录 在倒排索引中,通过Term索引可以找到Term在Term Dictionary中的位置,进而找到Posting List,有了倒排列表就可以根据ID找到文档了 (PS:可以这样理解,类比MyISAM的话,Term Index相当于索引文件,Term Dictionary相当于数据文件) (PS:其实,前面我们分了三步,我们可以把Term Index和Term Dictionary看成一步,就是找Term。因此,可以这样理解倒排索引:通过单词找到对应的倒排列表,根据倒排列表中的倒排项进而可以找到文档记录) 为了更进一步理解,用两张图来具现化这一过程: (至于里面涉及的更加高深的数据压缩技巧,以及多个field联合查询利用跳表的数据结构快速做运算来查询,这些大家有兴趣可以自己去了解)

问问小秘 2020-04-29 15:40:48 0 浏览量 回答数 0

回答

PHP面试干货 1、进程和线程 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 2、apache默认使用进程管理还是线程管理?如何判断并设置最大连接数? 一个进程可以开多个线程 默认是进程管理 默认有一个主进程 Linux: ps -aux | grep httpd | more 一个子进程代表一个用户的连接 Conf/extra/httpd-mpm.conf 多路功能模块 http -l 查询当前apache处于什么模式下 3、单例模式 单例模式需求:只能实例化产生一个对象 如何实现: 私有化构造函数 禁止克隆对象 提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一对象 需要一个保存类的静态属性 class demo { private static $MyObject; //保存对象的静态属性 private function __construct(){ //私有化构造函数 } private function __clone(){ //禁止克隆 } public static function getInstance(){ if(! (self::$MyObject instanceof self)){ self::$MyObject = new self; } return self::$MyObject; } } 4、安装完Apache后,在http.conf中配置加载PHP文件以Apache模块的方式安装PHP,在文件http.conf中首先要用语句LoadModule php5_module "e:/php/php5apache2.dll"动态装载PHP模块,然后再用语句AddType application/x-httpd-php .php 使得Apache把所有扩展名为PHP的文件都作为PHP脚本处理 5、debug_backtrace()函数能返回脚本里的任意行中调用的函数的名称。该函数同时还经常被用在调试中,用来判断错误是如何发生的 function one($str1, $str2) { two("Glenn", "Quagmire"); } function two($str1, $str2) { three("Cleveland", "Brown"); } function three($str1, $str2) { print_r(debug_backtrace()); } one("Peter", "Griffin"); Array ( [0] => Array ( [file] => D:\www\test\result.php [line] => 9 [function] => three [args] => Array ( [0] => Cleveland [1] => Brown ) ) [1] => Array ( [file] => D:\www\test\result.php [line] => 5 [function] => two [args] => Array ( [0] => Glenn [1] => Quagmire ) ) [2] => Array ( [file] => D:\www\test\result.php [line] => 16 [function] => one [args] => Array ( [0] => Peter [1] => Griffin ) ) ) 6、输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 — 192.168.1.150之间 echo $ip=getenv('REMOTE_ADDR'); $ip=str_replace('.','',$ip); if($ip<1921681150 && $ip>1921681100) { echo 'ip在192.168.1.100—–192.168.1.150之间'; } else { echo 'ip不在192.168.1.100—–192.168.1.150之间'; } 7、请将2维数组按照name的长度进行重新排序,按照顺序将id赋值 $tarray = array( array('id' => 0, 'name' => '123'), array('id' => 0, 'name' => '1234'), array('id' => 0, 'name' => '1235'), array('id' => 0, 'name' => '12356'), array('id' => 0, 'name' => '123abc') ); foreach($tarray as $key=>$val) { $c[]=$val['name']; } function aa($a,$b) { if(strlen($a)==strlen($b)) return 0; return strlen($a)>strlen($b)?-1:1; } usort($c,'aa'); $len=count($c); for($i=0;$i<$len;$i++) { $t[$i]['id']=$i+1; $t[$i]['name']=$c[$i]; } print_r($t); 8、表单数据提交方式POST和GET的区别,URL地址传递的数据最大长度是多少? POST方式提交数据用户不可见,是数据更安全,最大长度不受限制,而GET方式传值在URL地址可以看到,相对不安全,对大长度是2048字节。 9、SESSION和COOKIE的作用和区别,SESSION信息的存储方式,如何进行遍历 SESSION和COOKIE都能够使值在页面之间进行传递,SESSION存储在服务器端,数据更安全,COOKIE保存在客户端,用户使用手段可以进行修改,SESSION依赖于COOKIE进行传递的。Session遍历使用$_SESSION[]取值,cookie遍历使用$_COOKIE[]取值。 10、什么是数据库索引,主键索引,唯一索引的区别,索引的缺点是什么 索引用来快速地寻找那些具有特定值的记录。 主键索引和唯一索引的区别:主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,每个表只能有一个主键。唯一索引索引列的所有值都只能出现一次,即必须唯一。 索引的缺点: 1、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 2、索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,需要的空间就会更大。 3、当对表中的数据进行增加、删除、修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 11、数据库设计时,常遇到的性能瓶颈有哪些,常有的解决方案 瓶颈主要有: 1、磁盘搜索 优化方法是:将数据分布在多个磁盘上 2、磁盘读/写 优化方法是:从多个磁盘并行读写。 3、CPU周期 优化方法:扩充内存 4、内存带宽 12、include和require区别 include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。 require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 13、文件上传时设计到点 和文件上传有关的php.ini配置选项(File Uploads): file_uploads=On/Off:文件是否允许上传 upload_max_filesize上传文件时,单个文件的最大大小 post_max_size:提交表单时,整个post表单的最大大小 max_file_uploads =20上传文件的个数 内存占用,脚本最大执行时间也间接影响到文件的上传 14、header常见状态 //200 正常状态 header('HTTP/1.1 200 OK'); // 301 永久重定向,记得在后面要加重定向地址 Location:$url header('HTTP/1.1 301 Moved Permanently'); // 重定向,其实就是302 暂时重定向 header('Location: http://www.maiyoule.com/'); // 设置页面304 没有修改 header('HTTP/1.1 304 Not Modified'); // 显示登录框, header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic realm="登录信息"'); echo '显示的信息!'; // 403 禁止访问 header('HTTP/1.1 403 Forbidden'); // 404 错误 header('HTTP/1.1 404 Not Found'); // 500 服务器错误 header('HTTP/1.1 500 Internal Server Error'); // 3秒后重定向指定地址(也就是刷新到新页面与 <meta http-equiv="refresh" content="10;http://www.maiyoule.com/ /> 相同) header('Refresh: 3; url=http://www.maiyoule.com/'); echo '10后跳转到http://www.maiyoule.com'; // 重写 X-Powered-By 值 header('X-Powered-By: PHP/5.3.0'); header('X-Powered-By: Brain/0.6b'); //设置上下文语言 header('Content-language: en'); // 设置页面最后修改时间(多用于防缓存) $time = time() - 60; //建议使用filetime函数来设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); // 设置内容长度 header('Content-Length: 39344'); // 设置头文件类型,可以用于流文件或者文件下载 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="example.zip"'); header('Content-Transfer-Encoding: binary'); readfile('example.zip');//读取文件到客户端 //禁用页面缓存 header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); //设置页面头信息 header('Content-Type: text/html; charset=iso-8859-1'); header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/plain'); header('Content-Type: image/jpeg'); header('Content-Type: application/zip'); header('Content-Type: application/pdf'); header('Content-Type: audio/mpeg'); header('Content-Type: application/x-shockwave-flash'); //.... 至于Content-Type 的值 可以去查查 w3c 的文档库,那里很丰富 15、ORM和ActiveRecord ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却是关系型的。为了可以靠贴近面向对象开发,我们想要像操作对象一样操作数据库。还可以隔离底层数据库层,我们不需要关心我们使用的是mysql还是其他的关系型数据库 ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。 ActiveRecord的主要思想是: 1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field; 2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; 3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑; ActiveRecord比较适用于: 1. 业务逻辑比较简单,当你的类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的,即你的业务逻辑大多数是对单表操作; 2. 当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script),把跨表事务提升到事务脚本中; 3. ActiveRecord最大优点是简单, 直观。 一个类就包括了数据访问和业务逻辑. 如果配合代码生成器使用就更方便了; 这些优点使ActiveRecord特别适合WEB快速开发。 16、斐波那契方法,也就是1 1 2 3 5 8 ……,这里给出两种方法,大家可以对比下,看看哪种快,以及为什么 function fibonacci($n){ if($n == 0){ return 0; } if($n == 1){ return 1; } return fibonacci($n-1)+fibonacci($n-2); } function fibonacci($n){ for($i=0; $i<$n; $i++){ $r[] = $i<2 ? 1 : $r[$i-1]+$r[$i-2]; } return $r[--$i]; } 17、约瑟夫环,也就是常见的数猴子,n只猴子围成一圈,每只猴子下面标了编号,从1开始数起,数到m那么第m只猴子便退出,依次类推,每数到m,那么那个位置的猴子退出,那么最后剩下的猴子下的编号是啥。 function yuesefu($n,$m) { $r=0; for($i=2; $i<=$n; $i++) { $r=($r+$m)%$i; } return $r+1; } 18、冒泡排序,大致是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第二位时结束 function bubbleSort($arr){ for($i=0, $len=count($arr); $i<$len; $i++){ for($j=0; $j<$len; $j++){ if($arr[$i]<$arr[$j]){ $tmp = $arr[$j]; $arr[$j] = $arr[$i]; $arr[$i] = $tmp; } } } return $arr; } 19、快速排序,也就是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 function quickSort($arr){ $len = count($arr); if($len <=1){ return $arr; } $key = $arr[0]; $leftArr = $rightArr= array(); for($i=1; $i<$len; $i++){ if($arr[$i] <= $key){ $leftArr[] = $arr[$i]; } else{ $rightArr[] = $arr[$i]; } } $leftArr = quickSort($leftArr); $rightArr = quickSort($rightArr); return array_merge($leftArr, array($key), $rightArr); } 20、(递归的)列出目录下所有文件及目录,这里也有两种方法 function listDir($path){ $res = dir($path); while($file = $res->read()){ if($file == '.' || $file == '..'){ continue; } if(is_dir($path . '/' .$file)){ echo $path . '/' .$file . "\r\n"; listDir($path . '/' .$file); } else{ echo $path . '/' .$file . "\r\n"; } } $res->close(); } function listDir($path){ if(is_dir($path)){ if(FALSE !== ($res = opendir($path))){ while(FALSE !== ($file = readdir($res))){ if($file == '.' || $file == '..'){ continue; } $subPath = $path . '/' . $file; if(is_dir($subPath)){ echo $subPath . "\r\n"; listDir($subPath); } else{ echo $subPath . "\r\n"; } } } } } 21、找出相对的目录,比如/a/b/c/d/e.php相对于/a/b/13/34/c.php是/c/d/ function ralativePath($a, $b){ $a = explode('/', dirname($a)); $b = explode('/', dirname($b)); $c = '/'; foreach ($a as $k=> $v){ if($v != $b[$k]){ $c .= $v . '/'; } } echo $c; } 22、快速找出url中php后缀 function get_ext($url){ $data = parse_url($url); return pathinfo($data['path'], PATHINFO_EXTENSION); } 23、正则题,使用正则抓取网页,以网页meta为utf8为准,若是抓取的网页编码为big5之类的,需要转化为utf8再收录 function preg_meta($meta){ $replacement = "\\1utf8\\6\\7"; $pattern = '#(<meta\s+http-equiv=(\'|"|)Content-Type(\'|"|)\s+content=(\'|"|)text/html; charset=)(\w+)(\'|"|)(>)#i'; return preg_replace($pattern, $replacement, $meta); } echo preg_meta("<meta http-equiv=Content-Type content='text/html; charset=big5'><META http-equiv=\"Content-Type\" content='text/html; charset=big5'>"); 24、不用php的反转函数倒序输出字符串,如abc,反序输出cba function revstring($str){ for($i=strlen($str)-1; $i>=0; $i--){ echo $str{$i}; } } revstring('abc'); 25、常见端口 TCP 21端口:FTP 文件传输服务 SSH 22端口:SSH连接linux服务器,通过SSH连接可以远程管理Linux等设备 TCP 23端口:TELNET 终端仿真服务 TCP 25端口:SMTP 简单邮件传输服务 UDP 53端口:DNS 域名解析服务 TCP 80端口:HTTP 超文本传输服务 TCP 110端口:POP3 “邮局协议版本3”使用的端口 TCP 443端口:HTTPS 加密的超文本传输服务 TCP 1521端口:Oracle数据库服务 TCP 1863端口:MSN Messenger的文件传输功能所使用的端口 TCP 3389端口:Microsoft RDP 微软远程桌面使用的端口 TCP 5631端口:Symantec pcAnywhere 远程控制数据传输时使用的端口 UDP 5632端口:Symantec pcAnywhere 主控端扫描被控端时使用的端口 TCP 5000端口:MS SQL Server使用的端口 UDP 8000端口:腾讯QQ 26、linux常用的命令 top linux进程实时监控 ps 在Linux中是查看进程的命令。ps查看正处于Running的进程 mv 为文件或目录改名或将文件由一个目录移入另一个目录中。 find 查找文件 df 可显示所有文件系统对i节点和磁盘块的使用情况。 cat 打印文件类容 chmod 变更文件或目录的权限 chgrp 文件或目录的权限的掌控以拥有者及所诉群组来管理。可以使用chgrp指令取变更文件与目录所属群组 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。 wc 为统计指定文件中的字节数、字数、行数,并将统计结果显示输出 27、对于大流量的网站,您采用什么样的方法来解决访问量问题 首先,确认服务器硬件是否足够支持当前的流量 其次,优化数据库访问。 第三,禁止外部的盗链。 第四,控制大文件的下载。 第五,使用不同主机分流主要流量 第六,使用流量分析统计软件 28、$_SERVER常用的字段 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名 $_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称 $_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT” $_SERVER['QUERY_STRING'] #查询(query)的字符串 $_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容 $_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址 $_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址 $_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名 $_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名 $_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用 $_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html” 29、安装php扩展 进入扩展的目录 phpize命令得到configure文件 ./configure --with-php-config=/usr/local/php/bin/php-config make & make install 在php.ini中加入扩展名称.so 重启web服务器(nginx/apache) 30、php-fpm与nginx PHP-FPM也是一个第三方的FastCGI进程管理器,它是作为PHP的一个补丁来开发的,在安装的时候也需要和PHP源码一起编译,也就是说PHP-FPM被编译到PHP内核中,因此在处理性能方面更加优秀;同时它在处理高并发方面也比spawn-fcgi引擎好很多,因此,推荐Nginx+PHP/PHP-FPM这个组合对PHP进行解析。 FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求 #fastcgi FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同时,FastCGI也被许多脚本语言所支持,其中就有PHP。 FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发访问时,几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少被使用了。 FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 Nginx+FastCGI运行原理 Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个运作过程。 31、ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML)

小川游鱼 2019-12-02 01:41:29 0 浏览量 回答数 0

回答

PHP面试干货 1、进程和线程 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 2、apache默认使用进程管理还是线程管理?如何判断并设置最大连接数? 一个进程可以开多个线程 默认是进程管理 默认有一个主进程 Linux: ps -aux | grep httpd | more 一个子进程代表一个用户的连接 Conf/extra/httpd-mpm.conf 多路功能模块 http -l 查询当前apache处于什么模式下 3、单例模式 单例模式需求:只能实例化产生一个对象 如何实现: 私有化构造函数 禁止克隆对象 提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一对象 需要一个保存类的静态属性 class demo { private static $MyObject; //保存对象的静态属性 private function __construct(){ //私有化构造函数 } private function __clone(){ //禁止克隆 } public static function getInstance(){ if(! (self::$MyObject instanceof self)){ self::$MyObject = new self; } return self::$MyObject; } } 4、安装完Apache后,在http.conf中配置加载PHP文件以Apache模块的方式安装PHP,在文件http.conf中首先要用语句LoadModule php5_module "e:/php/php5apache2.dll"动态装载PHP模块,然后再用语句AddType application/x-httpd-php .php 使得Apache把所有扩展名为PHP的文件都作为PHP脚本处理 5、debug_backtrace()函数能返回脚本里的任意行中调用的函数的名称。该函数同时还经常被用在调试中,用来判断错误是如何发生的 function one($str1, $str2) { two("Glenn", "Quagmire"); } function two($str1, $str2) { three("Cleveland", "Brown"); } function three($str1, $str2) { print_r(debug_backtrace()); } one("Peter", "Griffin"); Array ( [0] => Array ( [file] => D:\www\test\result.php [line] => 9 [function] => three [args] => Array ( [0] => Cleveland [1] => Brown ) ) [1] => Array ( [file] => D:\www\test\result.php [line] => 5 [function] => two [args] => Array ( [0] => Glenn [1] => Quagmire ) ) [2] => Array ( [file] => D:\www\test\result.php [line] => 16 [function] => one [args] => Array ( [0] => Peter [1] => Griffin ) ) ) 6、输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 — 192.168.1.150之间 echo $ip=getenv('REMOTE_ADDR'); $ip=str_replace('.','',$ip); if($ip<1921681150 && $ip>1921681100) { echo 'ip在192.168.1.100—–192.168.1.150之间'; } else { echo 'ip不在192.168.1.100—–192.168.1.150之间'; } 7、请将2维数组按照name的长度进行重新排序,按照顺序将id赋值 $tarray = array( array('id' => 0, 'name' => '123'), array('id' => 0, 'name' => '1234'), array('id' => 0, 'name' => '1235'), array('id' => 0, 'name' => '12356'), array('id' => 0, 'name' => '123abc') ); foreach($tarray as $key=>$val) { $c[]=$val['name']; } function aa($a,$b) { if(strlen($a)==strlen($b)) return 0; return strlen($a)>strlen($b)?-1:1; } usort($c,'aa'); $len=count($c); for($i=0;$i<$len;$i++) { $t[$i]['id']=$i+1; $t[$i]['name']=$c[$i]; } print_r($t); 8、表单数据提交方式POST和GET的区别,URL地址传递的数据最大长度是多少? POST方式提交数据用户不可见,是数据更安全,最大长度不受限制,而GET方式传值在URL地址可以看到,相对不安全,对大长度是2048字节。 9、SESSION和COOKIE的作用和区别,SESSION信息的存储方式,如何进行遍历 SESSION和COOKIE都能够使值在页面之间进行传递,SESSION存储在服务器端,数据更安全,COOKIE保存在客户端,用户使用手段可以进行修改,SESSION依赖于COOKIE进行传递的。Session遍历使用$_SESSION[]取值,cookie遍历使用$_COOKIE[]取值。 10、什么是数据库索引,主键索引,唯一索引的区别,索引的缺点是什么 索引用来快速地寻找那些具有特定值的记录。 主键索引和唯一索引的区别:主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,每个表只能有一个主键。唯一索引索引列的所有值都只能出现一次,即必须唯一。 索引的缺点: 1、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 2、索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,需要的空间就会更大。 3、当对表中的数据进行增加、删除、修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 11、数据库设计时,常遇到的性能瓶颈有哪些,常有的解决方案 瓶颈主要有: 1、磁盘搜索 优化方法是:将数据分布在多个磁盘上 2、磁盘读/写 优化方法是:从多个磁盘并行读写。 3、CPU周期 优化方法:扩充内存 4、内存带宽 12、include和require区别 include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。 require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 13、文件上传时设计到点 和文件上传有关的php.ini配置选项(File Uploads): file_uploads=On/Off:文件是否允许上传 upload_max_filesize上传文件时,单个文件的最大大小 post_max_size:提交表单时,整个post表单的最大大小 max_file_uploads =20上传文件的个数 内存占用,脚本最大执行时间也间接影响到文件的上传 14、header常见状态 //200 正常状态 header('HTTP/1.1 200 OK'); // 301 永久重定向,记得在后面要加重定向地址 Location:$url header('HTTP/1.1 301 Moved Permanently'); // 重定向,其实就是302 暂时重定向 header('Location: http://www.maiyoule.com/'); // 设置页面304 没有修改 header('HTTP/1.1 304 Not Modified'); // 显示登录框, header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic realm="登录信息"'); echo '显示的信息!'; // 403 禁止访问 header('HTTP/1.1 403 Forbidden'); // 404 错误 header('HTTP/1.1 404 Not Found'); // 500 服务器错误 header('HTTP/1.1 500 Internal Server Error'); // 3秒后重定向指定地址(也就是刷新到新页面与 <meta http-equiv="refresh" content="10;http://www.maiyoule.com/ /> 相同) header('Refresh: 3; url=http://www.maiyoule.com/'); echo '10后跳转到http://www.maiyoule.com'; // 重写 X-Powered-By 值 header('X-Powered-By: PHP/5.3.0'); header('X-Powered-By: Brain/0.6b'); //设置上下文语言 header('Content-language: en'); // 设置页面最后修改时间(多用于防缓存) $time = time() - 60; //建议使用filetime函数来设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); // 设置内容长度 header('Content-Length: 39344'); // 设置头文件类型,可以用于流文件或者文件下载 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="example.zip"'); header('Content-Transfer-Encoding: binary'); readfile('example.zip');//读取文件到客户端 //禁用页面缓存 header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); //设置页面头信息 header('Content-Type: text/html; charset=iso-8859-1'); header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/plain'); header('Content-Type: image/jpeg'); header('Content-Type: application/zip'); header('Content-Type: application/pdf'); header('Content-Type: audio/mpeg'); header('Content-Type: application/x-shockwave-flash'); //.... 至于Content-Type 的值 可以去查查 w3c 的文档库,那里很丰富 15、ORM和ActiveRecord ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却是关系型的。为了可以靠贴近面向对象开发,我们想要像操作对象一样操作数据库。还可以隔离底层数据库层,我们不需要关心我们使用的是mysql还是其他的关系型数据库 ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。 ActiveRecord的主要思想是: 1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field; 2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; 3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑; ActiveRecord比较适用于: 1. 业务逻辑比较简单,当你的类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的,即你的业务逻辑大多数是对单表操作; 2. 当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script),把跨表事务提升到事务脚本中; 3. ActiveRecord最大优点是简单, 直观。 一个类就包括了数据访问和业务逻辑. 如果配合代码生成器使用就更方便了; 这些优点使ActiveRecord特别适合WEB快速开发。 16、斐波那契方法,也就是1 1 2 3 5 8 ……,这里给出两种方法,大家可以对比下,看看哪种快,以及为什么 function fibonacci($n){ if($n == 0){ return 0; } if($n == 1){ return 1; } return fibonacci($n-1)+fibonacci($n-2); } function fibonacci($n){ for($i=0; $i<$n; $i++){ $r[] = $i<2 ? 1 : $r[$i-1]+$r[$i-2]; } return $r[--$i]; } 17、约瑟夫环,也就是常见的数猴子,n只猴子围成一圈,每只猴子下面标了编号,从1开始数起,数到m那么第m只猴子便退出,依次类推,每数到m,那么那个位置的猴子退出,那么最后剩下的猴子下的编号是啥。 function yuesefu($n,$m) { $r=0; for($i=2; $i<=$n; $i++) { $r=($r+$m)%$i; } return $r+1; } 18、冒泡排序,大致是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第二位时结束 function bubbleSort($arr){ for($i=0, $len=count($arr); $i<$len; $i++){ for($j=0; $j<$len; $j++){ if($arr[$i]<$arr[$j]){ $tmp = $arr[$j]; $arr[$j] = $arr[$i]; $arr[$i] = $tmp; } } } return $arr; } 19、快速排序,也就是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 function quickSort($arr){ $len = count($arr); if($len <=1){ return $arr; } $key = $arr[0]; $leftArr = $rightArr= array(); for($i=1; $i<$len; $i++){ if($arr[$i] <= $key){ $leftArr[] = $arr[$i]; } else{ $rightArr[] = $arr[$i]; } } $leftArr = quickSort($leftArr); $rightArr = quickSort($rightArr); return array_merge($leftArr, array($key), $rightArr); } 20、(递归的)列出目录下所有文件及目录,这里也有两种方法 function listDir($path){ $res = dir($path); while($file = $res->read()){ if($file == '.' || $file == '..'){ continue; } if(is_dir($path . '/' .$file)){ echo $path . '/' .$file . "\r\n"; listDir($path . '/' .$file); } else{ echo $path . '/' .$file . "\r\n"; } } $res->close(); } function listDir($path){ if(is_dir($path)){ if(FALSE !== ($res = opendir($path))){ while(FALSE !== ($file = readdir($res))){ if($file == '.' || $file == '..'){ continue; } $subPath = $path . '/' . $file; if(is_dir($subPath)){ echo $subPath . "\r\n"; listDir($subPath); } else{ echo $subPath . "\r\n"; } } } } } 21、找出相对的目录,比如/a/b/c/d/e.php相对于/a/b/13/34/c.php是/c/d/ function ralativePath($a, $b){ $a = explode('/', dirname($a)); $b = explode('/', dirname($b)); $c = '/'; foreach ($a as $k=> $v){ if($v != $b[$k]){ $c .= $v . '/'; } } echo $c; } 22、快速找出url中php后缀 function get_ext($url){ $data = parse_url($url); return pathinfo($data['path'], PATHINFO_EXTENSION); } 23、正则题,使用正则抓取网页,以网页meta为utf8为准,若是抓取的网页编码为big5之类的,需要转化为utf8再收录 function preg_meta($meta){ $replacement = "\\1utf8\\6\\7"; $pattern = '#(<meta\s+http-equiv=(\'|"|)Content-Type(\'|"|)\s+content=(\'|"|)text/html; charset=)(\w+)(\'|"|)(>)#i'; return preg_replace($pattern, $replacement, $meta); } echo preg_meta("<meta http-equiv=Content-Type content='text/html; charset=big5'><META http-equiv=\"Content-Type\" content='text/html; charset=big5'>"); 24、不用php的反转函数倒序输出字符串,如abc,反序输出cba function revstring($str){ for($i=strlen($str)-1; $i>=0; $i--){ echo $str{$i}; } } revstring('abc'); 25、常见端口 TCP 21端口:FTP 文件传输服务 SSH 22端口:SSH连接linux服务器,通过SSH连接可以远程管理Linux等设备 TCP 23端口:TELNET 终端仿真服务 TCP 25端口:SMTP 简单邮件传输服务 UDP 53端口:DNS 域名解析服务 TCP 80端口:HTTP 超文本传输服务 TCP 110端口:POP3 “邮局协议版本3”使用的端口 TCP 443端口:HTTPS 加密的超文本传输服务 TCP 1521端口:Oracle数据库服务 TCP 1863端口:MSN Messenger的文件传输功能所使用的端口 TCP 3389端口:Microsoft RDP 微软远程桌面使用的端口 TCP 5631端口:Symantec pcAnywhere 远程控制数据传输时使用的端口 UDP 5632端口:Symantec pcAnywhere 主控端扫描被控端时使用的端口 TCP 5000端口:MS SQL Server使用的端口 UDP 8000端口:腾讯QQ 26、linux常用的命令 top linux进程实时监控 ps 在Linux中是查看进程的命令。ps查看正处于Running的进程 mv 为文件或目录改名或将文件由一个目录移入另一个目录中。 find 查找文件 df 可显示所有文件系统对i节点和磁盘块的使用情况。 cat 打印文件类容 chmod 变更文件或目录的权限 chgrp 文件或目录的权限的掌控以拥有者及所诉群组来管理。可以使用chgrp指令取变更文件与目录所属群组 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。 wc 为统计指定文件中的字节数、字数、行数,并将统计结果显示输出 27、对于大流量的网站,您采用什么样的方法来解决访问量问题 首先,确认服务器硬件是否足够支持当前的流量 其次,优化数据库访问。 第三,禁止外部的盗链。 第四,控制大文件的下载。 第五,使用不同主机分流主要流量 第六,使用流量分析统计软件 28、$_SERVER常用的字段 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名 $_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称 $_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT” $_SERVER['QUERY_STRING'] #查询(query)的字符串 $_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容 $_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址 $_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址 $_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名 $_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名 $_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用 $_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html” 29、安装php扩展 进入扩展的目录 phpize命令得到configure文件 ./configure --with-php-config=/usr/local/php/bin/php-config make & make install 在php.ini中加入扩展名称.so 重启web服务器(nginx/apache) 30、php-fpm与nginx PHP-FPM也是一个第三方的FastCGI进程管理器,它是作为PHP的一个补丁来开发的,在安装的时候也需要和PHP源码一起编译,也就是说PHP-FPM被编译到PHP内核中,因此在处理性能方面更加优秀;同时它在处理高并发方面也比spawn-fcgi引擎好很多,因此,推荐Nginx+PHP/PHP-FPM这个组合对PHP进行解析。 FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求 #fastcgi FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同时,FastCGI也被许多脚本语言所支持,其中就有PHP。 FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发访问时,几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少被使用了。 FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 Nginx+FastCGI运行原理 Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个运作过程。 31、ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML)

小川游鱼 2019-12-02 01:41:29 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站